summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPawel Chojnacki <pawel@chojnacki.ws>2018-01-29 19:49:23 +0100
committerPawel Chojnacki <pawel@chojnacki.ws>2018-01-29 19:49:23 +0100
commitf87afeff18337fa6205ee65710033b33a7c5aa66 (patch)
tree8c583c5c97d882767f9fc126a7e9d6fa76160ec1
parent4b1d42bbc583b0884498bd129653587acc69ab0f (diff)
parent44edd1111f9ba6dd0dacffad23b54c0c85a723c4 (diff)
downloadgitlab-ce-f87afeff18337fa6205ee65710033b33a7c5aa66.tar.gz
Merge remote-tracking branch 'upstream/master' into pawel/connect_to_prometheus_through_proxy-30480
-rw-r--r--.gitlab-ci.yml157
-rw-r--r--.rubocop.yml1
-rw-r--r--.rubocop_todo.yml139
-rw-r--r--CHANGELOG.md3464
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile11
-rw-r--r--Gemfile.lock22
-rw-r--r--PROCESS.md30
-rw-r--r--VERSION2
-rw-r--r--app/assets/images/icons.json2
-rw-r--r--app/assets/images/icons.svg2
-rw-r--r--app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg1
-rw-r--r--app/assets/images/illustrations/multi-editor_no_changes_empty.svg1
-rw-r--r--app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg1
-rw-r--r--app/assets/images/illustrations/pending_job_empty.svg1
-rw-r--r--app/assets/javascripts/api.js3
-rw-r--r--app/assets/javascripts/awards_handler.js12
-rw-r--r--app/assets/javascripts/behaviors/copy_as_gfm.js7
-rw-r--r--app/assets/javascripts/behaviors/secret_values.js21
-rw-r--r--app/assets/javascripts/boards/utils/query_data.js2
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js12
-rw-r--r--app/assets/javascripts/clusters/clusters_index.js68
-rw-r--r--app/assets/javascripts/compare.js47
-rw-r--r--app/assets/javascripts/compare_autocomplete.js16
-rw-r--r--app/assets/javascripts/create_item_dropdown.js12
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js147
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue1
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue33
-rw-r--r--app/assets/javascripts/dispatcher.js345
-rw-r--r--app/assets/javascripts/dropzone_input.js33
-rw-r--r--app/assets/javascripts/due_date_select.js48
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue6
-rw-r--r--app/assets/javascripts/filterable_list.js31
-rw-r--r--app/assets/javascripts/flash.js4
-rw-r--r--app/assets/javascripts/fly_out_nav.js4
-rw-r--r--app/assets/javascripts/groups/groups_filterable_list.js17
-rw-r--r--app/assets/javascripts/groups/index.js4
-rw-r--r--app/assets/javascripts/groups/service/groups_service.js4
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue9
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue5
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue11
-rw-r--r--app/assets/javascripts/ide/components/repo_tabs.vue2
-rw-r--r--app/assets/javascripts/ide/ide_router.js4
-rw-r--r--app/assets/javascripts/ide/lib/editor.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js35
-rw-r--r--app/assets/javascripts/ide/stores/actions/branch.js2
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js18
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js2
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js4
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js2
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue18
-rw-r--r--app/assets/javascripts/labels_select.js2
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js8
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js2
-rw-r--r--app/assets/javascripts/merge_request.js9
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js4
-rw-r--r--app/assets/javascripts/notebook/cells/markdown.vue8
-rw-r--r--app/assets/javascripts/notebook/cells/output/html.vue15
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue4
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue47
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/index.js29
-rw-r--r--app/assets/javascripts/pages/constants.js6
-rw-r--r--app/assets/javascripts/pages/dashboard/groups/index/index.js5
-rw-r--r--app/assets/javascripts/pages/explore/groups/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/activity/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index/index.js11
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js8
-rw-r--r--app/assets/javascripts/pages/groups/labels/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/labels/index/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/labels/new/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js8
-rw-r--r--app/assets/javascripts/pages/groups/milestones/show/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/new/index.js9
-rw-r--r--app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js11
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js22
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue110
-rw-r--r--app/assets/javascripts/pages/milestones/shared/event_hub.js3
-rw-r--r--app/assets/javascripts/pages/milestones/shared/index.js88
-rw-r--r--app/assets/javascripts/pages/milestones/shared/init_milestones_show.js9
-rw-r--r--app/assets/javascripts/pages/omniauth_callbacks/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/constants.js6
-rw-r--r--app/assets/javascripts/pages/projects/edit/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/environments/metrics/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/init_form.js7
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js12
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js18
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js13
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js19
-rw-r--r--app/assets/javascripts/pages/projects/milestones/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/milestones/show/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/new/index.js9
-rw-r--r--app/assets/javascripts/pages/projects/project.js (renamed from app/assets/javascripts/project.js)4
-rw-r--r--app/assets/javascripts/pages/projects/releases/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js22
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue (renamed from app/assets/javascripts/projects/permissions/components/project_feature_setting.vue)2
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue (renamed from app/assets/javascripts/projects/permissions/components/project_setting_row.vue)0
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue (renamed from app/assets/javascripts/projects/permissions/components/settings_panel.vue)4
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/constants.js (renamed from app/assets/javascripts/projects/permissions/constants.js)0
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/external.js (renamed from app/assets/javascripts/projects/permissions/external.js)0
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/index.js (renamed from app/assets/javascripts/projects/permissions/index.js)0
-rw-r--r--app/assets/javascripts/pages/projects/shared/project_avatar.js (renamed from app/assets/javascripts/project_avatar.js)0
-rw-r--r--app/assets/javascripts/pages/projects/shared/project_new.js (renamed from app/assets/javascripts/project_new.js)2
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js27
-rw-r--r--app/assets/javascripts/pages/projects/snippets/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/snippets/new/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/snippets/show/index.js11
-rw-r--r--app/assets/javascripts/pages/projects/wikis/index.js11
-rw-r--r--app/assets/javascripts/pages/projects/wikis/wikis.js (renamed from app/assets/javascripts/wikis.js)4
-rw-r--r--app/assets/javascripts/pages/search/init_filtered_search.js7
-rw-r--r--app/assets/javascripts/pages/search/show/search.js2
-rw-r--r--app/assets/javascripts/pages/sessions/index.js5
-rw-r--r--app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js6
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue4
-rw-r--r--app/assets/javascripts/projects/project_new.js2
-rw-r--r--app/assets/javascripts/render_math.js2
-rw-r--r--app/assets/javascripts/render_mermaid.js6
-rw-r--r--app/assets/javascripts/shared/sessions/u2f.js16
-rw-r--r--app/assets/javascripts/shortcuts.js8
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js4
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.js2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.js2
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue2
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js2
-rw-r--r--app/assets/javascripts/toggle_buttons.js61
-rw-r--r--app/assets/javascripts/users_select.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js36
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue57
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.js26
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue31
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.js47
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue52
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.js18
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue23
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js35
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue48
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js47
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue61
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js78
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue105
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js124
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue147
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js139
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue192
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js29
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue35
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js18
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/index.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_icon.vue2
-rw-r--r--app/assets/stylesheets/framework/awards.scss11
-rw-r--r--app/assets/stylesheets/framework/buttons.scss33
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss7
-rw-r--r--app/assets/stylesheets/framework/files.scss4
-rw-r--r--app/assets/stylesheets/framework/header.scss2
-rw-r--r--app/assets/stylesheets/framework/icons.scss6
-rw-r--r--app/assets/stylesheets/framework/images.scss2
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss8
-rw-r--r--app/assets/stylesheets/framework/layout.scss8
-rw-r--r--app/assets/stylesheets/framework/mixins.scss2
-rw-r--r--app/assets/stylesheets/framework/modal.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/pages/commits.scss8
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss41
-rw-r--r--app/assets/stylesheets/pages/note_form.scss8
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss2
-rw-r--r--app/assets/stylesheets/pages/repo.scss74
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb4
-rw-r--r--app/controllers/admin/hooks_controller.rb6
-rw-r--r--app/controllers/admin/jobs_controller.rb2
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/concerns/group_tree.rb6
-rw-r--r--app/controllers/concerns/issuable_actions.rb2
-rw-r--r--app/controllers/concerns/with_performance_bar.rb15
-rw-r--r--app/controllers/confirmations_controller.rb2
-rw-r--r--app/controllers/groups/milestones_controller.rb6
-rw-r--r--app/controllers/health_controller.rb3
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb9
-rw-r--r--app/controllers/projects/commits_controller.rb46
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb9
-rw-r--r--app/controllers/projects/hooks_controller.rb10
-rw-r--r--app/controllers/projects/milestones_controller.rb16
-rw-r--r--app/finders/group_descendants_finder.rb41
-rw-r--r--app/finders/issues_finder.rb11
-rw-r--r--app/finders/labels_finder.rb2
-rw-r--r--app/finders/milestones_finder.rb8
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/helpers/application_settings_helper.rb1
-rw-r--r--app/helpers/auto_devops_helper.rb6
-rw-r--r--app/helpers/emails_helper.rb16
-rw-r--r--app/helpers/issuables_helper.rb8
-rw-r--r--app/helpers/issues_helper.rb2
-rw-r--r--app/helpers/search_helper.rb4
-rw-r--r--app/helpers/todos_helper.rb10
-rw-r--r--app/helpers/webpack_helper.rb14
-rw-r--r--app/mailers/emails/issues.rb33
-rw-r--r--app/mailers/emails/merge_requests.rb37
-rw-r--r--app/mailers/notify.rb2
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/triggerable_hooks.rb40
-rw-r--r--app/models/deploy_key.rb20
-rw-r--r--app/models/deploy_keys_project.rb10
-rw-r--r--app/models/deployment.rb9
-rw-r--r--app/models/global_milestone.rb8
-rw-r--r--app/models/hooks/project_hook.rb26
-rw-r--r--app/models/hooks/system_hook.rb16
-rw-r--r--app/models/hooks/web_hook.rb1
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/notification_reason.rb19
-rw-r--r--app/models/notification_recipient.rb36
-rw-r--r--app/models/project.rb12
-rw-r--r--app/models/project_services/emails_on_push_service.rb2
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/models/project_statistics.rb2
-rw-r--r--app/models/repository.rb92
-rw-r--r--app/models/route.rb12
-rw-r--r--app/models/service.rb11
-rw-r--r--app/models/user.rb4
-rw-r--r--app/presenters/projects/settings/deploy_keys_presenter.rb2
-rw-r--r--app/serializers/deploy_key_entity.rb9
-rw-r--r--app/serializers/deploy_keys_project_entity.rb4
-rw-r--r--app/services/merge_requests/create_service.rb28
-rw-r--r--app/services/merge_requests/refresh_service.rb16
-rw-r--r--app/services/notification_recipient_service.rb48
-rw-r--r--app/services/notification_service.rb26
-rw-r--r--app/services/projects/gitlab_projects_import_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/services/test_hooks/base_service.rb2
-rw-r--r--app/services/test_hooks/system_service.rb7
-rw-r--r--app/services/web_hook_service.rb2
-rw-r--r--app/views/admin/deploy_keys/index.html.haml8
-rw-r--r--app/views/admin/hooks/_form.html.haml7
-rw-r--r--app/views/admin/hooks/edit.html.haml2
-rw-r--r--app/views/admin/hooks/index.html.haml4
-rw-r--r--app/views/admin/jobs/index.html.haml7
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml2
-rw-r--r--app/views/dashboard/activity.html.haml4
-rw-r--r--app/views/dashboard/groups/_groups.html.haml2
-rw-r--r--app/views/dashboard/groups/index.html.haml3
-rw-r--r--app/views/dashboard/projects/index.html.haml3
-rw-r--r--app/views/dashboard/projects/starred.html.haml3
-rw-r--r--app/views/devise/shared/_signin_box.html.haml2
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml2
-rw-r--r--app/views/explore/groups/_groups.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml3
-rw-r--r--app/views/groups/_children.html.haml5
-rw-r--r--app/views/help/index.html.haml17
-rw-r--r--app/views/ide/index.html.haml5
-rw-r--r--app/views/layouts/_head.html.haml1
-rw-r--r--app/views/layouts/nav_only.html.haml5
-rw-r--r--app/views/layouts/notify.html.haml2
-rw-r--r--app/views/layouts/notify.text.erb2
-rw-r--r--app/views/profiles/preferences/show.html.haml17
-rw-r--r--app/views/profiles/show.html.haml4
-rw-r--r--app/views/projects/_last_push.html.haml27
-rw-r--r--app/views/projects/_new_project_fields.html.haml2
-rw-r--r--app/views/projects/_readme.html.haml8
-rw-r--r--app/views/projects/activity.html.haml3
-rw-r--r--app/views/projects/blob/show.html.haml3
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml46
-rw-r--r--app/views/projects/clusters/_cluster.html.haml5
-rw-r--r--app/views/projects/clusters/_integration_form.html.haml7
-rw-r--r--app/views/projects/commit/_change.html.haml3
-rw-r--r--app/views/projects/commit/_other_user_signature_badge.html.haml2
-rw-r--r--app/views/projects/commit/_same_user_different_email_signature_badge.html.haml2
-rw-r--r--app/views/projects/commit/_signature_badge.html.haml2
-rw-r--r--app/views/projects/commit/_unverified_signature_badge.html.haml2
-rw-r--r--app/views/projects/commit/_verified_signature_badge.html.haml2
-rw-r--r--app/views/projects/commits/_commit.atom.builder4
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml18
-rw-r--r--app/views/projects/empty.html.haml9
-rw-r--r--app/views/projects/environments/metrics.html.haml1
-rw-r--r--app/views/projects/hooks/edit.html.haml2
-rw-r--r--app/views/projects/jobs/_empty_state.html.haml5
-rw-r--r--app/views/projects/jobs/show.html.haml12
-rw-r--r--app/views/projects/merge_requests/index.html.haml3
-rw-r--r--app/views/projects/milestones/index.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml14
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/settings/integrations/_project_hook.html.haml4
-rw-r--r--app/views/projects/show.html.haml4
-rw-r--r--app/views/projects/tree/_tree_header.html.haml4
-rw-r--r--app/views/projects/tree/show.html.haml3
-rw-r--r--app/views/search/_category.html.haml9
-rw-r--r--app/views/search/_results.html.haml5
-rw-r--r--app/views/shared/_group_form.html.haml2
-rw-r--r--app/views/shared/deploy_keys/_form.html.haml19
-rw-r--r--app/views/shared/form_elements/_description.html.haml2
-rw-r--r--app/views/shared/icons/_icon_status_notfound_borderless.svg1
-rw-r--r--app/views/shared/icons/_icon_status_success_borderless.svg1
-rw-r--r--app/views/shared/issuable/_filter.html.haml2
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml6
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/form/_title.html.haml2
-rw-r--r--app/views/shared/milestones/_milestone.html.haml13
-rw-r--r--app/views/shared/web_hooks/_form.html.haml2
-rw-r--r--app/views/users/show.html.haml6
-rw-r--r--app/workers/repository_check/single_repository_worker.rb5
-rw-r--r--app/workers/repository_import_worker.rb10
-rwxr-xr-xbin/profile-url57
-rw-r--r--changelogs/archive.md3248
-rw-r--r--changelogs/unreleased/13695-order-contributors-in-api.yml5
-rw-r--r--changelogs/unreleased/15832-fix-access-level-update-for-requesters.yml5
-rw-r--r--changelogs/unreleased/15922-validate-file-status-when-commiting-multiple-files.yml5
-rw-r--r--changelogs/unreleased/15955-improve-search-query.yml5
-rw-r--r--changelogs/unreleased/16036-ignore-lost-found-folder-during-backup-on-a-volume.yml5
-rw-r--r--changelogs/unreleased/16117-improve-search-for-issues.yml5
-rw-r--r--changelogs/unreleased/16468-add-fast-blank.yml5
-rw-r--r--changelogs/unreleased/20035-pause-resume-runners.yml5
-rw-r--r--changelogs/unreleased/24035-api-create-application.yml4
-rw-r--r--changelogs/unreleased/24347-dont-post-system-note-when-branch-creation-fails.yml5
-rw-r--r--changelogs/unreleased/25317-prioritize-author-date-over-commit.yml5
-rw-r--r--changelogs/unreleased/26296-update-styling-disabled-buttons.yml5
-rw-r--r--changelogs/unreleased/28004-consider-refactoring-member-view-by-using-presenter.yml4
-rw-r--r--changelogs/unreleased/31995-project-limit-default-fix.yml5
-rw-r--r--changelogs/unreleased/32364-updating-slack-notification-not-working-by-api.yml5
-rw-r--r--changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml5
-rw-r--r--changelogs/unreleased/33028-event-tag-links.yml5
-rw-r--r--changelogs/unreleased/33609-hide-pagination.yml5
-rw-r--r--changelogs/unreleased/33926-update-issuable-icons.yml5
-rw-r--r--changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml6
-rw-r--r--changelogs/unreleased/34252-trailing-plus.yml5
-rw-r--r--changelogs/unreleased/34534-switch-to-axios.yml5
-rw-r--r--changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml5
-rw-r--r--changelogs/unreleased/36020-private-npm-modules.yml5
-rw-r--r--changelogs/unreleased/36669-default-mr-title-with-external-issues.yml5
-rw-r--r--changelogs/unreleased/36782-replace-team-user-role-with-add_role-user-in-specs.yml5
-rw-r--r--changelogs/unreleased/36958-enable-ordering-projects-subgroups-by-name.yml5
-rw-r--r--changelogs/unreleased/37843-ci-trace-ansi-colours-256-bold-have-no-css-due-wrongly-ansi2html-light-color-variant-conversion-feature.yml5
-rw-r--r--changelogs/unreleased/38019-hide-runner-token.yml5
-rw-r--r--changelogs/unreleased/38030-add-graph-value-to-hover.yml5
-rw-r--r--changelogs/unreleased/38145_ux_issues_in_system_info_page.yml5
-rw-r--r--changelogs/unreleased/38239-update-toggle-design.yml5
-rw-r--r--changelogs/unreleased/38318-search-merge-requests-with-api.yml5
-rw-r--r--changelogs/unreleased/38541-cancel-alignment.yml5
-rw-r--r--changelogs/unreleased/38596-fix-backspace-visual-token-clearing.yml5
-rw-r--r--changelogs/unreleased/38893-banzai-upload-filter-relative-urls.yml5
-rw-r--r--changelogs/unreleased/39246-fork-and-import-jobs-should-only-be-marked-as-failed-when-the-number-of-retries-was-exhausted.yml5
-rw-r--r--changelogs/unreleased/39298-list-of-avatars-2.yml5
-rw-r--r--changelogs/unreleased/39608-comment-on-image-discussions-tab-alignment.yml5
-rw-r--r--changelogs/unreleased/3968-protected-branch-is-not-set-for-default-branch-on-import.yml5
-rw-r--r--changelogs/unreleased/39917-revert-this-merge-request-text.yml5
-rw-r--r--changelogs/unreleased/39957-redirect-to-gpc-page-if-users-try-to-create-a-cluster-but-the-account-is-not-enabled.yml5
-rw-r--r--changelogs/unreleased/40028-special-characters-on-issuable-templates.yml5
-rw-r--r--changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml5
-rw-r--r--changelogs/unreleased/40031-include-assset_sync-gem.yml5
-rw-r--r--changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml5
-rw-r--r--changelogs/unreleased/40063-markdown-editor-improvements.yml5
-rw-r--r--changelogs/unreleased/40190-fix-slash-commands-dropdown-description-mis-alignement-on-firefox.yml5
-rw-r--r--changelogs/unreleased/40228-verify-integrity-of-repositories.yml5
-rw-r--r--changelogs/unreleased/40274-user-settings-breadcrumbs.yml5
-rw-r--r--changelogs/unreleased/40301-rebase.yml5
-rw-r--r--changelogs/unreleased/40418-migrate-existing-data-from-kubernetesservice-to-clusters-platforms-kubernetes.yml5
-rw-r--r--changelogs/unreleased/40453-fix-api-endpoints-to-edit-wiki-pages-where-project-belongs-to-a-group.yml5
-rw-r--r--changelogs/unreleased/40509_sorting_tags_api.yml5
-rw-r--r--changelogs/unreleased/40533-groups-tree-updates.yml6
-rw-r--r--changelogs/unreleased/40540-use-limit-for-global-search.yml5
-rw-r--r--changelogs/unreleased/40549-render-emoj-in-groups-overview.yml5
-rw-r--r--changelogs/unreleased/40622-use-left-right-and-max-count.yml6
-rw-r--r--changelogs/unreleased/40780-choose-file.yml5
-rw-r--r--changelogs/unreleased/40871-todo-notification-count-shows-notification-without-having-a-todo.yml5
-rw-r--r--changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml5
-rw-r--r--changelogs/unreleased/41016-import-gitlab-shell-projects.yml6
-rw-r--r--changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml5
-rw-r--r--changelogs/unreleased/41054-disable-creation-of-new-kubernetes-integrations.yml6
-rw-r--r--changelogs/unreleased/41056-create-cluster-from-kubernetes-integration-application-template.yml5
-rw-r--r--changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml5
-rw-r--r--changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml5
-rw-r--r--changelogs/unreleased/41244-issue-board-shortcut-working-while-no-issues.yml5
-rw-r--r--changelogs/unreleased/41247-timestamp.yml6
-rw-r--r--changelogs/unreleased/41249-clearing-the-cache.yml5
-rw-r--r--changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml5
-rw-r--r--changelogs/unreleased/41424-gitlab-rake-gitlab-import-repos-schedules-an-import.yml5
-rw-r--r--changelogs/unreleased/41468-error-500-trying-to-view-a-merge-request-json-undefined-method-binary-for-nil-nilclass.yml5
-rw-r--r--changelogs/unreleased/41491-fix-nil-blob-name-error.yml5
-rw-r--r--changelogs/unreleased/41532-email-reason.yml5
-rw-r--r--changelogs/unreleased/41673-blank-query-members-api.yml5
-rw-r--r--changelogs/unreleased/41727-target-branch-name.yml5
-rw-r--r--changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml5
-rw-r--r--changelogs/unreleased/41754-update-scss-lint-to-0-56-0.yml5
-rw-r--r--changelogs/unreleased/41789-fix-up-web-ide-user-preference-copy-and-buttons.yml5
-rw-r--r--changelogs/unreleased/41814-text-decoration-skip.yml5
-rw-r--r--changelogs/unreleased/41882-respect-only-path-in-relative-link-filter.yml5
-rw-r--r--changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml5
-rw-r--r--changelogs/unreleased/42025-fix-issue-api.yml5
-rw-r--r--changelogs/unreleased/42031-fix-links-to-uploads-in-wikis.yml5
-rw-r--r--changelogs/unreleased/42046-fork-icon.yml5
-rw-r--r--changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml5
-rw-r--r--changelogs/unreleased/42154-fix-artifact-size-calc.yml5
-rw-r--r--changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml5
-rw-r--r--changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml5
-rw-r--r--changelogs/unreleased/42206-permit-password-for-git-param.yml5
-rw-r--r--changelogs/unreleased/42220-add-pending-empty-state.yml5
-rw-r--r--changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml5
-rw-r--r--changelogs/unreleased/42251-explicit-timezone-for-karma.yml5
-rw-r--r--changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/42285-not-found-status-icon.yml5
-rw-r--r--changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml6
-rw-r--r--changelogs/unreleased/ac-autodevopfix-kubectl-version.yml5
-rw-r--r--changelogs/unreleased/add-tcp-check-rake-task.yml5
-rw-r--r--changelogs/unreleased/anchor-issue-references.yml6
-rw-r--r--changelogs/unreleased/api-domains-expose-project_id.yml5
-rw-r--r--changelogs/unreleased/bump_mysql_gem.yml5
-rw-r--r--changelogs/unreleased/bvl-fork-public-project-to-private-namespace.yml5
-rw-r--r--changelogs/unreleased/change-issues-closed-at-background-migration.yml5
-rw-r--r--changelogs/unreleased/changes-dropdown-ellipsis.yml5
-rw-r--r--changelogs/unreleased/conditionally-eager-load-event-target-authors.yml5
-rw-r--r--changelogs/unreleased/cs-fix-commercial-content-check.yml6
-rw-r--r--changelogs/unreleased/da-handle-hashed-storage-repos-using-repo-import-task.yml5
-rw-r--r--changelogs/unreleased/default-to-https-for-gravatar-urls.yml5
-rw-r--r--changelogs/unreleased/delay-background-migrations.yml5
-rw-r--r--changelogs/unreleased/disable-pages-on-jobs.yml6
-rw-r--r--changelogs/unreleased/disable-throwOnError-in-katex.yml5
-rw-r--r--changelogs/unreleased/dm-diff-note-for-line-performance.yml5
-rw-r--r--changelogs/unreleased/dm-project-system-hooks-in-transaction.yml5
-rw-r--r--changelogs/unreleased/docs-add-why-do-i-get-signed-out-authentication-section.yml5
-rw-r--r--changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml5
-rw-r--r--changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml5
-rw-r--r--changelogs/unreleased/feature-39591-visibility-level.yml5
-rw-r--r--changelogs/unreleased/feature-40842-provide-oracles-webgate-cookies-to-jira-requests.yml6
-rw-r--r--changelogs/unreleased/feature-api_runners_online.yml5
-rw-r--r--changelogs/unreleased/feature-merge-request-system-hook.yml5
-rw-r--r--changelogs/unreleased/file-content-large-screen-padding.yml5
-rw-r--r--changelogs/unreleased/fix-abuse-reports-link-url.yml5
-rw-r--r--changelogs/unreleased/fix-activity-inline-event-line-height.yml5
-rw-r--r--changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml5
-rw-r--r--changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml5
-rw-r--r--changelogs/unreleased/fix-cache-clear-windows.yml5
-rw-r--r--changelogs/unreleased/fix-create-mr-from-issue-with-template.yml5
-rw-r--r--changelogs/unreleased/fix-docs-help-shortcut.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-import-export-restoring-associations.yml6
-rw-r--r--changelogs/unreleased/fix-last-push-event-widget-layout.yml5
-rw-r--r--changelogs/unreleased/fix-move-2fa-disable-button.yml5
-rw-r--r--changelogs/unreleased/fix-onion-skin-reenter.yml5
-rw-r--r--changelogs/unreleased/fix-postgresql-table-grant.yml5
-rw-r--r--changelogs/unreleased/fix-profile-settings-content-width.yml5
-rw-r--r--changelogs/unreleased/fix-profile-settings-sidebar-heading.yml5
-rw-r--r--changelogs/unreleased/fix-remove-unnecessary-sidebar-element-alignment.yml5
-rw-r--r--changelogs/unreleased/fix_build_count_in_pipeline_success_maild.yml5
-rw-r--r--changelogs/unreleased/fj-40053-error-500-members-list.yml5
-rw-r--r--changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml5
-rw-r--r--changelogs/unreleased/fj-41477-fix-bug-wiki-last-version.yml5
-rw-r--r--changelogs/unreleased/fj-41598-fixing-request-mime-type.yml5
-rw-r--r--changelogs/unreleased/fj-41681-add-param-disable-commit-stats-api.yml5
-rw-r--r--changelogs/unreleased/fl-mr-widget-refactor.yml5
-rw-r--r--changelogs/unreleased/gitaly-git-http-ssh.yml6
-rw-r--r--changelogs/unreleased/gitaly-repo-exists.yml5
-rw-r--r--changelogs/unreleased/index-namespaces-lower-name.yml5
-rw-r--r--changelogs/unreleased/issue-description-field-typo.yml5
-rw-r--r--changelogs/unreleased/issue_40500.yml5
-rw-r--r--changelogs/unreleased/issues-40986-get-participants-from-issues-mr-api.yml5
-rw-r--r--changelogs/unreleased/jej-backport-authorized-keys-to-ce.yml5
-rw-r--r--changelogs/unreleased/jej-lfs-rev-list-handles-non-utf-paths-41627.yml5
-rw-r--r--changelogs/unreleased/jivl-activate-repo-cookie-preferences.yml5
-rw-r--r--changelogs/unreleased/jivl-fix-import-project-url-bug.yml5
-rw-r--r--changelogs/unreleased/jramsay-4012-i18n-compare.yml5
-rw-r--r--changelogs/unreleased/jramsay-41590-add-readme-case.yml5
-rw-r--r--changelogs/unreleased/ldap_username_attributes.yml5
-rw-r--r--changelogs/unreleased/lfs-badge.yml5
-rw-r--r--changelogs/unreleased/mk-fix-permanent-redirect-validation.yml5
-rw-r--r--changelogs/unreleased/mk-no-op-delete-conflicting-redirects.yml6
-rw-r--r--changelogs/unreleased/mr-status-box-update.yml5
-rw-r--r--changelogs/unreleased/multiple-clusters-single-list.yml5
-rw-r--r--changelogs/unreleased/optimize-issues-avoid-noop-empty-cache-updates2.yml6
-rw-r--r--changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/osw-introduce-merge-request-statistics.yml5
-rw-r--r--changelogs/unreleased/remove-incorrect-guidance.yml6
-rw-r--r--changelogs/unreleased/remove-links-mr-empty-state.yml5
-rw-r--r--changelogs/unreleased/remove-soft-removals.yml5
-rw-r--r--changelogs/unreleased/remove-tabindexes-from-tag-form.yml5
-rw-r--r--changelogs/unreleased/sh-add-gitaly-health-check.yml5
-rw-r--r--changelogs/unreleased/sh-add-schedule-pipeline-run-now.yml5
-rw-r--r--changelogs/unreleased/sh-catch-invalid-uri-markdown.yml5
-rw-r--r--changelogs/unreleased/sh-fix-bare-import-hooks.yml5
-rw-r--r--changelogs/unreleased/sh-make-kib-human.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-commit-stats.yml5
-rw-r--r--changelogs/unreleased/sh-validate-path-project-import.yml5
-rw-r--r--changelogs/unreleased/show-inline-edit-btn.yml5
-rw-r--r--changelogs/unreleased/show_proper_labels_in_board_issue_sidebar_when_issue_is_closed.yml5
-rw-r--r--changelogs/unreleased/sophie-h-gitlab-ce-patch-15.yml5
-rw-r--r--changelogs/unreleased/tc-correct-email-in-reply-to.yml5
-rw-r--r--changelogs/unreleased/update-node-docs.yml5
-rw-r--r--changelogs/unreleased/update-redis-rack.yml5
-rw-r--r--changelogs/unreleased/ux-guide-deprecation.yml6
-rw-r--r--changelogs/unreleased/winh-delete-milestone-modal.yml5
-rw-r--r--changelogs/unreleased/winh-modal-target-id.yml5
-rw-r--r--changelogs/unreleased/winh-search-page-filters.yml5
-rw-r--r--changelogs/unreleased/winh-translate-contributors-page-dates.yml5
-rw-r--r--config/application.rb6
-rw-r--r--config/dependency_decisions.yml17
-rw-r--r--config/gitlab.yml.example14
-rw-r--r--config/initializers/0_post_deployment_migrations.rb12
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/date_time_formats.rb2
-rw-r--r--config/initializers/rugged_use_gitlab_git_attributes.rb28
-rw-r--r--config/karma.config.js2
-rw-r--r--config/locales/en.yml12
-rw-r--r--config/routes/project.rb2
-rw-r--r--config/unicorn.rb.example.development13
-rw-r--r--config/webpack.config.js12
-rw-r--r--db/migrate/20170425112128_create_pipeline_schedules_table.rb2
-rw-r--r--db/migrate/20171211145425_add_can_push_to_deploy_keys_projects.rb15
-rw-r--r--db/migrate/20171215113714_populate_can_push_from_deploy_keys_projects.rb64
-rw-r--r--db/migrate/20180113220114_rework_redirect_routes_indexes.rb68
-rw-r--r--db/migrate/20180115201419_add_index_updated_at_to_issues.rb15
-rw-r--r--db/post_migrate/20171215121205_post_populate_can_push_from_deploy_keys_projects.rb63
-rw-r--r--db/post_migrate/20171215121259_remove_can_push_from_keys.rb17
-rw-r--r--db/schema.rb7
-rw-r--r--doc/README.md8
-rw-r--r--doc/administration/auth/okta.md2
-rw-r--r--doc/administration/container_registry.md2
-rw-r--r--doc/administration/high_availability/redis.md2
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md8
-rw-r--r--doc/api/applications.md37
-rw-r--r--doc/api/award_emoji.md6
-rw-r--r--doc/api/deploy_keys.md48
-rw-r--r--doc/api/group_milestones.md2
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--doc/api/milestones.md2
-rw-r--r--doc/api/notes.md2
-rw-r--r--doc/api/project_snippets.md9
-rw-r--r--doc/api/projects.md2
-rw-r--r--doc/api/system_hooks.md3
-rw-r--r--doc/articles/index.md5
-rw-r--r--doc/ci/environments.md2
-rw-r--r--doc/ci/examples/dast.md21
-rw-r--r--doc/ci/examples/php.md4
-rw-r--r--doc/ci/examples/test-and-deploy-python-application-to-heroku.md2
-rw-r--r--doc/ci/examples/test-phoenix-application.md1
-rw-r--r--doc/ci/quick_start/README.md6
-rw-r--r--doc/ci/runners/README.md22
-rw-r--r--doc/ci/ssh_keys/README.md2
-rw-r--r--doc/ci/variables/README.md4
-rw-r--r--doc/ci/yaml/README.md4
-rw-r--r--doc/development/automatic_ce_ee_merge.md2
-rw-r--r--doc/development/background_migrations.md2
-rw-r--r--doc/development/changelog.md4
-rw-r--r--doc/development/doc_styleguide.md2
-rw-r--r--doc/development/fe_guide/axios.md23
-rw-r--r--doc/development/fe_guide/vue.md2
-rw-r--r--doc/development/migration_style_guide.md2
-rw-r--r--doc/development/performance.md3
-rw-r--r--doc/development/profiling.md45
-rw-r--r--doc/development/rake_tasks.md2
-rw-r--r--doc/development/testing_guide/best_practices.md3
-rw-r--r--doc/development/ux_guide/animation.md2
-rw-r--r--doc/development/ux_guide/components.md6
-rw-r--r--doc/development/ux_guide/copy.md2
-rw-r--r--doc/development/ux_guide/index.md2
-rw-r--r--doc/install/azure/index.md4
-rw-r--r--doc/install/installation.md8
-rw-r--r--doc/install/requirements.md1
-rw-r--r--doc/raketasks/backup_restore.md28
-rw-r--r--doc/security/rack_attack.md136
-rw-r--r--doc/system_hooks/system_hooks.md129
-rw-r--r--doc/topics/autodevops/index.md63
-rw-r--r--doc/topics/git/how_to_install_git/index.md2
-rw-r--r--doc/university/glossary/README.md2
-rw-r--r--doc/university/high-availability/aws/README.md4
-rw-r--r--doc/update/10.2-to-10.3.md7
-rw-r--r--doc/update/10.3-to-10.4.md7
-rw-r--r--doc/user/admin_area/monitoring/img/convdev_index.pngbin116112 -> 119743 bytes
-rw-r--r--doc/user/profile/account/two_factor_authentication.md2
-rw-r--r--doc/user/project/clusters/index.md276
-rw-r--r--doc/user/project/integrations/emails_on_push.md2
-rw-r--r--doc/user/project/integrations/kubernetes.md10
-rw-r--r--doc/user/project/integrations/webhooks.md2
-rw-r--r--doc/user/project/issues/crosslinking_issues.md2
-rw-r--r--doc/user/project/milestones/index.md2
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md2
-rw-r--r--doc/user/project/pages/introduction.md2
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedule_play.pngbin0 -> 39142 bytes
-rw-r--r--doc/user/project/pipelines/img/pipeline_schedules_list.pngbin14665 -> 38034 bytes
-rw-r--r--doc/user/project/pipelines/schedules.md15
-rw-r--r--doc/workflow/lfs/img/lfs-icon.pngbin0 -> 4317 bytes
-rw-r--r--doc/workflow/lfs/manage_large_binaries_with_git_lfs.md5
-rw-r--r--doc/workflow/notifications.md32
-rw-r--r--features/project/issues/milestones.feature7
-rw-r--r--features/steps/project/issues/milestones.rb9
-rw-r--r--features/steps/user.rb38
-rw-r--r--features/support/capybara_helpers.rb10
-rw-r--r--features/support/db_cleaner.rb2
-rw-r--r--features/user.feature86
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/applications.rb27
-rw-r--r--lib/api/deploy_keys.rb65
-rw-r--r--lib/api/entities.rb26
-rw-r--r--lib/api/helpers/internal_helpers.rb15
-rw-r--r--lib/api/members.rb2
-rw-r--r--lib/api/project_snippets.rb2
-rw-r--r--lib/api/projects.rb4
-rw-r--r--lib/api/protected_branches.rb2
-rw-r--r--lib/api/services.rb2
-rw-r--r--lib/api/system_hooks.rb1
-rw-r--r--lib/api/v3/deploy_keys.rb46
-rw-r--r--lib/api/v3/entities.rb5
-rw-r--r--lib/api/v3/members.rb2
-rw-r--r--lib/api/v3/projects.rb2
-rw-r--r--lib/api/v3/services.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb6
-rw-r--r--lib/gitlab/background_migration/copy_column.rb2
-rw-r--r--lib/gitlab/bare_repository_import/repository.rb2
-rw-r--r--lib/gitlab/checks/change_access.rb38
-rw-r--r--lib/gitlab/ci/config/entry/validators.rb16
-rw-r--r--lib/gitlab/database/grant.rb50
-rw-r--r--lib/gitlab/database/migration_helpers.rb5
-rw-r--r--lib/gitlab/git/attributes_at_ref_parser.rb14
-rw-r--r--lib/gitlab/git/attributes_parser.rb (renamed from lib/gitlab/git/attributes.rb)53
-rw-r--r--lib/gitlab/git/blame.rb4
-rw-r--r--lib/gitlab/git/blob.rb24
-rw-r--r--lib/gitlab/git/commit.rb18
-rw-r--r--lib/gitlab/git/info_attributes.rb49
-rw-r--r--lib/gitlab/git/popen.rb2
-rw-r--r--lib/gitlab/git/ref.rb4
-rw-r--r--lib/gitlab/git/remote_mirror.rb24
-rw-r--r--lib/gitlab/git/repository.rb306
-rw-r--r--lib/gitlab/git/wiki_page.rb3
-rw-r--r--lib/gitlab/gitaly_client.rb21
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb22
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb41
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb5
-rw-r--r--lib/gitlab/gitaly_client/health_check_service.rb19
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb72
-rw-r--r--lib/gitlab/gitaly_client/remote_service.rb27
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb42
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_importer.rb5
-rw-r--r--lib/gitlab/gpg/commit.rb8
-rw-r--r--lib/gitlab/health_checks/gitaly_check.rb53
-rw-r--r--lib/gitlab/import_export/command_line_util.rb9
-rw-r--r--lib/gitlab/import_export/file_importer.rb6
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb2
-rw-r--r--lib/gitlab/import_export/repo_saver.rb2
-rw-r--r--lib/gitlab/import_export/saver.rb2
-rw-r--r--lib/gitlab/import_export/shared.rb21
-rw-r--r--lib/gitlab/import_export/wiki_repo_saver.rb2
-rw-r--r--lib/gitlab/o_auth.rb6
-rw-r--r--lib/gitlab/o_auth/user.rb11
-rw-r--r--lib/gitlab/performance_bar.rb1
-rw-r--r--lib/gitlab/profiler.rb142
-rw-r--r--lib/gitlab/project_search_results.rb2
-rw-r--r--lib/gitlab/regex.rb2
-rw-r--r--lib/gitlab/search_results.rb66
-rw-r--r--lib/gitlab/seeder.rb10
-rw-r--r--lib/gitlab/snippet_search_results.rb2
-rw-r--r--lib/gitlab/utils.rb4
-rw-r--r--lib/gitlab/workhorse.rb5
-rw-r--r--lib/support/nginx/gitlab4
-rw-r--r--lib/support/nginx/gitlab-ssl4
-rw-r--r--lib/tasks/gitlab/check.rake2
-rw-r--r--lib/tasks/gitlab/cleanup.rake2
-rw-r--r--lib/tasks/gitlab/gitaly.rake6
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake2
-rw-r--r--locale/gitlab.pot4
-rw-r--r--package.json27
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock40
-rw-r--r--qa/qa.rb38
-rw-r--r--qa/qa/factory/dependency.rb7
-rw-r--r--qa/qa/factory/resource/deploy_key.rb14
-rw-r--r--qa/qa/factory/resource/merge_request.rb49
-rw-r--r--qa/qa/factory/resource/personal_access_token.rb27
-rw-r--r--qa/qa/factory/resource/runner.rb42
-rw-r--r--qa/qa/factory/resource/secret_variable.rb41
-rw-r--r--qa/qa/page/README.md12
-rw-r--r--qa/qa/page/base.rb36
-rw-r--r--qa/qa/page/dashboard/projects.rb11
-rw-r--r--qa/qa/page/group/show.rb14
-rw-r--r--qa/qa/page/main/login.rb22
-rw-r--r--qa/qa/page/menu/main.rb9
-rw-r--r--qa/qa/page/menu/profile.rb27
-rw-r--r--qa/qa/page/menu/side.rb49
-rw-r--r--qa/qa/page/merge_request/new.rb31
-rw-r--r--qa/qa/page/profile/personal_access_tokens.rb33
-rw-r--r--qa/qa/page/project/activity.rb15
-rw-r--r--qa/qa/page/project/new.rb4
-rw-r--r--qa/qa/page/project/pipeline/index.rb13
-rw-r--r--qa/qa/page/project/pipeline/show.rb35
-rw-r--r--qa/qa/page/project/settings/advanced.rb33
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb28
-rw-r--r--qa/qa/page/project/settings/common.rb24
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb40
-rw-r--r--qa/qa/page/project/settings/main.rb21
-rw-r--r--qa/qa/page/project/settings/repository.rb11
-rw-r--r--qa/qa/page/project/settings/runners.rb35
-rw-r--r--qa/qa/page/project/settings/secret_variables.rb57
-rw-r--r--qa/qa/page/project/show.rb22
-rw-r--r--qa/qa/runtime/address.rb20
-rw-r--r--qa/qa/runtime/api.rb82
-rw-r--r--qa/qa/runtime/browser.rb23
-rw-r--r--qa/qa/runtime/env.rb6
-rw-r--r--qa/qa/runtime/rsa_key.rb21
-rw-r--r--qa/qa/runtime/user.rb11
-rw-r--r--qa/qa/scenario/entrypoint.rb34
-rw-r--r--qa/qa/scenario/taggable.rb17
-rw-r--r--qa/qa/scenario/test/instance.rb22
-rw-r--r--qa/qa/scenario/test/integration/mattermost.rb2
-rw-r--r--qa/qa/service/omnibus.rb20
-rw-r--r--qa/qa/service/runner.rb41
-rw-r--r--qa/qa/service/shellout.rb23
-rw-r--r--qa/qa/shell/omnibus.rb39
-rw-r--r--qa/qa/specs/features/api/users_spec.rb42
-rw-r--r--qa/qa/specs/features/merge_request/create_spec.rb17
-rw-r--r--qa/qa/specs/features/project/activity_spec.rb20
-rw-r--r--qa/qa/specs/features/project/add_deploy_key_spec.rb20
-rw-r--r--qa/qa/specs/features/project/add_secret_variable_spec.rb19
-rw-r--r--qa/qa/specs/features/project/pipelines_spec.rb102
-rw-r--r--qa/qa/specs/features/repository/push_spec.rb5
-rw-r--r--qa/spec/factory/base_spec.rb1
-rw-r--r--qa/spec/factory/dependency_spec.rb13
-rw-r--r--qa/spec/runtime/api_client_spec.rb30
-rw-r--r--qa/spec/runtime/api_request_spec.rb42
-rw-r--r--qa/spec/runtime/env_spec.rb8
-rw-r--r--qa/spec/runtime/rsa_key.rb9
-rw-r--r--qa/spec/scenario/test/instance_spec.rb (renamed from qa/spec/scenario/entrypoint_spec.rb)4
-rw-r--r--qa/spec/spec_helper.rb2
-rw-r--r--qa/spec/support/stub_env.rb38
-rwxr-xr-xscripts/add-code-formatters18
-rwxr-xr-xscripts/gitaly-test-build16
-rwxr-xr-xscripts/lint-rugged36
-rw-r--r--scripts/pre-commit18
-rw-r--r--scripts/prepare_build.sh2
-rwxr-xr-xscripts/static-analysis3
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb7
-rw-r--r--spec/controllers/admin/hooks_controller_spec.rb6
-rw-r--r--spec/controllers/dashboard/groups_controller_spec.rb20
-rw-r--r--spec/controllers/groups/children_controller_spec.rb24
-rw-r--r--spec/controllers/import/gitlab_projects_controller_spec.rb38
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb75
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb8
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb8
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb26
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb66
-rw-r--r--spec/factories/deploy_keys_projects.rb4
-rw-r--r--spec/factories/keys.rb4
-rw-r--r--spec/features/admin/admin_builds_spec.rb16
-rw-r--r--spec/features/admin/admin_deploy_keys_spec.rb14
-rw-r--r--spec/features/admin/admin_hooks_spec.rb65
-rw-r--r--spec/features/boards/boards_spec.rb11
-rw-r--r--spec/features/commits_spec.rb2
-rw-r--r--spec/features/cycle_analytics_spec.rb1
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb9
-rw-r--r--spec/features/issues/spam_issues_spec.rb3
-rw-r--r--spec/features/merge_request/user_assigns_themselves_spec.rb49
-rw-r--r--spec/features/merge_request/user_awards_emoji_spec.rb (renamed from spec/features/merge_requests/award_spec.rb)4
-rw-r--r--spec/features/merge_request/user_cherry_picks_spec.rb (renamed from spec/features/merge_requests/cherry_pick_spec.rb)22
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb (renamed from spec/features/merge_requests/image_diff_notes_spec.rb)39
-rw-r--r--spec/features/merge_request/user_creates_mr_spec.rb31
-rw-r--r--spec/features/merge_request/user_customizes_merge_commit_message_spec.rb (renamed from spec/features/merge_requests/merge_commit_message_toggle_spec.rb)15
-rw-r--r--spec/features/merge_request/user_edits_mr_spec.rb11
-rw-r--r--spec/features/merge_request/user_locks_discussion_spec.rb (renamed from spec/features/merge_requests/discussion_lock_spec.rb)6
-rw-r--r--spec/features/merge_request/user_merges_immediately_spec.rb (renamed from spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb)22
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb (renamed from spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb)51
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb (renamed from spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb)82
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb (renamed from spec/features/merge_requests/user_posts_diff_notes_spec.rb)15
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb (renamed from spec/features/merge_requests/user_posts_notes_spec.rb)8
-rw-r--r--spec/features/merge_request/user_resolves_conflicts_spec.rb (renamed from spec/features/merge_requests/conflicts_spec.rb)7
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb (renamed from spec/features/merge_requests/diff_notes_resolve_spec.rb)33
-rw-r--r--spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb (renamed from spec/features/merge_requests/resolve_outdated_diff_discussions.rb)2
-rw-r--r--spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb26
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb (renamed from spec/features/merge_requests/diff_notes_avatars_spec.rb)8
-rw-r--r--spec/features/merge_request/user_sees_closing_issues_message_spec.rb (renamed from spec/features/merge_requests/closes_issues_spec.rb)10
-rw-r--r--spec/features/merge_request/user_sees_deleted_target_branch_spec.rb22
-rw-r--r--spec/features/merge_request/user_sees_deployment_widget_spec.rb56
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb (renamed from spec/features/merge_requests/diffs_spec.rb)4
-rw-r--r--spec/features/merge_request/user_sees_discussions_spec.rb (renamed from spec/features/merge_requests/discussion_spec.rb)19
-rw-r--r--spec/features/merge_request/user_sees_empty_state_spec.rb30
-rw-r--r--spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb (renamed from spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb)24
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb (renamed from spec/features/merge_requests/widget_spec.rb)4
-rw-r--r--spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb (renamed from spec/features/merge_requests/mini_pipeline_graph_spec.rb)28
-rw-r--r--spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb24
-rw-r--r--spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb (renamed from spec/features/merge_requests/deleted_source_branch_spec.rb)20
-rw-r--r--spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb35
-rw-r--r--spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb34
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb (renamed from spec/features/merge_requests/pipelines_spec.rb)32
-rw-r--r--spec/features/merge_request/user_sees_system_notes_spec.rb (renamed from spec/features/merge_requests/user_sees_system_notes_spec.rb)6
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb (renamed from spec/features/merge_requests/versions_spec.rb)45
-rw-r--r--spec/features/merge_request/user_sees_wip_help_message_spec.rb (renamed from spec/features/merge_requests/wip_message_spec.rb)8
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb (renamed from spec/features/merge_requests/create_new_mr_spec.rb)9
-rw-r--r--spec/features/merge_request/user_toggles_whitespace_changes_spec.rb (renamed from spec/features/merge_requests/toggle_whitespace_changes_spec.rb)13
-rw-r--r--spec/features/merge_request/user_uses_slash_commands_spec.rb (renamed from spec/features/merge_requests/user_uses_slash_commands_spec.rb)19
-rw-r--r--spec/features/merge_requests/assign_issues_spec.rb51
-rw-r--r--spec/features/merge_requests/create_new_mr_from_fork_spec.rb89
-rw-r--r--spec/features/merge_requests/created_from_fork_spec.rb94
-rw-r--r--spec/features/merge_requests/edit_mr_spec.rb73
-rw-r--r--spec/features/merge_requests/filter_by_labels_spec.rb93
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb97
-rw-r--r--spec/features/merge_requests/filter_merge_requests_spec.rb337
-rw-r--r--spec/features/merge_requests/filters_generic_behavior_spec.rb81
-rw-r--r--spec/features/merge_requests/form_spec.rb301
-rw-r--r--spec/features/merge_requests/reset_filters_spec.rb136
-rw-r--r--spec/features/merge_requests/target_branch_spec.rb33
-rw-r--r--spec/features/merge_requests/toggler_behavior_spec.rb28
-rw-r--r--spec/features/merge_requests/user_filters_by_assignees_spec.rb36
-rw-r--r--spec/features/merge_requests/user_filters_by_labels_spec.rb49
-rw-r--r--spec/features/merge_requests/user_filters_by_milestones_spec.rb62
-rw-r--r--spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb38
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb4
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb (renamed from spec/features/merge_requests/update_merge_requests_spec.rb)20
-rw-r--r--spec/features/merge_requests/widget_deployments_spec.rb59
-rw-r--r--spec/features/oauth_login_spec.rb3
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb12
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb6
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb20
-rw-r--r--spec/features/projects/merge_requests/list_spec.rb44
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb6
-rw-r--r--spec/features/user_can_display_performance_bar_spec.rb26
-rw-r--r--spec/features/user_page_spec.rb107
-rw-r--r--spec/finders/group_descendants_finder_spec.rb20
-rw-r--r--spec/finders/milestones_finder_spec.rb41
-rw-r--r--spec/fixtures/emails/attachment.eml28
-rw-r--r--spec/helpers/application_helper_spec.rb6
-rw-r--r--spec/helpers/issuables_helper_spec.rb35
-rw-r--r--spec/helpers/projects_helper_spec.rb22
-rw-r--r--spec/initializers/settings_spec.rb2
-rw-r--r--spec/javascripts/api_spec.js4
-rw-r--r--spec/javascripts/behaviors/secret_values_spec.js110
-rw-r--r--spec/javascripts/blob/notebook/index_spec.js6
-rw-r--r--spec/javascripts/boards/board_card_spec.js2
-rw-r--r--spec/javascripts/boards/board_list_spec.js8
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js2
-rw-r--r--spec/javascripts/boards/boards_store_spec.js2
-rw-r--r--spec/javascripts/boards/list_spec.js2
-rw-r--r--spec/javascripts/boards/utils/query_data_spec.js27
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js24
-rw-r--r--spec/javascripts/clusters/clusters_index_spec.js58
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js5
-rw-r--r--spec/javascripts/create_item_dropdown_spec.js106
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js20
-rw-r--r--spec/javascripts/environments/environment_item_spec.js4
-rw-r--r--spec/javascripts/fixtures/clusters.rb15
-rw-r--r--spec/javascripts/fixtures/create_item_dropdown.html.haml13
-rw-r--r--spec/javascripts/fixtures/projects.json2
-rw-r--r--spec/javascripts/fixtures/search.rb18
-rw-r--r--spec/javascripts/flash_spec.js12
-rw-r--r--spec/javascripts/fly_out_nav_spec.js32
-rw-r--r--spec/javascripts/helpers/class_spec_helper_spec.js20
-rw-r--r--spec/javascripts/helpers/user_mock_data_helper.js2
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js35
-rw-r--r--spec/javascripts/jobs/job_details_mediator_spec.js8
-rw-r--r--spec/javascripts/jobs/mock_data.js8
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js6
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js2
-rw-r--r--spec/javascripts/notebook/cells/markdown_spec.js12
-rw-r--r--spec/javascripts/notebook/cells/output/html_sanitize_tests.js66
-rw-r--r--spec/javascripts/notebook/cells/output/html_spec.js29
-rw-r--r--spec/javascripts/notes/mock_data.js2
-rw-r--r--spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js63
-rw-r--r--spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js95
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js8
-rw-r--r--spec/javascripts/pipelines/graph/stage_column_component_spec.js15
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js7
-rw-r--r--spec/javascripts/pipelines/pipelines_table_spec.js5
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js2
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_item_spec.js2
-rw-r--r--spec/javascripts/repo/components/repo_commit_section_spec.js2
-rw-r--r--spec/javascripts/repo/components/repo_file_spec.js8
-rw-r--r--spec/javascripts/repo/components/repo_tab_spec.js4
-rw-r--r--spec/javascripts/repo/components/repo_tabs_spec.js2
-rw-r--r--spec/javascripts/repo/stores/actions/file_spec.js31
-rw-r--r--spec/javascripts/repo/stores/actions_spec.js27
-rw-r--r--spec/javascripts/repo/stores/mutations/file_spec.js2
-rw-r--r--spec/javascripts/repo/stores/mutations/tree_spec.js2
-rw-r--r--spec/javascripts/search_spec.js40
-rw-r--r--spec/javascripts/sidebar/mock_data.js34
-rw-r--r--spec/javascripts/sidebar/sidebar_store_spec.js6
-rw-r--r--spec/javascripts/signin_tabs_memoizer_spec.js7
-rw-r--r--spec/javascripts/test_bundle.js10
-rw-r--r--spec/javascripts/toggle_buttons_spec.js120
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js38
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js44
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js37
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js57
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js36
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js106
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js138
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js123
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js33
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js131
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js208
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js34
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js10
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js10
-rw-r--r--spec/javascripts/vue_shared/components/loading_icon_spec.js3
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js9
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb17
-rw-r--r--spec/lib/gitlab/auth_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb55
-rw-r--r--spec/lib/gitlab/ci/config/entry/key_spec.rb62
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb2
-rw-r--r--spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb28
-rw-r--r--spec/lib/gitlab/git/attributes_parser_spec.rb (renamed from spec/lib/gitlab/git/attributes_spec.rb)53
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb70
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb78
-rw-r--r--spec/lib/gitlab/git/gitlab_projects_spec.rb3
-rw-r--r--spec/lib/gitlab/git/info_attributes_spec.rb43
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb79
-rw-r--r--spec/lib/gitlab/git_access_spec.rb14
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb23
-rw-r--r--spec/lib/gitlab/gitaly_client/health_check_service_spec.rb41
-rw-r--r--spec/lib/gitlab/gitaly_client/remote_service_spec.rb14
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb25
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb6
-rw-r--r--spec/lib/gitlab/gpg/commit_spec.rb24
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb4
-rw-r--r--spec/lib/gitlab/health_checks/gitaly_check_spec.rb57
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb57
-rw-r--r--spec/lib/gitlab/import_export/project.json2
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/profiler_spec.rb156
-rw-r--r--spec/lib/gitlab/search_results_spec.rb58
-rw-r--r--spec/lib/gitlab/utils_spec.rb16
-rw-r--r--spec/mailers/notify_spec.rb49
-rw-r--r--spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb2
-rw-r--r--spec/migrations/calculate_conv_dev_index_percentages_spec.rb2
-rw-r--r--spec/migrations/fix_wrongly_renamed_routes_spec.rb65
-rw-r--r--spec/migrations/migrate_issues_to_ghost_user_spec.rb6
-rw-r--r--spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb2
-rw-r--r--spec/migrations/migrate_user_project_view_spec.rb2
-rw-r--r--spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb43
-rw-r--r--spec/migrations/remove_duplicate_mr_events_spec.rb2
-rw-r--r--spec/migrations/remove_empty_fork_networks_spec.rb13
-rw-r--r--spec/migrations/rename_more_reserved_project_names_spec.rb4
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb4
-rw-r--r--spec/migrations/rename_users_with_renamed_namespace_spec.rb2
-rw-r--r--spec/migrations/update_retried_for_ci_build_spec.rb2
-rw-r--r--spec/migrations/update_upload_paths_to_system_spec.rb58
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/commit_spec.rb6
-rw-r--r--spec/models/concerns/avatarable_spec.rb39
-rw-r--r--spec/models/concerns/triggerable_hooks_spec.rb43
-rw-r--r--spec/models/deploy_keys_project_spec.rb2
-rw-r--r--spec/models/hooks/system_hook_spec.rb3
-rw-r--r--spec/models/hooks/web_hook_spec.rb6
-rw-r--r--spec/models/member_spec.rb4
-rw-r--r--spec/models/merge_request_spec.rb46
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/project_services/microsoft_teams_service_spec.rb4
-rw-r--r--spec/models/project_spec.rb40
-rw-r--r--spec/models/project_statistics_spec.rb6
-rw-r--r--spec/models/repository_spec.rb66
-rw-r--r--spec/models/route_spec.rb61
-rw-r--r--spec/models/service_spec.rb34
-rw-r--r--spec/models/user_spec.rb18
-rw-r--r--spec/models/wiki_page_spec.rb11
-rw-r--r--spec/requests/api/applications_spec.rb86
-rw-r--r--spec/requests/api/commits_spec.rb12
-rw-r--r--spec/requests/api/deploy_keys_spec.rb12
-rw-r--r--spec/requests/api/internal_spec.rb41
-rw-r--r--spec/requests/api/issues_spec.rb4
-rw-r--r--spec/requests/api/jobs_spec.rb2
-rw-r--r--spec/requests/api/members_spec.rb10
-rw-r--r--spec/requests/api/merge_requests_spec.rb26
-rw-r--r--spec/requests/api/project_snippets_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb26
-rw-r--r--spec/requests/api/protected_branches_spec.rb6
-rw-r--r--spec/requests/api/services_spec.rb4
-rw-r--r--spec/requests/api/system_hooks_spec.rb20
-rw-r--r--spec/requests/api/v3/builds_spec.rb2
-rw-r--r--spec/requests/api/v3/commits_spec.rb6
-rw-r--r--spec/requests/api/v3/deploy_keys_spec.rb2
-rw-r--r--spec/requests/api/v3/members_spec.rb10
-rw-r--r--spec/requests/api/v3/merge_requests_spec.rb26
-rw-r--r--spec/requests/lfs_http_spec.rb4
-rw-r--r--spec/serializers/deploy_key_entity_spec.rb15
-rw-r--r--spec/services/files/update_service_spec.rb12
-rw-r--r--spec/services/issues/move_service_spec.rb4
-rw-r--r--spec/services/merge_requests/create_service_spec.rb61
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb25
-rw-r--r--spec/services/notification_service_spec.rb62
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb15
-rw-r--r--spec/services/projects/gitlab_projects_import_service_spec.rb31
-rw-r--r--spec/services/test_hooks/system_service_spec.rb20
-rw-r--r--spec/spec_helper.rb10
-rw-r--r--spec/support/cycle_analytics_helpers.rb31
-rw-r--r--spec/support/db_cleaner.rb26
-rw-r--r--spec/support/devise_helpers.rb15
-rw-r--r--spec/support/email_helpers.rb4
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb6
-rw-r--r--spec/support/login_helpers.rb7
-rw-r--r--spec/support/project_forks_helper.rb4
-rw-r--r--spec/support/services_shared_context.rb8
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb99
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb140
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb1
-rw-r--r--spec/support/test_env.rb2
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb6
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb4
-rw-r--r--spec/views/projects/pipelines_settings/_show.html.haml_spec.rb8
-rw-r--r--spec/workers/new_issue_worker_spec.rb5
-rw-r--r--spec/workers/new_merge_request_worker_spec.rb6
-rw-r--r--spec/workers/repository_import_worker_spec.rb15
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml32
-rw-r--r--yarn.lock1563
1026 files changed, 17067 insertions, 10120 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 80ba8e5c1a1..c1d78ef2d48 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git
- gitlab-org
.default-cache: &default-cache
- key: "ruby-235-with-yarn"
+ key: "ruby-2.3.6-with-yarn"
paths:
- vendor/ruby
- .yarn-cache/
@@ -290,7 +290,7 @@ flaky-examples-check:
- scripts/merge-reports ${NEW_FLAKY_SPECS_REPORT} rspec_flaky/new_*_*.json
- scripts/detect-new-flaky-examples $NEW_FLAKY_SPECS_REPORT
-setup-test-env:
+compile-assets:
<<: *dedicated-runner
<<: *except-docs
<<: *use-pg
@@ -301,82 +301,93 @@ setup-test-env:
- node --version
- yarn install --frozen-lockfile --cache-folder .yarn-cache
- bundle exec rake gitlab:assets:compile
- - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
- - scripts/gitaly-test-build # Do not use 'bundle exec' here
artifacts:
expire_in: 7d
paths:
- node_modules
- public/assets
+
+setup-test-env:
+ <<: *dedicated-runner
+ <<: *except-docs
+ <<: *use-pg
+ stage: prepare
+ cache:
+ <<: *default-cache
+ script:
+ - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
+ - scripts/gitaly-test-build # Do not use 'bundle exec' here
+ artifacts:
+ expire_in: 7d
+ paths:
- tmp/tests
-rspec-pg 0 26: *rspec-metadata-pg
-rspec-pg 1 26: *rspec-metadata-pg
-rspec-pg 2 26: *rspec-metadata-pg
-rspec-pg 3 26: *rspec-metadata-pg
-rspec-pg 4 26: *rspec-metadata-pg
-rspec-pg 5 26: *rspec-metadata-pg
-rspec-pg 6 26: *rspec-metadata-pg
-rspec-pg 7 26: *rspec-metadata-pg
-rspec-pg 8 26: *rspec-metadata-pg
-rspec-pg 9 26: *rspec-metadata-pg
-rspec-pg 10 26: *rspec-metadata-pg
-rspec-pg 11 26: *rspec-metadata-pg
-rspec-pg 12 26: *rspec-metadata-pg
-rspec-pg 13 26: *rspec-metadata-pg
-rspec-pg 14 26: *rspec-metadata-pg
-rspec-pg 15 26: *rspec-metadata-pg
-rspec-pg 16 26: *rspec-metadata-pg
-rspec-pg 17 26: *rspec-metadata-pg
-rspec-pg 18 26: *rspec-metadata-pg
-rspec-pg 19 26: *rspec-metadata-pg
-rspec-pg 20 26: *rspec-metadata-pg
-rspec-pg 21 26: *rspec-metadata-pg
-rspec-pg 22 26: *rspec-metadata-pg
-rspec-pg 23 26: *rspec-metadata-pg
-rspec-pg 24 26: *rspec-metadata-pg
-rspec-pg 25 26: *rspec-metadata-pg
-
-rspec-mysql 0 26: *rspec-metadata-mysql
-rspec-mysql 1 26: *rspec-metadata-mysql
-rspec-mysql 2 26: *rspec-metadata-mysql
-rspec-mysql 3 26: *rspec-metadata-mysql
-rspec-mysql 4 26: *rspec-metadata-mysql
-rspec-mysql 5 26: *rspec-metadata-mysql
-rspec-mysql 6 26: *rspec-metadata-mysql
-rspec-mysql 7 26: *rspec-metadata-mysql
-rspec-mysql 8 26: *rspec-metadata-mysql
-rspec-mysql 9 26: *rspec-metadata-mysql
-rspec-mysql 10 26: *rspec-metadata-mysql
-rspec-mysql 11 26: *rspec-metadata-mysql
-rspec-mysql 12 26: *rspec-metadata-mysql
-rspec-mysql 13 26: *rspec-metadata-mysql
-rspec-mysql 14 26: *rspec-metadata-mysql
-rspec-mysql 15 26: *rspec-metadata-mysql
-rspec-mysql 16 26: *rspec-metadata-mysql
-rspec-mysql 17 26: *rspec-metadata-mysql
-rspec-mysql 18 26: *rspec-metadata-mysql
-rspec-mysql 19 26: *rspec-metadata-mysql
-rspec-mysql 20 26: *rspec-metadata-mysql
-rspec-mysql 21 26: *rspec-metadata-mysql
-rspec-mysql 22 26: *rspec-metadata-mysql
-rspec-mysql 23 26: *rspec-metadata-mysql
-rspec-mysql 24 26: *rspec-metadata-mysql
-rspec-mysql 25 26: *rspec-metadata-mysql
-
-spinach-pg 0 4: *spinach-metadata-pg
-spinach-pg 1 4: *spinach-metadata-pg
-spinach-pg 2 4: *spinach-metadata-pg
-spinach-pg 3 4: *spinach-metadata-pg
-
-spinach-mysql 0 4: *spinach-metadata-mysql
-spinach-mysql 1 4: *spinach-metadata-mysql
-spinach-mysql 2 4: *spinach-metadata-mysql
-spinach-mysql 3 4: *spinach-metadata-mysql
+rspec-pg 0 27: *rspec-metadata-pg
+rspec-pg 1 27: *rspec-metadata-pg
+rspec-pg 2 27: *rspec-metadata-pg
+rspec-pg 3 27: *rspec-metadata-pg
+rspec-pg 4 27: *rspec-metadata-pg
+rspec-pg 5 27: *rspec-metadata-pg
+rspec-pg 6 27: *rspec-metadata-pg
+rspec-pg 7 27: *rspec-metadata-pg
+rspec-pg 8 27: *rspec-metadata-pg
+rspec-pg 9 27: *rspec-metadata-pg
+rspec-pg 10 27: *rspec-metadata-pg
+rspec-pg 11 27: *rspec-metadata-pg
+rspec-pg 12 27: *rspec-metadata-pg
+rspec-pg 13 27: *rspec-metadata-pg
+rspec-pg 14 27: *rspec-metadata-pg
+rspec-pg 15 27: *rspec-metadata-pg
+rspec-pg 16 27: *rspec-metadata-pg
+rspec-pg 17 27: *rspec-metadata-pg
+rspec-pg 18 27: *rspec-metadata-pg
+rspec-pg 19 27: *rspec-metadata-pg
+rspec-pg 20 27: *rspec-metadata-pg
+rspec-pg 21 27: *rspec-metadata-pg
+rspec-pg 22 27: *rspec-metadata-pg
+rspec-pg 23 27: *rspec-metadata-pg
+rspec-pg 24 27: *rspec-metadata-pg
+rspec-pg 25 27: *rspec-metadata-pg
+rspec-pg 26 27: *rspec-metadata-pg
+
+rspec-mysql 0 27: *rspec-metadata-mysql
+rspec-mysql 1 27: *rspec-metadata-mysql
+rspec-mysql 2 27: *rspec-metadata-mysql
+rspec-mysql 3 27: *rspec-metadata-mysql
+rspec-mysql 4 27: *rspec-metadata-mysql
+rspec-mysql 5 27: *rspec-metadata-mysql
+rspec-mysql 6 27: *rspec-metadata-mysql
+rspec-mysql 7 27: *rspec-metadata-mysql
+rspec-mysql 8 27: *rspec-metadata-mysql
+rspec-mysql 9 27: *rspec-metadata-mysql
+rspec-mysql 10 27: *rspec-metadata-mysql
+rspec-mysql 11 27: *rspec-metadata-mysql
+rspec-mysql 12 27: *rspec-metadata-mysql
+rspec-mysql 13 27: *rspec-metadata-mysql
+rspec-mysql 14 27: *rspec-metadata-mysql
+rspec-mysql 15 27: *rspec-metadata-mysql
+rspec-mysql 16 27: *rspec-metadata-mysql
+rspec-mysql 17 27: *rspec-metadata-mysql
+rspec-mysql 18 27: *rspec-metadata-mysql
+rspec-mysql 19 27: *rspec-metadata-mysql
+rspec-mysql 20 27: *rspec-metadata-mysql
+rspec-mysql 21 27: *rspec-metadata-mysql
+rspec-mysql 22 27: *rspec-metadata-mysql
+rspec-mysql 23 27: *rspec-metadata-mysql
+rspec-mysql 24 27: *rspec-metadata-mysql
+rspec-mysql 25 27: *rspec-metadata-mysql
+rspec-mysql 26 27: *rspec-metadata-mysql
+
+spinach-pg 0 3: *spinach-metadata-pg
+spinach-pg 1 3: *spinach-metadata-pg
+spinach-pg 2 3: *spinach-metadata-pg
+
+spinach-mysql 0 3: *spinach-metadata-mysql
+spinach-mysql 1 3: *spinach-metadata-mysql
+spinach-mysql 2 3: *spinach-metadata-mysql
# Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis
- <<: *pull-cache
variables:
SIMPLECOV: "false"
SETUP_DB: "false"
@@ -397,6 +408,12 @@ static-analysis:
stage: test
script:
- scripts/static-analysis
+ cache:
+ key: "ruby-2.3.6-with-yarn-and-rubocop"
+ paths:
+ - vendor/ruby
+ - .yarn-cache/
+ - tmp/rubocop_cache
# Documentation checks:
# - Check validity of relative links
@@ -664,6 +681,7 @@ lint:javascript:report:
<<: *pull-cache
stage: post-test
dependencies:
+ - compile-assets
- setup-test-env
before_script: []
script:
@@ -704,8 +722,6 @@ pages:
cache gems:
<<: *dedicated-runner
<<: *pull-cache
- only:
- - tags
variables:
SETUP_DB: "false"
script:
@@ -716,6 +732,7 @@ cache gems:
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
+ - tags
gitlab_git_test:
<<: *dedicated-runner
diff --git a/.rubocop.yml b/.rubocop.yml
index 9adc2fae7a8..563a00db6c0 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -17,6 +17,7 @@ AllCops:
- 'bin/**/*'
- 'generator_templates/**/*'
- 'builds/**/*'
+ CacheRootDirectory: tmp
# Gitlab ###################################################################
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 085dc153596..442d61bcf4f 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,27 +1,26 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
-# on 2017-12-14 12:04:26 +0100 using RuboCop version 0.52.0.
+# on 2018-01-18 18:23:26 +0100 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 174
+# Offense count: 181
Capybara/CurrentPathExpectation:
Enabled: false
-# Offense count: 951
+# Offense count: 956
Capybara/FeatureMethods:
Enabled: false
-# Offense count: 24
+# Offense count: 23
FactoryBot/DynamicAttributeDefinedStatically:
Exclude:
- 'spec/factories/broadcast_messages.rb'
- 'spec/factories/ci/builds.rb'
- 'spec/factories/ci/runners.rb'
- 'spec/factories/clusters/applications/helm.rb'
- - 'spec/factories/clusters/applications/ingress.rb'
- 'spec/factories/clusters/platforms/kubernetes.rb'
- 'spec/factories/emails.rb'
- 'spec/factories/gpg_keys.rb'
@@ -33,40 +32,31 @@ FactoryBot/DynamicAttributeDefinedStatically:
- 'spec/factories/todos.rb'
- 'spec/factories/uploads.rb'
-# Offense count: 65
+# Offense count: 167
# Cop supports --auto-correct.
Layout/EmptyLinesAroundArguments:
Enabled: false
-# Offense count: 249
+# Offense count: 253
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
Layout/ExtraSpacing:
Enabled: false
-# Offense count: 82
+# Offense count: 83
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
Layout/IndentArray:
Enabled: false
-# Offense count: 239
+# Offense count: 237
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: special_inside_parentheses, consistent, align_braces
Layout/IndentHash:
Enabled: false
-# Offense count: 15
-# Cop supports --auto-correct.
-# Configuration parameters: .
-# SupportedStyles: space, no_space
-# SupportedStylesForEmptyBraces: space, no_space
-Layout/SpaceBeforeBlockBraces:
- EnforcedStyle: space
- EnforcedStyleForEmptyBraces: space
-
# Offense count: 11
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
@@ -97,7 +87,7 @@ Layout/SpaceInsideArrayLiteralBrackets:
Exclude:
- 'spec/lib/gitlab/import_export/relation_factory_spec.rb'
-# Offense count: 323
+# Offense count: 327
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
# SupportedStyles: space, no_space
@@ -105,7 +95,7 @@ Layout/SpaceInsideArrayLiteralBrackets:
Layout/SpaceInsideBlockBraces:
Enabled: false
-# Offense count: 146
+# Offense count: 156
# Cop supports --auto-correct.
Layout/SpaceInsideParens:
Enabled: false
@@ -118,7 +108,7 @@ Layout/SpaceInsidePercentLiteralDelimiters:
- 'lib/gitlab/health_checks/fs_shards_check.rb'
- 'spec/lib/gitlab/health_checks/fs_shards_check_spec.rb'
-# Offense count: 25
+# Offense count: 26
Lint/DuplicateMethods:
Exclude:
- 'app/models/application_setting.rb'
@@ -144,7 +134,7 @@ Lint/InterpolationCheck:
- 'spec/features/users_spec.rb'
- 'spec/services/quick_actions/interpret_service_spec.rb'
-# Offense count: 198
+# Offense count: 206
# Configuration parameters: MaximumRangeSize.
Lint/MissingCopEnableDirective:
Enabled: false
@@ -185,6 +175,12 @@ Lint/UriEscapeUnescape:
- 'spec/requests/api/issues_spec.rb'
- 'spec/requests/api/v3/issues_spec.rb'
+# Offense count: 1
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
+# URISchemes: http, https
+Metrics/LineLength:
+ Max: 1310
+
# Offense count: 2
Naming/ConstantName:
Exclude:
@@ -202,13 +198,13 @@ Naming/HeredocDelimiterCase:
- 'spec/support/repo_helpers.rb'
- 'spec/support/seed_repo.rb'
-# Offense count: 101
+# Offense count: 112
# Configuration parameters: Blacklist.
# Blacklist: END, (?-mix:EO[A-Z]{1})
Naming/HeredocDelimiterNaming:
Enabled: false
-# Offense count: 28
+# Offense count: 27
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect.
Performance/HashEachMethods:
@@ -225,21 +221,27 @@ Performance/UriDefaultParser:
Exclude:
- 'lib/gitlab/url_sanitizer.rb'
-# Offense count: 3745
+# Offense count: 3821
# Configuration parameters: Prefixes.
# Prefixes: when, with, without
RSpec/ContextWording:
Enabled: false
-# Offense count: 291
+# Offense count: 293
RSpec/EmptyLineAfterFinalLet:
Enabled: false
-# Offense count: 180
+# Offense count: 188
RSpec/EmptyLineAfterSubject:
Enabled: false
-# Offense count: 220
+# Offense count: 258
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: method_call, block
+RSpec/ExpectChange:
+ Enabled: false
+
+# Offense count: 221
RSpec/ExpectInHook:
Enabled: false
@@ -304,7 +306,7 @@ RSpec/OverwritingSetup:
- 'spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb'
- 'spec/services/notes/quick_actions_service_spec.rb'
-# Offense count: 917
+# Offense count: 965
# Configuration parameters: Strict, EnforcedStyle.
# SupportedStyles: inflected, explicit
RSpec/PredicateMatcher:
@@ -314,13 +316,13 @@ RSpec/PredicateMatcher:
RSpec/RepeatedExample:
Enabled: false
-# Offense count: 132
+# Offense count: 140
# Configuration parameters: EnforcedStyle.
# SupportedStyles: and_return, block
RSpec/ReturnFromStub:
Enabled: false
-# Offense count: 105
+# Offense count: 112
RSpec/ScatteredLet:
Enabled: false
@@ -340,10 +342,6 @@ RSpec/SharedContext:
Exclude:
- 'spec/features/admin/admin_groups_spec.rb'
-# Offense count: 90
-RSpec/SingleLineHook:
- Enabled: false
-
# Offense count: 5
RSpec/VoidExpect:
Exclude:
@@ -353,23 +351,23 @@ RSpec/VoidExpect:
- 'spec/models/ci/runner_spec.rb'
- 'spec/services/users/destroy_service_spec.rb'
-# Offense count: 40
+# Offense count: 41
# Configuration parameters: Include.
# Include: db/migrate/*.rb
Rails/CreateTableWithTimestamps:
Enabled: false
-# Offense count: 149
+# Offense count: 155
Rails/FilePath:
Enabled: false
-# Offense count: 119
+# Offense count: 121
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasManyOrHasOneDependent:
Enabled: false
-# Offense count: 113
+# Offense count: 157
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/InverseOf:
@@ -399,12 +397,6 @@ Rails/Presence:
- 'lib/gitlab/git/hook.rb'
- 'lib/gitlab/github_import/importer/releases_importer.rb'
-# Offense count: 14
-# Cop supports --auto-correct.
-Rails/RedundantReceiverInWithOptions:
- Exclude:
- - 'config/initializers/doorkeeper_openid_connect.rb'
-
# Offense count: 2
# Configuration parameters: Include.
# Include: db/migrate/*.rb
@@ -412,7 +404,7 @@ Rails/ReversibleMigration:
Exclude:
- 'db/migrate/20160824103857_drop_unused_ci_tables.rb'
-# Offense count: 430
+# Offense count: 446
# Configuration parameters: Blacklist.
# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters
Rails/SkipsModelValidations:
@@ -439,7 +431,7 @@ Security/YAMLLoad:
- 'spec/models/clusters/platforms/kubernetes_spec.rb'
- 'spec/models/project_services/kubernetes_service_spec.rb'
-# Offense count: 63
+# Offense count: 64
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: percent_q, bare_percent
@@ -506,7 +498,7 @@ Style/EmptyLiteral:
- 'spec/requests/api/jobs_spec.rb'
- 'spec/support/chat_slash_commands_shared_examples.rb'
-# Offense count: 98
+# Offense count: 102
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: compact, expanded
@@ -523,35 +515,28 @@ Style/EvalWithLocation:
Exclude:
- 'app/models/service.rb'
-# Offense count: 52
-# Cop supports --auto-correct.
-# Configuration parameters: Autocorrect, EnforcedStyle.
-# SupportedStyles: module_function, extend_self
-Style/ExtendSelf:
- Enabled: false
-
-# Offense count: 34
+# Offense count: 35
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: format, sprintf, percent
Style/FormatString:
Enabled: false
-# Offense count: 371
+# Offense count: 384
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Enabled: false
-# Offense count: 21
+# Offense count: 22
Style/IfInsideElse:
Enabled: false
-# Offense count: 781
+# Offense count: 809
# Cop supports --auto-correct.
Style/IfUnlessModifier:
Enabled: false
-# Offense count: 71
+# Offense count: 75
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: line_count_dependent, lambda, literal
@@ -573,7 +558,7 @@ Style/LineEndConcatenation:
Style/MethodCallWithoutArgsParentheses:
Enabled: false
-# Offense count: 17
+# Offense count: 18
Style/MethodMissing:
Enabled: false
@@ -599,28 +584,28 @@ Style/MultilineIfModifier:
- 'lib/api/commit_statuses.rb'
- 'lib/gitlab/ci/trace.rb'
-# Offense count: 23
+# Offense count: 25
# Cop supports --auto-correct.
# Configuration parameters: Whitelist.
# Whitelist: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with
Style/NestedParenthesizedCalls:
Enabled: false
-# Offense count: 20
+# Offense count: 19
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength.
# SupportedStyles: skip_modifier_ifs, always
Style/Next:
Enabled: false
-# Offense count: 58
+# Offense count: 61
# Cop supports --auto-correct.
# Configuration parameters: EnforcedOctalStyle.
# SupportedOctalStyles: zero_with_o, zero_only
Style/NumericLiteralPrefix:
Enabled: false
-# Offense count: 112
+# Offense count: 114
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle.
# SupportedStyles: predicate, comparison
@@ -641,7 +626,7 @@ Style/OrAssignment:
Style/ParallelAssignment:
Enabled: false
-# Offense count: 891
+# Offense count: 917
# Cop supports --auto-correct.
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
@@ -663,14 +648,14 @@ Style/PerlBackrefs:
- 'lib/gitlab/search_results.rb'
- 'lib/gitlab/sherlock/query.rb'
-# Offense count: 82
+# Offense count: 87
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
Enabled: false
-# Offense count: 8
+# Offense count: 9
# Cop supports --auto-correct.
Style/RedundantBegin:
Exclude:
@@ -689,7 +674,7 @@ Style/RedundantConditional:
Exclude:
- 'lib/system_check/helpers.rb'
-# Offense count: 58
+# Offense count: 57
# Cop supports --auto-correct.
Style/RedundantFreeze:
Enabled: false
@@ -709,31 +694,31 @@ Style/RedundantReturn:
- 'lib/gitlab/utils.rb'
- 'lib/google_api/auth.rb'
-# Offense count: 454
+# Offense count: 460
# Cop supports --auto-correct.
Style/RedundantSelf:
Enabled: false
-# Offense count: 140
+# Offense count: 142
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
# SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral:
Enabled: false
-# Offense count: 35
+# Offense count: 36
# Cop supports --auto-correct.
Style/RescueModifier:
Enabled: false
-# Offense count: 105
+# Offense count: 107
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: implicit, explicit
Style/RescueStandardError:
Enabled: false
-# Offense count: 91
+# Offense count: 92
# Cop supports --auto-correct.
# Configuration parameters: ConvertCodeThatCanStartToReturnNil.
Style/SafeNavigation:
@@ -778,7 +763,7 @@ Style/StderrPuts:
Style/StringLiteralsInInterpolation:
Enabled: false
-# Offense count: 99
+# Offense count: 106
# Cop supports --auto-correct.
# Configuration parameters: IgnoredMethods.
# IgnoredMethods: respond_to, define_method
@@ -837,7 +822,7 @@ Style/UnlessElse:
- 'lib/tasks/gitlab/check.rake'
- 'spec/features/issues/award_emoji_spec.rb'
-# Offense count: 30
+# Offense count: 31
# Cop supports --auto-correct.
Style/UnneededInterpolation:
Enabled: false
@@ -856,7 +841,7 @@ Style/ZeroLengthPredicate:
- 'lib/extracts_path.rb'
- 'lib/gitlab/git/repository.rb'
-# Offense count: 22050
+# Offense count: 22840
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87260744190..248c85304a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,212 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.4.1 (2018-01-24)
+
+### Fixed (4 changes)
+
+- Ensure that users can reclaim a namespace or project path that is blocked by an orphaned route. !16242
+- Correctly escape UTF-8 path elements for uploads. !16560
+- Fix issues when rendering groups and their children. !16584
+- Fix bug in which projects with forks could not change visibility settings from Private to Public. !16595
+
+### Performance (2 changes)
+
+- rework indexes on redirect_routes.
+- Remove unecessary query from labels filter.
+
+
+## 10.4.0 (2018-01-22)
+
+### Security (8 changes, 1 of them is from the community)
+
+- Upgrade Ruby to 2.3.6 to include security patches. !16016
+- Prevent a SQL injection in the MilestonesFinder.
+- Check user authorization for source and target projects when creating a merge request.
+- Fix path traversal in gitlab-ci.yml cache:key.
+- Fix writable shared deploy keys.
+- Filter out sensitive fields from the project services API. (Robert Schilling)
+- Fix RCE via project import mechanism.
+- Prevent OAuth login POST requests when a provider has been disabled.
+
+### Fixed (68 changes, 24 of them are from the community)
+
+- Update comment on image cursor and icons. !15760
+- Fixes the wording of headers in system info page. !15802 (Gilbert Roulot)
+- Reset todo counters when the target is deleted. !15807
+- Execute quick actions (if present) when creating MR from issue. !15810
+- fix build count in pipeline success mail. !15827 (Christiaan Van den Poel)
+- Fix error that was preventing users to change the access level of access requests for Groups or Projects. !15832
+- Last push event widget width for fixed layout. !15862 (George Tsiolis)
+- Hide link to issues/MRs from labels list if issues/MRs are disabled. !15863 (Sophie Herold)
+- Use relative URL for projects to avoid storing domains. !15876
+- Fix gitlab-rake gitlab:import:repos import schedule. !15931
+- Removed incorrect guidance stating blocked users will be removed from groups and project as members. !15947 (CesarApodaca)
+- Fix some POST/DELETE requests in IE by switching some bundles to Axios for Ajax requests. !15951
+- Fixing error 500 when member exist but not the user. !15970
+- show None when issue is in closed list and no labels assigned. !15976 (Christiaan Van den Poel)
+- Fix tags in the Activity tab not being clickable. !15996 (Mario de la Ossa)
+- Disable Vue pagination when only one page of content is available. !15999 (Mario de la Ossa)
+- disables shortcut to issue boards when issues are not enabled. !16020 (Christiaan Van den Poel)
+- Ignore lost+found folder during backup on a volume. !16036 (Julien Millau)
+- Fix abuse reports link url in admin area navbar. !16068 (megos)
+- Keep typographic hierarchy in User Settings. !16090 (George Tsiolis)
+- Adjust content width for User Settings, GPG Keys. !16093 (George Tsiolis)
+- Fix gitlab-rake gitlab:import:repos import schedule. !16115
+- Fix import project url not updating project name. !16120
+- Fix activity inline event line height on mobile. !16121 (George Tsiolis)
+- Fix slash commands dropdown description mis-alignment on Firefox. !16125 (Maurizio De Santis)
+- Remove unnecessary sidebar element realignment. !16159 (George Tsiolis)
+- User#projects_limit remove DB default and added NOT NULL constraint. !16165 (Mario de la Ossa)
+- Fix API endpoints to edit wiki pages where project belongs to a group. !16170
+- Fix breadcrumbs in User Settings. !16172 (rfwatson)
+- Move 2FA disable button. !16177 (George Tsiolis)
+- Fixing bug when wiki last version. !16197
+- Protected branch is now created for default branch on import. !16198
+- Prevent excessive DB load due to faulty DeleteConflictingRedirectRoutes background migration. !16205
+- Force Auto DevOps kubectl version to 1.8.6. !16218
+- Fix missing references to pipeline objects when restoring project with import/export feature. !16221
+- Fix inconsistent downcase of filenames in prefilled `Add` commit messages. !16232 (James Ramsay)
+- Default merge request title is set correctly again when external issue tracker is activated. !16356 (Ben305)
+- Ensure that emails contain absolute, rather than relative, links to user uploads. !16364
+- Prevent invalid Route path if path is unchanged. !16397
+- Fixing rack request mime type when using rack attack. !16427
+- Prevent RevList failing on non utf8 paths. !16440
+- Fix giant fork icons on forks page. !16474
+- Fix links to uploaded files on wiki pages. !16499
+- Modify `LDAP::Person` to return username value based on attributes.
+- Fixed merge request status badge not updating after merging.
+- Remove related links in MR widget when empty state.
+- Gracefully handle garbled URIs in Markdown.
+- Fix hooks not being set up properly for bare import Rake task.
+- Fix Mermaid drawings not loading on some browsers.
+- Humanize the units of "Showing last X KiB of log" in job trace.
+- Avoid leaving a push event empty if payload cannot be created.
+- Show authored date rather than committed date on the commit list.
+- Fix when branch creation fails don't post system note. (Mateusz Bajorski)
+- Fix viewing merge request diffs where the underlying blobs are unavailable.
+- Fix 500 error when visiting a commit where the blobs do not exist.
+- Set target_branch to the ref branch when creating MR from issue.
+- Fix closed text for issues on Todos page.
+- [API] Fix creating issue when assignee_id is empty.
+- Fix false positive issue references in merge requests caused by header anchor links.
+- Fixed chanages dropdown ellipsis positioning.
+- Fix shortcut links on help page.
+- Clears visual token on second backspace. (Martin Wortschack)
+- Fix onion-skin re-entering state.
+- fix button alignment on MWPS component.
+- Add optional search param for Merge Requests API.
+- Normalizing Identity extern_uid when saving the record.
+- Fixed typo for issue description field declaration. (Marcus Amargi)
+- Fix ANSI 256 bold colors in pipelines job output.
+
+### Changed (18 changes, 3 of them are from the community)
+
+- Make mail notifications of discussion notes In-Reply-To of each other. !14289
+- Migrate existing data from KubernetesService to Clusters::Platforms::Kubernetes. !15589
+- Implement checking GCP project billing status in cluster creation form. !15665
+- Present multiple clusters in a single list instead of a tabbed view. !15669
+- Remove soft removals related code. !15789
+- Only mark import and fork jobs as failed once all Sidekiq retries get exhausted. !15844
+- Translate date ranges on contributors page. !15846
+- Update issuable status icons. !15898
+- Update feature toggle design to use icons and make it i18n friendly. !15904
+- Update groups tree to use GitLab SVG icons, add last updated at information for projects. !15980
+- Allow forking a public project to a private group. !16050
+- Expose project_id on /api/v4/pages/domains. !16200 (Luc Didry)
+- Display graph values on hover within monitoring page. !16261
+- removed tabindexes from tag form. (Marcus Amargi)
+- Move edit button to second row on issue page (and change it to a pencil icon).
+- Run background migrations with a minimum interval.
+- Provide additional cookies to JIRA service requests to allow Oracle WebGates Basic Auth. (Stanislaw Wozniak)
+- Hide markdown toolbar in preview mode.
+
+### Performance (11 changes)
+
+- Improve the performance for counting diverging commits. Show 999+ if it is more than 1000 commits. !15963
+- Treat empty markdown and html strings as valid cached text, not missing cache that needs to be updated.
+- Cache merged and closed events data in merge_request_metrics table.
+- Speed up generation of commit stats by using Rugged native methods.
+- Improve search query for issues.
+- Improve search query for merge requests.
+- Eager load event target authors whenever possible.
+- Use simple Next/Prev paging for jobs to avoid large count queries on arbitrarily large sets of historical jobs.
+- Improve performance of MR discussions on large diffs.
+- Add index on namespaces lower(name) for UsersController#exists.
+- Fix timeout when filtering issues by label.
+
+### Added (26 changes, 8 of them are from the community)
+
+- Support new chat notifications parameters in Services API. !11435
+- Add online and status attribute to runner api entity. !11750
+- Adds ordering to projects contributors in API. !15469 (Jacopo Beschi @jacopo-beschi)
+- Add assets_sync gem to Gemfile. !15734
+- Add a gitlab:tcp_check rake task. !15759
+- add support for sorting in tags api. !15772 (haseebeqx)
+- Add Prometheus to available Cluster applications. !15895
+- Validate file status when commiting multiple files. !15922
+- List of avatars should never show +1. !15972 (Jacopo Beschi @jacopo-beschi)
+- Do not generate NPM links for private NPM modules in blob view. !16002 (Mario de la Ossa)
+- Backport fast database lookup of SSH authorized_keys from EE. !16014
+- Add i18n helpers to branch comparison view. !16031 (James Ramsay)
+- Add pause/resume button to project runners. !16032 (Mario de la Ossa)
+- Added option to user preferences to enable the multi file editor. !16056
+- Implement project jobs cache reset. !16067
+- Rendering of emoji's in Group-Overview. !16098 (Jacopo Beschi @jacopo-beschi)
+- Allow automatic creation of Kubernetes Integration from template. !16104
+- API: get participants from merge_requests & issues. !16187 (Brent Greeff)
+- Added option to disable commits stats in the commit endpoint. !16309
+- Disable creation of new Kubernetes Integrations unless they're active or created from template. !41054
+- Added badge to tree & blob views to indicate LFS tracked files.
+- Enable ordering of groups and their children by name.
+- Add button to run scheduled pipeline immediately.
+- Allow user to rebase merge requests.
+- Handle GitLab hashed storage repositories using the repo import task.
+- Hide runner token in CI/CD settings page.
+
+### Other (12 changes, 3 of them are from the community)
+
+- Adds the multi file editor as a new beta feature. !15430
+- Use relative URLs when linking to uploaded files. !15751
+- Add docs for why you might be signed out when using the Remember me token. !15756
+- Replace '.team << [user, role]' with 'add_role(user)' in specs. !16069 (@blackst0ne)
+- Add id to modal.vue to support data-toggle="modal". !16189
+- Update scss-lint to 0.56.0. !16278 (Takuya Noguchi)
+- Fix web ide user preferences copy and buttons. !41789
+- Update redis-rack to 2.0.4.
+- Import some code and functionality from gitlab-shell to improve subprocess handling.
+- Update Browse file to Choose file in all occurences.
+- Bump mysql2 gem version from 0.4.5 to 0.4.10. (asaparov)
+- Use a background migration for issues.closed_at.
+
+
+## 10.3.6 (2018-01-22)
+
+### Fixed (17 changes, 2 of them are from the community)
+
+- Fix abuse reports link url in admin area navbar. !16068 (megos)
+- Fix gitlab-rake gitlab:import:repos import schedule. !16115
+- Fixing bug when wiki last version. !16197
+- Prevent excessive DB load due to faulty DeleteConflictingRedirectRoutes background migration. !16205
+- Default merge request title is set correctly again when external issue tracker is activated. !16356 (Ben305)
+- Prevent invalid Route path if path is unchanged. !16397
+- Fixing rack request mime type when using rack attack. !16427
+- Prevent RevList failing on non utf8 paths. !16440
+- Fix 500 error when visiting a commit where the blobs do not exist.
+- Fix viewing merge request diffs where the underlying blobs are unavailable.
+- Gracefully handle garbled URIs in Markdown.
+- Fix hooks not being set up properly for bare import Rake task.
+- Fix Mermaid drawings not loading on some browsers.
+- Fixed chanages dropdown ellipsis positioning.
+- Avoid leaving a push event empty if payload cannot be created.
+- Set target_branch to the ref branch when creating MR from issue.
+- Fix shortcut links on help page.
+
+
+## 10.3.5 (2018-01-18)
+
+- Fix error that prevented the 'deploy_keys' migration from working in MySQL databases.
+
## 10.3.4 (2018-01-10)
### Security (7 changes, 1 of them is from the community)
@@ -193,6 +399,10 @@ entry.
- Clean up schema of the "merge_requests" table.
+## 10.2.7 (2018-01-18)
+
+- No changes.
+
## 10.2.6 (2018-01-11)
### Security (9 changes, 1 of them is from the community)
@@ -474,6 +684,10 @@ entry.
- Add Gitaly metrics to the performance bar.
+## 10.1.7 (2018-01-18)
+
+- No changes.
+
## 10.1.6 (2018-01-11)
### Security (8 changes, 1 of them is from the community)
@@ -3243,3254 +3457,6 @@ entry.
- Add margin to markdown math blocks.
- Add hover state to MR comment reply button.
-## 8.15.8 (2017-03-19)
-
-- Only show public emails in atom feeds.
-- 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.
-
-## 8.15.7 (2017-02-15)
-
-- No changes.
-
-## 8.15.6 (2017-02-14)
-
-- Patch Asciidocs rendering to block XSS.
-- Fix XSS vulnerability in SVG attachments.
-- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
-- Patch XSS vulnerability in RDOC support.
-
-## 8.15.5 (2017-01-20)
-
-- Ensure export files are removed after a namespace is deleted.
-- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
-- Prevent users from creating notes on resources they can't access.
-- Prevent users from deleting system deploy keys via the project deploy key API.
-- Upgrade omniauth gem to 1.3.2.
-
-## 8.15.4 (2017-01-09)
-
-- Make successful pipeline emails off for watchers. !8176
-- Speed up group milestone index by passing group_id to IssuesFinder. !8363
-- Don't instrument 405 Grape calls. !8445
-- Update the gitlab-markup gem to the version 1.5.1. !8509
-- Updated Turbolinks to mitigate potential XSS attacks.
-- Re-order update steps in the 8.14 -> 8.15 upgrade guide.
-- Re-add Google Cloud Storage as a backup strategy.
-
-## 8.15.3 (2017-01-06)
-
-- Rename wiki_events to wiki_page_events in project hooks API to avoid errors. !8425
-- Rename projects wth reserved names. !8234
-- Cache project authorizations even when user has access to zero projects. !8327
-- Fix a minor grammar error in merge request widget. !8337
-- Fix unclear closing issue behaviour on Merge Request show page. !8345 (Gabriel Gizotti)
-- fix border in login session tabs. !8346
-- Copy, don't move uploaded avatar files. !8396
-- Increases width of mini-pipeline-graph dropdown to prevent wrong position on chrome on ubuntu. !8399
-- Removes invalid html and unneed CSS to prevent shaking in the pipelines tab. !8411
-- Gitlab::LDAP::Person uses LDAP attributes configuration. !8418
-- Fix 500 errors when creating a user with identity via API. !8442
-- Whitelist next project names: assets, profile, public. !8470
-- Fixed regression of note-headline-light where it was always placed on 2 lines, even on wide viewports.
-- Fix 500 error when visit group from admin area if group name contains dot.
-- Fix cross-project references copy to include the project reference.
-- Fix 500 error renaming group.
-- Fixed GFM dropdown not showing on new lines.
-
-## 8.15.2 (2016-12-27)
-
-- Fix finding the latest pipeline. !8301
-- Fix mr list timestamp alignment. !8271
-- Fix discussion overlap text in regular screens. !8273
-- Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282
-- Fix line breaking in nodes of the pipeline graph in firefox. !8292
-- Fixes confendential warning text alignment. !8293
-- Hide Scroll Top button for failed build page. !8295
-- Fix finding the latest pipeline. !8301
-- Disable PostgreSQL statement timeouts when removing unneeded services. !8322
-- Fix timeout when MR contains large files marked as binary by .gitattributes.
-- Rename "autodeploy" to "auto deploy".
-- Fixed GFM autocomplete error when no data exists.
-- Fixed resolve discussion note button color.
-
-## 8.15.1 (2016-12-23)
-
-- Push payloads schedule at most 100 commits, instead of all commits.
-- Fix Mattermost command creation by specifying username.
-- Do not override incoming webhook for mattermost and slack.
-- Adds background color for disabled state to merge when succeeds dropdown. !8222
-- Standardises font-size for titles in Issues, Merge Requests and Merge Request widget. !8235
-- Fix Pipeline builds list blank on MR. !8255
-- Do not show retried builds in pipeline stage dropdown. !8260
-
-## 8.15.0 (2016-12-22)
-
-- Whitelist next project names: notes, services.
-- Use Grape's new Route methods.
-- Fixed issue boards scrolling with a lot of lists & issues.
-- Remove unnecessary sentences for status codes in the API documentation. (Luis Alonso Chavez Armendariz)
-- Allow unauthenticated access to Repositories Files API GET endpoints.
-- Add note to the invite page when the logged in user email is not the same as the invitation.
-- Don't accidentally mark unsafe diff lines as HTML safe.
-- Add git diff context to notifications of new notes on merge requests. (Heidi Hoopes)
-- Shows group members in project members list.
-- Gem update: Update grape to 0.18.0. (Robert Schilling)
-- API: Expose merge status for branch API. (Robert Schilling)
-- Displays milestone remaining days only when it's present.
-- API: Expose committer details for commits. (Robert Schilling)
-- API: Ability to set 'should_remove_source_branch' on merge requests. (Robert Schilling)
-- Fix project import label priorities error.
-- Fix Import/Export merge requests error while importing.
-- Refactor Bitbucket importer to use BitBucket API Version 2.
-- Fix Import/Export duplicated builds error.
-- Ci::Builds have same ref as Ci::Pipeline in dev fixtures. (twonegatives)
-- For single line git commit messages, the close quote should be on the same line as the open quote.
-- Use authorized projects in ProjectTeam.
-- Destroy a user's session when they delete their own account.
-- Edit help text to clarify annotated tag creation. (Liz Lam)
-- Fixed file template dropdown for the "New File" editor for smaller/zoomed screens.
-- Fix Route#rename_children behavior.
-- Add nested groups support on data level.
-- Allow projects with 'dashboard' as path.
-- Disabled emoji buttons when user is not logged in.
-- Remove unused and void services from the database.
-- Add issue search slash command.
-- Accept issue new as command to create an issue.
-- Non members cannot create labels through the API.
-- API: expose pipeline coverage.
-- Validate state param when filtering issuables.
-- Username exists check respects relative root path.
-- Bump Git version requirement to 2.8.4.
-- Updates the font weight of button styles because of the change to system fonts.
-- Update API spec files to describe the correct class. (Livier)
-- Fixed timeago re-rendering every timeago.
-- Enable ColorVariable in scss-lint. (Sam Rose)
-- Various small emoji positioning adjustments.
-- Add shortcuts for adding users to a project team with a specific role. (Nikolay Ponomarev and Dino M)
-- Additional rounded label fixes.
-- Remove unnecessary database indices.
-- 24726 Remove Across GitLab from side navigation.
-- Changed cursor icon to pointer when mousing over stages on the Cycle Analytics pages. (Ryan Harris)
-- Add focus state to dropdown items.
-- Fixes Environments displaying incorrect date since 8.14 upgrade.
-- Improve bulk assignment for issuables.
-- Stop supporting Google and Azure as backup strategies.
-- Fix broken README.md UX guide link.
-- Allow public access to some Tag API endpoints.
-- Encode input when migrating ProcessCommitWorker jobs to prevent migration errors.
-- Adjust the width of project avatars to fix alignment within their container. (Ryan Harris)
-- Sentence cased the nav tab headers on the project dashboard page. (Ryan Harris)
-- Adds hoverstates for collapsed Issue/Merge Request sidebar.
-- Make CI badge hitboxes match parent.
-- Add a starting date to milestones.
-- Adjusted margins for Build Status and Coverage Report rows to match those of the CI/CD Pipeline row. (Ryan Harris)
-- Updated members dropdowns.
-- Move all action buttons to project header.
-- Replace issue access checks with use of IssuableFinder.
-- Fix missing Note access checks by moving Note#search to updated NoteFinder.
-- Centered Accept Merge Request button within MR widget and added padding for viewports smaller than 768px. (Ryan Harris)
-- Fix missing access checks on issue lookup using IssuableFinder.
-- Added top margin to Build status page header for mobile views. (Ryan Harris)
-- Fixes "ActionView::Template::Error: undefined method `text?` for nil:NilClass" on MR pages.
-- Issue#visible_to_user moved to IssuesFinder to prevent accidental use.
-- Replace MR access checks with use of MergeRequestsFinder.
-- Fix information disclosure in `Projects::BlobController#update`.
-- Allow branch names with dots on API endpoint.
-- Changed Housekeeping button on project settings page to default styling. (Ryan Harris)
-- Ensure issuable state changes only fire webhooks once.
-- Fix bad selection on dropdown menu for tags filter. (Luis Alonso Chavez Armendariz)
-- Fix title case to sentence case. (Luis Alonso Chavez Armendariz)
-- Fix appearance in error pages. (Luis Alonso Chavez Armendariz)
-- Create mattermost service.
-- 25617 Fix placeholder color of todo filters.
-- Made the padding on the plus button in the breadcrumb menu even. (Ryan Harris)
-- Allow to delete tag release note.
-- Ensure nil User-Agent doesn't break the CI API.
-- Replace Rack::Multipart with GitLab-Workhorse based solution. !5867
-- Add scopes for personal access tokens and OAuth tokens. !5951
-- API: Endpoint to expose personal snippets as /snippets. !6373 (Bernard Guyzmo Pratz)
-- New `gitlab:workhorse:install` rake task. !6574
-- Filter protocol-relative URLs in ExternalLinkFilter. Fixes issue #22742. !6635 (Makoto Scott-Hinkle)
-- Add support for setting the GitLab Runners Registration Token during initial database seeding. !6642
-- Guests can read builds when public. !6842
-- Made comment autocomplete more performant and removed some loading bugs. !6856
-- Add GitLab host to 2FA QR code and manual info. !6941
-- Add sorting functionality for group/project members. !7032
-- Rename Merge When Build Succeeds to Merge When Pipeline Succeeds. !7135
-- Resolve all discussions in a merge request by creating an issue collecting them. !7180 (Bob Van Landuyt)
-- Add Human Readable format for rake backup. !7188 (David Gerő)
-- post_receive: accept any user email from last commit. !7225 (Elan Ruusamäe)
-- Add support for Dockerfile templates. !7247
-- Add shorthand support to gitlab markdown references. !7255 (Oswaldo Ferreira)
-- Display error code for U2F errors. !7305 (winniehell)
-- Fix wrong tab selected when loggin fails and multiple login tabs exists. !7314 (Jacopo Beschi @jacopo-beschi)
-- Clean up common_utils.js. !7318 (winniehell)
-- Show commit status from latest pipeline. !7333
-- Remove the help text under the sidebar subscribe button and style it inline. !7389
-- Update wiki page design. !7429
-- Add nested groups support to the routing. !7459
-- Changed eslint airbnb config to the base airbnb config and corrected eslintrc plugins and envs. !7470 (Luke "Jared" Bennett)
-- Fix cancelling created or external pipelines. !7508
-- Allow admins to stop impersonating users without e-mail addresses. !7550 (Oren Kanner)
-- Remove unnecessary self from user model. !7551 (Semyon Pupkov)
-- Homogenize filter and sort dropdown look'n'feel. !7583 (David Wagner)
-- Create dynamic fixture for build_spec. !7589 (winniehell)
-- Moved Leave Project and Leave Group buttons to access_request_buttons from the settings dropdown. !7600
-- Remove unnecessary require_relative calls from service classes. !7601 (Semyon Pupkov)
-- Simplify copy on "Create a new list" dropdown in Issue Boards. !7605 (Victor Rodrigues)
-- Refactor create service spec. !7609 (Semyon Pupkov)
-- Shows unconfirmed email status in profile. !7611
-- The admin user projects view now has a clickable group link. !7620 (James Gregory)
-- Prevent DOM ID collisions resulting from user-generated content anchors. !7631
-- Replace static fixture for abuse_reports_spec. !7644 (winniehell)
-- Define common helper for describe pagination params in api. !7646 (Semyon Pupkov)
-- Move abuse report spinach test to rspec. !7659 (Semyon Pupkov)
-- Replace static fixture for awards_handler_spec. !7661 (winniehell)
-- API: Add ability to unshare a project from a group. !7662 (Robert Schilling)
-- Replace references to MergeRequestDiff#commits with st_commits when we care only about the number of commits. !7668
-- Add issue events filter and make all really show all events. !7673 (Oxan van Leeuwen)
-- Replace static fixture for notes_spec. !7683 (winniehell)
-- Replace static fixture for shortcuts_issuable_spec. !7685 (winniehell)
-- Replace static fixture for zen_mode_spec. !7686 (winniehell)
-- Replace static fixture for right_sidebar_spec. !7687 (winniehell)
-- Add online terminal support for Kubernetes. !7690
-- Move admin abuse report spinach test to rspec. !7691 (Semyon Pupkov)
-- Move admin spam spinach test to Rspec. !7708 (Semyon Pupkov)
-- Make API::Helpers find a project with only one query. !7714
-- Create builds in transaction to avoid empty pipelines. !7742
-- Render SVG images in diffs and notes. !7747 (andrebsguedes)
-- Add setting to enable/disable HTML emails. !7749
-- Use SmartInterval for MR widget and improve visibilitychange functionality. !7762
-- Resolve "Remove Builds tab from Merge Requests and Commits". !7763
-- Moved new projects button below new group button on the welcome screen. !7770
-- fix display hook error message. !7775 (basyura)
-- Refactor issuable_filters_present to reduce duplications. !7776 (Semyon Pupkov)
-- Redirect to sign-in page when unauthenticated user tries to create a snippet. !7786
-- Fix Archived project merge requests add to group's Merge Requests. !7790 (Jacopo Beschi @jacopo-beschi)
-- Update generic/external build status to match normal build status template. !7811
-- Enable AsciiDoctor admonition icons. !7812 (Horacio Sanson)
-- Do not raise error in AutocompleteController#users when not authorized. !7817 (Semyon Pupkov)
-- fix: 24982- Remove'Signed in successfully' message After this change the sign-in-success flash message will not be shown. !7837 (jnoortheen)
-- Fix Latest deployment link is broken. !7839
-- Don't display prompt to add SSH keys if SSH protocol is disabled. !7840 (Andrew Smith (EspadaV8))
-- Allow unauthenticated access to some Project API GET endpoints. !7843
-- Refactor presenters ChatCommands. !7846
-- Improve help message for issue create slash command. !7850
-- change text around timestamps to make it clear which timestamp is displayed. !7860 (BM5k)
-- Improve Build Log scrolling experience. !7895
-- Change ref property to commitRef in vue commit component. !7901
-- Prevent user creating issue or MR without signing in for a group. !7902
-- Provides a sensible default message when adding a README to a project. !7903
-- Bump ruby version to 2.3.3. !7904
-- Fix comments activity tab visibility condition. !7913 (Rydkin Maxim)
-- Remove unnecessary target branch link from MR page in case of deleted target branch. !7916 (Rydkin Maxim)
-- Add image controls to MR diffs. !7919
-- Remove wrong '.builds-feature' class from the MR settings fieldset. !7930
-- Resolve "Manual actions on pipeline graph". !7931
-- Avoid escaping relative links in Markdown twice. !7940 (winniehell)
-- Move admin hooks spinach to rspec. !7942 (Semyon Pupkov)
-- Move admin logs spinach test to rspec. !7945 (Semyon Pupkov)
-- fix: removed signed_out notification. !7958 (jnoortheen)
-- Accept environment variables from the `pre-receive` script. !7967
-- Do not reload diff for merge request made from fork when target branch in fork is updated. !7973
-- Fixes left align issue for long system notes. !7982
-- Add a slug to environments. !7983
-- Fix lookup of project by unknown ref when caching is enabled. !7988
-- Resolve "Provide SVG as a prop instead of hiding and copy them in environments table". !7992
-- Introduce deployment services, starting with a KubernetesService. !7994
-- Adds tests for custom event polyfill. !7996
-- Allow all alphanumeric characters in file names. !8002 (winniehell)
-- Added support for math rendering, using KaTeX, in Markdown and asciidoc. !8003 (Munken)
-- Remove unnecessary commits order message. !8004
-- API: Memoize the current_user so that sudo can work properly. !8017
-- group authors in contribution graph with case insensitive email handle comparison. !8021
-- Move admin active tab spinach tests to rspec. !8037 (Semyon Pupkov)
-- Add Authentiq as Oauth provider. !8038 (Alexandros Keramidas)
-- API: Ability to cherry pick a commit. !8047 (Robert Schilling)
-- Fix Slack pipeline message from pipelines made by API. !8059
-- API: Simple representation of group's projects. !8060 (Robert Schilling)
-- Prevent overflow with vertical scroll when we have space to show content. !8061
-- Allow to auto-configure Mattermost. !8070
-- Introduce $CI_BUILD_REF_SLUG. !8072
-- Added go back anchor on error pages. !8087
-- Convert CI YAML variables keys into strings. !8088
-- Adds Direct link from pipeline list to builds. !8097
-- Cache last commit id for path. !8098 (Hiroyuki Sato)
-- Pass variables from deployment project services to CI runner. !8107
-- New Gitea importer. !8116
-- Introduce "Set up autodeploy" button to help configure GitLab CI for deployment. !8135
-- Prevent enviroment table to overflow when name has underscores. !8142
-- Fix missing service error importing from EE to CE. !8144
-- Milestoneish SQL performance partially improved and memoized. !8146
-- Allow unauthenticated access to Repositories API GET endpoints. !8148
-- fix colors and margins for adjacent alert banners. !8151
-- Hides new issue button for non loggedin user. !8175
-- Fix N+1 queries on milestone show pages. !8185
-- Rename groups with .git in the end of the path. !8199
-- Whitelist next project names: help, ci, admin, search. !8227
-- Adds back CSS for progress-bars. !8237
-
-## 8.14.10 (2017-02-15)
-
-- No changes.
-
-## 8.14.9 (2017-02-14)
-
-- Patch Asciidocs rendering to block XSS.
-- Fix XSS vulnerability in SVG attachments.
-- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
-- Patch XSS vulnerability in RDOC support.
-
-## 8.14.8 (2017-01-25)
-
-- Accept environment variables from the `pre-receive` script. !7967
-- Milestoneish SQL performance partially improved and memoized. !8146
-- Fix N+1 queries on milestone show pages. !8185
-- Speed up group milestone index by passing group_id to IssuesFinder. !8363
-- Ensure issuable state changes only fire webhooks once.
-
-## 8.14.7 (2017-01-21)
-
-- Ensure export files are removed after a namespace is deleted.
-- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
-- Prevent users from creating notes on resources they can't access.
-- Prevent users from deleting system deploy keys via the project deploy key API.
-- Upgrade omniauth gem to 1.3.2.
-
-## 8.14.6 (2017-01-10)
-
-- Update the gitlab-markup gem to the version 1.5.1. !8509
-- Updated Turbolinks to mitigate potential XSS attacks.
-
-## 8.14.5 (2016-12-14)
-
-- Moved Leave Project and Leave Group buttons to access_request_buttons from the settings dropdown. !7600
-- fix display hook error message. !7775 (basyura)
-- Remove wrong '.builds-feature' class from the MR settings fieldset. !7930
-- Avoid escaping relative links in Markdown twice. !7940 (winniehell)
-- API: Memoize the current_user so that sudo can work properly. !8017
-- Displays milestone remaining days only when it's present.
-- Allow branch names with dots on API endpoint.
-- Issue#visible_to_user moved to IssuesFinder to prevent accidental use.
-- Shows group members in project members list.
-- Encode input when migrating ProcessCommitWorker jobs to prevent migration errors.
-- Fixed timeago re-rendering every timeago.
-- Fix missing Note access checks by moving Note#search to updated NoteFinder.
-
-## 8.14.4 (2016-12-08)
-
-- Fix diff view permalink highlighting. !7090
-- Fix pipeline author for Slack and use pipeline id for pipeline link. !7506
-- Fix compatibility with Internet Explorer 11 for merge requests. !7525 (Steffen Rauh)
-- Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615
-- Fix Cicking on tabs on pipeline page should set URL. !7709
-- Authorize users into imported GitLab project.
-- Destroy a user's session when they delete their own account.
-- Don't accidentally mark unsafe diff lines as HTML safe.
-- Replace MR access checks with use of MergeRequestsFinder.
-- Remove visible content caching.
-
-## 8.14.3 (2016-12-02)
-
-- Pass commit data to ProcessCommitWorker to reduce Git overhead. !7744
-- Speed up issuable dashboards.
-- Don't change relative URLs to absolute URLs in the Help page.
-- Fixes "ActionView::Template::Error: undefined method `text?` for nil:NilClass" on MR pages.
-- Fix branch validation for GitHub PR where repo/fork was renamed/deleted.
-- Validate state param when filtering issuables.
-
-## 8.14.2 (2016-12-01)
-
-- Remove caching of events data. !6578
-- Rephrase some system notes to be compatible with new system note style. !7692
-- Pass tag SHA to post-receive hook when tag is created via UI. !7700
-- Prevent error when submitting a merge request and pipeline is not defined. !7707
-- Fixes system note style in commit discussion. !7721
-- Use a Redis lease for updating authorized projects. !7733
-- Refactor JiraService by moving code out of JiraService#execute method. !7756
-- Update GitLab Workhorse to v1.0.1. !7759
-- Fix pipelines info being hidden in merge request widget. !7808
-- Fixed commit timeago not rendering after initial page.
-- Fix for error thrown in cycle analytics events if build has not started.
-- Fixed issue boards issue sorting when dragging issue into list.
-- Allow access to the wiki with git when repository feature disabled.
-- Fixed timeago not rendering when resolving a discussion.
-- Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1.
-- Timeout creating and viewing merge request for binary file.
-- Gracefully recover from Redis connection failures in Sidekiq initializer.
-
-## 8.14.1 (2016-11-28)
-
-- Fix deselecting calendar days on contribution graph. !6453 (ClemMakesApps)
-- Update grape entity to 0.6.0. !7491
-- If Build running change accept merge request when build succeeds button from orange to blue. !7577
-- Changed import sources buttons to checkboxes. !7598 (Luke "Jared" Bennett)
-- Last minute CI Style tweaks for 8.14. !7643
-- Fix exceptions when loading build trace. !7658
-- Fix wrong template rendered when CI/CD settings aren't update successfully. !7665
-- fixes last_deployment call environment is nil. !7671
-- Sort builds by name within pipeline graph. !7681
-- Correctly determine mergeability of MR with no discussions.
-- Sidekiq stats in the admin area will now show correctly on different platforms. (blackst0ne)
-- Fixed issue boards dragging card removing random issues.
-- Fix information disclosure in `Projects::BlobController#update`.
-- Fix missing access checks on issue lookup using IssuableFinder.
-- Replace issue access checks with use of IssuableFinder.
-- Non members cannot create labels through the API.
-- Fix cycle analytics plan stage when commits are missing.
-
-## 8.14.0 (2016-11-22)
-
-- Use separate email-token for incoming email and revert back the inactive feature. !5914
-- API: allow recursive tree request. !6088 (Rebeca Mendez)
-- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
-- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
-- Add button to delete all merged branches. !6449 (Toon Claes)
-- Finer-grained Git gargage collection. !6588
-- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
-- Centralize LDAP config/filter logic. !6606
-- Make system notes less intrusive. !6755
-- Process commits using a dedicated Sidekiq worker. !6802
-- Show random messages when the To Do list is empty. !6818 (Josep Llaneras)
-- Precalculate user's authorized projects in database. !6839
-- Fix record not found error on NewNoteWorker processing. !6863 (Oswaldo Ferreira)
-- Show avatars in mention dropdown. !6865
-- Fix expanding a collapsed diff when converting a symlink to a regular file. !6953
-- Defer saving project services to the database if there are no user changes. !6958
-- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
-- Display "folders" for environments. !7015
-- Make it possible to trigger builds from webhooks. !7022 (Dmitry Poray)
-- Fix showing pipeline status for a given commit from correct branch. !7034
-- Add link to build pipeline within individual build pages. !7082
-- Add api endpoint `/groups/owned`. !7103 (Borja Aparicio)
-- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
-- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
-- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
-- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
-- Fix trace patching feature - update the updated_at value. !7146
-- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
-- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
-- Add api endpoint for creating a pipeline. !7209 (Ido Leibovich)
-- Allow users to subscribe to group labels. !7215
-- Reduce API calls needed when importing issues and pull requests from GitHub. !7241 (Andrew Smith (EspadaV8))
-- Only skip group when it's actually a group in the "Share with group" select. !7262
-- Introduce round-robin project creation to spread load over multiple shards. !7266
-- Ensure merge request's "remove branch" accessors return booleans. !7267
-- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
-- Expose label IDs in API. !7275 (Rares Sfirlogea)
-- Fix invalid filename validation on eslint. !7281
-- API: Ability to retrieve version information. !7286 (Robert Schilling)
-- Added ability to throttle Sidekiq Jobs. !7292
-- Set default Sidekiq retries to 3. !7294
-- Fix double event and ajax request call on MR page. !7298 (YarNayar)
-- Unify anchor link format for MR diff files. !7298 (YarNayar)
-- Require projects before creating milestone. !7301 (gfyoung)
-- Fix error when using invalid branch name when creating a new pipeline. !7324
-- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
-- Auto-close environment when branch is deleted. !7355
-- Rework cache invalidation so only changed data is refreshed. !7360
-- Navigation bar issuables counters reflects dashboard issuables counters. !7368 (Lucas Deschamps)
-- Fix cache for commit status in commits list to respect branches. !7372
-- fixes 500 error on project show when user is not logged in and project is still empty. !7376
-- Removed gray button styling from todo buttons in sidebars. !7387
-- Fix project records with invalid visibility_level values. !7391
-- Use 'Forking in progress' title when appropriate. !7394 (Philip Karpiak)
-- Fix error links in help index page. !7396 (Fu Xu)
-- Add support for reply-by-email when the email only contains HTML. !7397
-- [Fix] Extra divider issue in dropdown. !7398
-- Project download buttons always show. !7405 (Philip Karpiak)
-- Give search-input correct padding-right value. !7407 (Philip Karpiak)
-- Remove additional padding on right-aligned items in MR widget. !7411 (Didem Acet)
-- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
-- Allow mail_room idle_timeout option to be configurable. !7423
-- Fix misaligned buttons on admin builds page. !7424 (Didem Acet)
-- Disable "Request Access" functionality by default for new projects and groups. !7425
-- fix shibboleth misconfigurations resulting in authentication bypass. !7428
-- Added Mattermost slash command. !7438
-- Allow to connect Chat account with GitLab. !7450
-- Make New Group form respect default visibility application setting. !7454 (Jacopo Beschi @jacopo-beschi)
-- Fix Error 500 when creating a merge request that contains an image that was deleted and added. !7457
-- Fix labels API by adding missing current_user parameter. !7458 (Francesco Coda Zabetta)
-- Changed restricted visibility admin buttons to checkboxes. !7463
-- Send credentials (currently for registry only) with build data to GitLab Runner. !7474
-- Fix POST /internal/allowed to cope with gitlab-shell v4.0.0 project paths. !7480
-- Adds es6-promise Polyfill. !7482
-- Added colored labels to related MR list. !7486 (Didem Acet)
-- Use setter for key instead AR callback. !7488 (Semyon Pupkov)
-- Limit labels returned for a specific project as an administrator. !7496
-- Change slack notification comment link. !7498 (Herbert Kagumba)
-- Allow registering users whose username contains dots. !7500 (Timothy Andrew)
-- Fix race condition during group deletion and remove stale records present due to this bug. !7528 (Timothy Andrew)
-- Check all namespaces on validation of new username. !7537
-- Pass correct tag target to post-receive hook when creating tag via UI. !7556
-- Add help message for configuring Mattermost slash commands. !7558
-- Fix typo in Build page JavaScript. !7563 (winniehell)
-- Make job script a required configuration entry. !7566
-- Fix errors happening when source branch of merge request is removed and then restored. !7568
-- Fix a wrong "The build for this merge request failed" message. !7579
-- Fix Margins look weird in Project page with pinned sidebar in project stats bar. !7580
-- Fix regression causing bad error message to appear on Merge Request form. !7599 (Alex Sanford)
-- Fix activity page endless scroll on large viewports. !7608
-- Fix 404 on some group pages when name contains dot. !7614
-- Do not create a new TODO when failed build is allowed to fail. !7618
-- Add deployment command to ChatOps. !7619
-- Fix 500 error when group name ends with git. !7630
-- Fix undefined error in CI linter. !7650
-- Show events per stage on Cycle Analytics page. !23449
-- Add JIRA remotelinks and prevent duplicated closing messages.
-- Fixed issue boards counter border when unauthorized.
-- Add placeholder for the example text for custom hex color on label creation popup. (Luis Alonso Chavez Armendariz)
-- Add an index for project_id in project_import_data to improve performance.
-- Fix broken commits search.
-- Assignee dropdown now searches author of issue or merge request.
-- Clicking "force remove source branch" label now toggles the checkbox again.
-- More aggressively preload on merge request and issue index pages.
-- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
-- Fixing the issue of the project fork url giving 500 when not signed instead of being redirected to sign in page. (Cagdas Gerede)
-- Fix: Guest sees some repository details and gets 404.
-- Add logging for rack attack events to production.log.
-- Add environment info to builds page.
-- Allow commit note to be visible if repo is visible.
-- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
-- Redesign pipelines page.
-- Faster search inside Project.
-- Search for a filename in a project.
-- Allow sorting groups in the API.
-- Fix: Todos Filter Shows All Users.
-- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
-- Fixed multiple requests sent when opening dropdowns.
-- Added permissions per stage to cycle analytics endpoint.
-- Fix project Visibility Level selector not using default values.
-- Add events per stage to cycle analytics.
-- Allow to test JIRA service settings without having a repository.
-- Fix JIRA references for project snippets.
-- Allow enabling and disabling commit and MR events for JIRA.
-- simplify url generation. (Jarka Kadlecova)
-- Show correct environment log in admin/logs (@duk3luk3 !7191)
-- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
-- Diff collapse won't shift when collapsing.
-- Backups do not fail anymore when using tar on annex and custom_hooks only. !5814
-- Adds user project membership expired event to clarify why user was removed (Callum Dryden)
-- Trim leading and trailing whitespace on project_path (Linus Thiel)
-- Prevent award emoji via notes for issues/MRs authored by user (barthc)
-- Adds support for the `token` attribute in project hooks API (Gauvain Pocentek)
-- Change auto selection behaviour of emoji and slash commands to be more UX/Type friendly (Yann Gravrand)
-- Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
-- Fix Markdown styling inside reference links (Jan Zdráhal)
-- Create new issue board list after creating a new label
-- Fix extra space on Build sidebar on Firefox !7060
-- Fail gracefully when creating merge request with non-existing branch (alexsanford)
-- Fix mobile layout issues in admin user overview page !7087
-- Fix HipChat notifications rendering (airatshigapov, eisnerd)
-- Removed unneeded "Builds" and "Environments" link from project titles
-- Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato)
-- Cleaned up global namespace JS !19661 (Jose Ivan Vargas)
-- Refactor Jira service to use jira-ruby gem
-- Improved todos empty state
-- Add hover to trash icon in notes !7008 (blackst0ne)
-- Hides project activity tabs when features are disabled
-- Only show one error message for an invalid email !5905 (lycoperdon)
-- Added guide describing how to upgrade PostgreSQL using Slony
-- Fix sidekiq stats in admin area (blackst0ne)
-- Added label description as tooltip to issue board list title
-- Created cycle analytics bundle JavaScript file
-- Make the milestone page more responsive (yury-n)
-- Hides container registry when repository is disabled
-- API: Fix booleans not recognized as such when using the `to_boolean` helper
-- Removed delete branch tooltip !6954
-- Stop unauthorized users dragging on milestone page (blackst0ne)
-- Restore issue boards welcome message when a project is created !6899
-- Check that JavaScript file names match convention !7238 (winniehell)
-- Do not show tooltip for active element !7105 (winniehell)
-- Escape ref and path for relative links !6050 (winniehell)
-- Fixed link typo on /help/ui to Alerts section. !6915 (Sam Rose)
-- Fix broken issue/merge request links in JIRA comments. !6143 (Brian Kintz)
-- Fix filtering of milestones with quotes in title (airatshigapov)
-- Fix issue boards dragging bug in Safari
-- Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
-- Update mail_room and enable sentinel support to Reply By Email (!7101)
-- Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
-- Simpler arguments passed to named_route on toggle_award_url helper method
-- Fix typo in framework css class. !7086 (Daniel Voogsgerd)
-- New issue board list dropdown stays open after adding a new list
-- Fix: Backup restore doesn't clear cache
-- Optimize Event queries by removing default order
-- Add new icon for skipped builds
-- Show created icon in pipeline mini-graph
-- Remove duplicate links from sidebar
-- API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh)
-- Add Rake task to create/repair GitLab Shell hooks symlinks !5634
-- Add job for removal of unreferenced LFS objects from both the database and the filesystem (Frank Groeneveld)
-- Replace jquery.cookie plugin with js.cookie !7085
-- Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
-- Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens
-- Show full status link on MR & commit pipelines
-- Fix documents and comments on Build API `scope`
-- Initialize Sidekiq with the list of queues used by GitLab
-- Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
-- Shortened merge request modal to let clipboard button not overlap
-- Adds JavaScript validation for group path editing field
-- In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo)
-- Improve search query parameter naming in /admin/users !7115 (YarNayar)
-- Fix table pagination to be responsive
-- Fix applying GitHub-imported labels when importing job is interrupted
-- Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar)
-- Updated commit SHA styling on the branches page.
-- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
-- Fix 404 when visit /projects page
-
-## 8.13.12 (2017-01-21)
-
-- Ensure export files are removed after a namespace is deleted.
-- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
-- Prevent users from creating notes on resources they can't access.
-- Prevent users from deleting system deploy keys via the project deploy key API.
-- Upgrade omniauth gem to 1.3.2.
-
-## 8.13.11 (2017-01-10)
-
-- Update the gitlab-markup gem to the version 1.5.1. !8509
-- Updated Turbolinks to mitigate potential XSS attacks.
-
-## 8.13.10 (2016-12-14)
-
-- API: Memoize the current_user so that sudo can work properly. !8017
-- Filter `authentication_token`, `incoming_email_token` and `runners_token` parameters.
-- Issue#visible_to_user moved to IssuesFinder to prevent accidental use.
-- Fix missing Note access checks by moving Note#search to updated NoteFinder.
-
-## 8.13.9 (2016-12-08)
-
-- Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615
-- Replace MR access checks with use of MergeRequestsFinder.
-
-## 8.13.8 (2016-12-02)
-
-- Pass tag SHA to post-receive hook when tag is created via UI. !7700
-- Validate state param when filtering issuables.
-
-## 8.13.7 (2016-11-28)
-
-- fixes 500 error on project show when user is not logged in and project is still empty. !7376
-- Update grape entity to 0.6.0. !7491
-- Fix information disclosure in `Projects::BlobController#update`.
-- Fix missing access checks on issue lookup using IssuableFinder.
-- Replace issue access checks with use of IssuableFinder.
-- Non members cannot create labels through the API.
-
-## 8.13.6 (2016-11-17)
-
-- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
-- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option. !7117
-- Fix relative links in Markdown wiki when displayed in "Project" tab. !7218
-- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
-- Fix cache for commit status in commits list to respect branches. !7372
-- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
-- Limit labels returned for a specific project as an administrator. !7496
-- Clicking "force remove source branch" label now toggles the checkbox again.
-- Allow commit note to be visible if repo is visible.
-- Fix project Visibility Level selector not using default values.
-
-## 8.13.5 (2016-11-08)
-
-- Restore unauthenticated access to public container registries
-- Fix showing pipeline status for a given commit from correct branch. !7034
-- Only skip group when it's actually a group in the "Share with group" select. !7262
-- Introduce round-robin project creation to spread load over multiple shards. !7266
-- Ensure merge request's "remove branch" accessors return booleans. !7267
-- Ensure external users are not able to clone disabled repositories.
-- Fix XSS issue in Markdown autolinker.
-- Respect event visibility in Gitlab::ContributionsCalendar.
-- Honour issue and merge request visibility in their respective finders.
-- Disable reference Markdown for unavailable features.
-- Fix lightweight tags not processed correctly by GitTagPushService. !6532
-- Allow owners to fetch source code in CI builds. !6943
-- Return conflict error in label API when title is taken by group label. !7014
-- Reduce the overhead to calculate number of open/closed issues and merge requests within the group or project. !7123
-- Fix builds tab visibility. !7178
-- Fix project features default values. !7181
-
-## 8.13.4
-
-- Pulled due to packaging error.
-
-## 8.13.3 (2016-11-02)
-
-- Removes any symlinks before importing a project export file. CVE-2016-9086
-- Fixed Import/Export foreign key issue to do with project members.
-- Changed build dropdown list length to be 6,5 builds long in the pipeline graph
-
-## 8.13.2 (2016-10-31)
-
-- Fix encoding issues on pipeline commits. !6832
-- Use Hash rocket syntax to fix cycle analytics under Ruby 2.1. !6977
-- Modify GitHub importer to be retryable. !7003
-- Fix refs dropdown selection with special characters. !7061
-- Fix horizontal padding for highlight blocks. !7062
-- Pass user instance to `Labels::FindOrCreateService` or `skip_authorization: true`. !7093
-- Fix builds dropdown overlapping bug. !7124
-- Fix applying labels for GitHub-imported MRs. !7139
-- Fix importing MR comments from GitHub. !7139
-- Fix project member access for group links. !7144
-- API: Fix booleans not recognized as such when using the `to_boolean` helper. !7149
-- Fix and improve `Sortable.highest_label_priority`. !7165
-- Fixed sticky merge request tabs when sidebar is pinned. !7167
-- Only remove right connector of first build of last stage. !7179
-
-## 8.13.1 (2016-10-25)
-
-- Fix branch protection API. !6215
-- Fix hidden pipeline graph on commit and MR page. !6895
-- Fix Cycle analytics not showing correct data when filtering by date. !6906
-- Ensure custom provider tab labels don't break layout. !6993
-- Fix issue boards user link when in subdirectory. !7018
-- Refactor and add new environment functionality to CI yaml reference. !7026
-- Fix typo in project settings that prevents users from enabling container registry. !7037
-- Fix events order in `users/:id/events` endpoint. !7039
-- Remove extra line for empty issue description. !7045
-- Don't append issue/MR templates to any existing text. !7050
-- Fix error in generating labels. !7055
-- Stop clearing the database cache on `rake cache:clear`. !7056
-- Only show register tab if signup enabled. !7058
-- Fix lightweight tags not processed correctly by GitTagPushService
-- Expire and build repository cache after project import. !7064
-- Fix bug where labels would be assigned to issues that were moved. !7065
-- Fix reply-by-email not working due to queue name mismatch. !7068
-- Fix 404 for group pages when GitLab setup uses relative url. !7071
-- Fix `User#to_reference`. !7088
-- Reduce overhead of `LabelFinder` by avoiding `#presence` call. !7094
-- Fix unauthorized users dragging on issue boards. !7096
-- Only schedule `ProjectCacheWorker` jobs when needed. !7099
-
-## 8.13.0 (2016-10-22)
-
-- Fix save button on project pipeline settings page. (!6955)
-- All Sidekiq workers now use their own queue
-- Avoid race condition when asynchronously removing expired artifacts. (!6881)
-- Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
-- Respond with 404 Not Found for non-existent tags (Linus Thiel)
-- Truncate long labels with ellipsis in labels page
-- Improve tabbing usability for sign in page (ClemMakesApps)
-- Enforce TrailingSemicolon and EmptyLineBetweenBlocks in scss-lint
-- Adding members no longer silently fails when there is extra whitespace
-- Update runner version only when updating contacted_at
-- Add link from system note to compare with previous version
-- Use gitlab-shell v3.6.6
-- Ignore references to internal issues when using external issues tracker
-- Ability to resolve merge request conflicts with editor !6374
-- Add `/projects/visible` API endpoint (Ben Boeckel)
-- Fix centering of custom header logos (Ashley Dumaine)
-- Keep around commits only pipeline creation as pipeline data doesn't change over time
-- Update duration at the end of pipeline
-- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
-- Add group level labels. (!6425)
-- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
-- Cancelled pipelines could be retried. !6927
-- Updating verbiage on git basics to be more intuitive
-- Fix project_feature record not generated on project creation
-- Clarify documentation for Runners API (Gennady Trafimenkov)
-- Use optimistic locking for pipelines and builds
-- The instrumentation for Banzai::Renderer has been restored
-- Change user & group landing page routing from /u/:username to /:username
-- Added documentation for .gitattributes files
-- Move Pipeline Metrics to separate worker
-- AbstractReferenceFilter caches project_refs on RequestStore when active
-- Replaced the check sign to arrow in the show build view. !6501
-- Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar)
-- ProjectCacheWorker updates caches at most once per 15 minutes per project
-- Fix Error 500 when viewing old merge requests with bad diff data
-- Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar)
-- Fix viewing merged MRs when the source project has been removed !6991
-- Speed-up group milestones show page
-- Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps)
-- Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService
-- Fix discussion thread from emails for merge requests. !7010
-- Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs)
-- Add tag shortcut from the Commit page. !6543
-- Keep refs for each deployment
-- Close open tooltips on page navigation (Linus Thiel)
-- Allow browsing branches that end with '.atom'
-- Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller)
-- Replace unique keyframes mixin with keyframe mixin with specific names (ClemMakesApps)
-- Add more tests for calendar contribution (ClemMakesApps)
-- Update Gitlab Shell to fix some problems with moving projects between storages
-- Cache rendered markdown in the database, rather than Redis
-- Add todo toggle event (ClemMakesApps)
-- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
-- Simplify Mentionable concern instance methods
-- API: Ability to retrieve version information (Robert Schilling)
-- Fix permission for setting an issue's due date
-- API: Multi-file commit !6096 (mahcsig)
-- Unicode emoji are now converted to images
-- Revert "Label list shows all issues (opened or closed) with that label"
-- Expose expires_at field when sharing project on API
-- Fix VueJS template tags being rendered in code comments
-- Added copy file path button to merge request diff files
-- Fix issue with page scrolling to top when closing or pinning sidebar (lukehowell)
-- Add Issue Board API support (andrebsguedes)
-- Allow the Koding integration to be configured through the API
-- Add new issue button to each list on Issues Board
-- Execute specific named route method from toggle_award_url helper method
-- Added soft wrap button to repository file/blob editor
-- Update namespace validation to forbid reserved names (.git and .atom) (Will Starms)
-- Show the time ago a merge request was deployed to an environment
-- Add RTL support to markdown renderer (Ebrahim Byagowi)
-- Add word-wrap to issue title on issue and milestone boards (ClemMakesApps)
-- Fix todos page mobile viewport layout (ClemMakesApps)
-- Make issues search less finicky
-- Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps)
-- Remove redundant mixins (ClemMakesApps)
-- Added 'Download' button to the Snippets page (Justin DiPierro)
-- Add visibility level to project repository
-- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
-- Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska)
-- Fix showing commits from source project for merge request !6658
-- Fix that manual jobs would no longer block jobs in the next stage. !6604
-- Add configurable email subject suffix (Fu Xu)
-- Use defined colour for a language when available !6748 (nilsding)
-- Added tooltip to fork count on project show page. (Justin DiPierro)
-- Use a ConnectionPool for Rails.cache on Sidekiq servers
-- Replace `alias_method_chain` with `Module#prepend`
-- Enable GitLab Import/Export for non-admin users.
-- Preserve label filters when sorting !6136 (Joseph Frazier)
-- MergeRequest#new form load diff asynchronously
-- Only update issuable labels if they have been changed
-- Take filters in account in issuable counters. !6496
-- Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*)
-- Replace static issue fixtures by script !6059 (winniehell)
-- Append issue template to existing description !6149 (Joseph Frazier)
-- Trending projects now only show public projects and the list of projects is cached for a day
-- Memoize Gitlab Shell's secret token (!6599, Justin DiPierro)
-- Revoke button in Applications Settings underlines on hover.
-- Use higher size on Gitlab::Redis connection pool on Sidekiq servers
-- Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska)
-- Revert avoid touching file system on Build#artifacts?
-- Stop using a Redis lease when updating the project activity timestamp whenever a new event is created
-- Add disabled delete button to protected branches (ClemMakesApps)
-- Add broadcast messages and alerts below sub-nav
-- Better empty state for Groups view
-- API: New /users/:id/events endpoint
-- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
-- Replace bootstrap caret with fontawesome caret (ClemMakesApps)
-- Fix unnecessary escaping of reserved HTML characters in milestone title. !6533
-- Add organization field to user profile
-- Change user pages routing from /u/:username/PATH to /users/:username/PATH. Old routes will redirect to the new ones for the time being.
-- Fix enter key when navigating search site search dropdown. !6643 (Brennan Roberts)
-- Fix deploy status responsiveness error !6633
-- Make searching for commits case insensitive
-- Fix resolved discussion display in side-by-side diff view !6575
-- Optimize GitHub importing for speed and memory
-- API: expose pipeline data in builds API (!6502, Guilherme Salazar)
-- Notify the Merger about merge after successful build (Dimitris Karakasilis)
-- Reduce queries needed to find users using their SSH keys when pushing commits
-- Prevent rendering the link to all when the author has no access (Katarzyna Kobierska Ula Budziszewska)
-- Fix broken repository 500 errors in project list
-- Fix the diff in the merge request view when converting a symlink to a regular file
-- Fix Pipeline list commit column width should be adjusted
-- Close todos when accepting merge requests via the API !6486 (tonygambone)
-- Ability to batch assign issues relating to a merge request to the author. !5725 (jamedjo)
-- Changed Slack service user referencing from full name to username (Sebastian Poxhofer)
-- Retouch environments list and deployments list
-- Add multiple command support for all label related slash commands !6780 (barthc)
-- Add Container Registry on/off status to Admin Area !6638 (the-undefined)
-- Add Nofollow for uppercased scheme in external urls !6820 (the-undefined)
-- Allow empty merge requests !6384 (Artem Sidorenko)
-- Grouped pipeline dropdown is a scrollable container
-- Cleanup Ci::ApplicationController. !6757 (Takuya Noguchi)
-- Fixes padding in all clipboard icons that have .btn class
-- Fix a typo in doc/api/labels.md
-- Fix double-escaping in activities tab (Alexandre Maia)
-- API: all unknown routing will be handled with 404 Not Found
-- Add docs for request profiling
-- Delete dynamic environments
-- Fix buggy iOS tooltip layering behavior.
-- Make guests unable to view MRs on private projects
-- Fix broken Project API docs (Takuya Noguchi)
-- Migrate invalid project members (owner -> master)
-
-## 8.12.12 (2016-12-08)
-
-- Replace MR access checks with use of MergeRequestsFinder
-- Reenables /user API request to return private-token if user is admin and request is made with sudo
-
-## 8.12.11 (2016-12-02)
-
-- No changes
-
-## 8.12.10 (2016-11-28)
-
-- Fix information disclosure in `Projects::BlobController#update`
-- Fix missing access checks on issue lookup using IssuableFinder
-- Replace issue access checks with use of IssuableFinder
-
-## 8.12.9 (2016-11-07)
-
-- Fix XSS issue in Markdown autolinker
-
-## 8.12.8 (2016-11-02)
-
-- Removes any symlinks before importing a project export file. CVE-2016-9086
-- Fixed Import/Export foreign key issue to do with project members.
-
-## 8.12.7
-
- - Prevent running `GfmAutocomplete` setup for each diff note. !6569
- - Fix long commit messages overflow viewport in file tree. !6573
- - Use `gitlab-markup` gem instead of `github-markup` to fix `.rst` file rendering. !6659
- - Prevent flash alert text from being obscured when container is fluid. !6694
- - Fix due date being displayed as `NaN` in Safari. !6797
- - Fix JS bug with select2 because of missing `data-field` attribute in select box. !6812
- - Do not alter `force_remove_source_branch` options on MergeRequest unless specified. !6817
- - Fix GFM autocomplete setup being called several times. !6840
- - Handle case where deployment ref no longer exists. !6855
-
-## 8.12.6
-
- - Update mailroom to 0.8.1 in Gemfile.lock !6814
-
-## 8.12.5
-
- - Switch from request to env in ::API::Helpers. !6615
- - Update the mail_room gem to 0.8.1 to fix a race condition with the mailbox watching thread. !6714
- - Improve issue load time performance by avoiding ORDER BY in find_by call. !6724
- - Add a new gitlab:users:clear_all_authentication_tokens task. !6745
- - Don't send Private-Token (API authentication) headers to Sentry
- - Share projects via the API only with groups the authenticated user can access
-
-## 8.12.4
-
- - Fix "Copy to clipboard" tooltip to say "Copied!" when clipboard button is clicked. !6294 (lukehowell)
- - Fix padding in build sidebar. !6506
- - Changed compare dropdowns to dropdowns with isolated search input. !6550
- - Fix race condition on LFS Token. !6592
- - Fix type mismatch bug when closing Jira issue. !6619
- - Fix lint-doc error. !6623
- - Skip wiki creation when GitHub project has wiki enabled. !6665
- - Fix issues importing services via Import/Export. !6667
- - Restrict failed login attempts for users with 2FA enabled. !6668
- - Fix failed project deletion when feature visibility set to private. !6688
- - Prevent claiming associated model IDs via import.
- - Set GitLab project exported file permissions to owner only
- - Improve the way merge request versions are compared with each other
-
-## 8.12.3
-
- - Update Gitlab Shell to support low IO priority for storage moves
-
-## 8.12.2
-
- - Fix Import/Export not recognising correctly the imported services.
- - Fix snippets pagination
- - Fix "Create project" button layout when visibility options are restricted
- - Fix List-Unsubscribe header in emails
- - Fix IssuesController#show degradation including project on loaded notes
- - Fix an issue with the "Commits" section of the cycle analytics summary. !6513
- - Fix errors importing project feature and milestone models using GitLab project import
- - Make JWT messages Docker-compatible
- - Fix duplicate branch entry in the merge request version compare dropdown
- - Respect the fork_project permission when forking projects
- - Only update issuable labels if they have been changed
- - Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv)
- - Fix resolve discussion buttons endpoint path
- - Refactor remnants of CoffeeScript destructured opts and super !6261
-
-## 8.12.1
-
- - Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
- - Fix issue with search filter labels not displaying
-
-## 8.12.0 (2016-09-22)
-
- - Removes inconsistency regarding tagging immediatelly as merged once you create a new branch. !6408
- - Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
- - Only check :can_resolve permission if the note is resolvable
- - Bump fog-aws to v0.11.0 to support ap-south-1 region
- - Add ability to fork to a specific namespace using API. (ritave)
- - Allow to set request_access_enabled for groups and projects
- - Cleanup misalignments in Issue list view !6206
- - Only create a protected branch upon a push to a new branch if a rule for that branch doesn't exist
- - Add Pipelines for Commit
- - Prune events older than 12 months. (ritave)
- - Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- - Fix issues/merge-request templates dropdown for forked projects
- - Filter tags by name !6121
- - Update gitlab shell secret file also when it is empty. !3774 (glensc)
- - Give project selection dropdowns responsive width, make non-wrapping.
- - Fix note form hint showing slash commands supported for commits.
- - Make push events have equal vertical spacing.
- - API: Ensure invitees are not returned in Members API.
- - Preserve applied filters on issues search.
- - Add two-factor recovery endpoint to internal API !5510
- - Pass the "Remember me" value to the U2F authentication form
- - Display stages in valid order in stages dropdown on build page
- - Only update projects.last_activity_at once per hour when creating a new event
- - Cycle analytics (first iteration) !5986
- - Remove vendor prefixes for linear-gradient CSS (ClemMakesApps)
- - Move pushes_since_gc from the database to Redis
- - Limit number of shown environments on Merge Request: show only environments for target_branch, source_branch and tags
- - Add font color contrast to external label in admin area (ClemMakesApps)
- - Fix find file navigation links (ClemMakesApps)
- - Change logo animation to CSS (ClemMakesApps)
- - Instructions for enabling Git packfile bitmaps !6104
- - Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint
- - Fix long comments in diffs messing with table width
- - Add spec covering 'Gitlab::Git::committer_hash' !6433 (dandunckelman)
- - Fix pagination on user snippets page
- - Honor "fixed layout" preference in more places !6422
- - Run CI builds with the permissions of users !5735
- - Fix sorting of issues in API
- - Fix download artifacts button links !6407
- - Sort project variables by key. !6275 (Diego Souza)
- - Ensure specs on sorting of issues in API are deterministic on MySQL
- - Added ability to use predefined CI variables for environment name
- - Added ability to specify URL in environment configuration in gitlab-ci.yml
- - Escape search term before passing it to Regexp.new !6241 (winniehell)
- - Fix pinned sidebar behavior in smaller viewports !6169
- - Fix file permissions change when updating a file on the Gitlab UI !5979
- - Added horizontal padding on build page sidebar on code coverage block. !6196 (Vitaly Baev)
- - Change merge_error column from string to text type
- - Fix issue with search filter labels not displaying
- - Reduce contributions calendar data payload (ClemMakesApps)
- - Show all pipelines for merge requests even from discarded commits !6414
- - Replace contributions calendar timezone payload with dates (ClemMakesApps)
- - Changed MR widget build status to pipeline status !6335
- - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
- - Enable pipeline events by default !6278
- - Add pipeline email service !6019
- - Move parsing of sidekiq ps into helper !6245 (pascalbetz)
- - Added go to issue boards keyboard shortcut
- - Expose `sha` and `merge_commit_sha` in merge request API (Ben Boeckel)
- - Emoji can be awarded on Snippets !4456
- - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
- - Fix blame table layout width
- - Spec testing if issue authors can read issues on private projects
- - Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps)
- - Request only the LDAP attributes we need !6187
- - Center build stage columns in pipeline overview (ClemMakesApps)
- - Fix bug with tooltip not hiding on discussion toggle button
- - Rename behaviour to behavior in bug issue template for consistency (ClemMakesApps)
- - Fix bug stopping issue description being scrollable after selecting issue template
- - Remove suggested colors hover underline (ClemMakesApps)
- - Fix jump to discussion button being displayed on commit notes
- - Shorten task status phrase (ClemMakesApps)
- - Fix project visibility level fields on settings
- - Add hover color to emoji icon (ClemMakesApps)
- - Increase ci_builds artifacts_size column to 8-byte integer to allow larger files
- - Add textarea autoresize after comment (ClemMakesApps)
- - Do not write SSH public key 'comments' to authorized_keys !6381
- - Add due date to issue todos
- - Refresh todos count cache when an Issue/MR is deleted
- - Fix branches page dropdown sort alignment (ClemMakesApps)
- - Hides merge request button on branches page is user doesn't have permissions
- - Add white background for no readme container (ClemMakesApps)
- - API: Expose issue confidentiality flag. (Robert Schilling)
- - Fix markdown anchor icon interaction (ClemMakesApps)
- - Test migration paths from 8.5 until current release !4874
- - Replace animateEmoji timeout with eventListener (ClemMakesApps)
- - Show badges in Milestone tabs. !5946 (Dan Rowden)
- - Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
- - Require confirmation when not logged in for unsubscribe links !6223 (Maximiliano Perez Coto)
- - Add `wiki_page_events` to project hook APIs (Ben Boeckel)
- - Remove Gitorious import
- - Loads GFM autocomplete source only when required
- - Fix issue with slash commands not loading on new issue page
- - Fix inconsistent background color for filter input field (ClemMakesApps)
- - Remove prefixes from transition CSS property (ClemMakesApps)
- - Add Sentry logging to API calls
- - Add BroadcastMessage API
- - Merge request tabs are fixed when scrolling page
- - Use 'git update-ref' for safer web commits !6130
- - Sort pipelines requested through the API
- - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
- - Fix issue boards loading on large screens
- - Change pipeline duration to be jobs running time instead of simple wall time from start to end !6084
- - Show queued time when showing a pipeline !6084
- - Remove unused mixins (ClemMakesApps)
- - Fix issue board label filtering appending already filtered labels
- - Add search to all issue board lists
- - Scroll active tab into view on mobile
- - Fix groups sort dropdown alignment (ClemMakesApps)
- - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps)
- - Use JavaScript tooltips for mentions !5301 (winniehell)
- - Add hover state to todos !5361 (winniehell)
- - Fix icon alignment of star and fork buttons !5451 (winniehell)
- - Fix alignment of icon buttons !5887 (winniehell)
- - Added Ubuntu 16.04 support for packager.io (JonTheNiceGuy)
- - Fix markdown help references (ClemMakesApps)
- - Add last commit time to repo view (ClemMakesApps)
- - Fix accessibility and visibility of project list dropdown button !6140
- - Fix missing flash messages on service edit page (airatshigapov)
- - Added project-specific enable/disable setting for LFS !5997
- - Added group-specific enable/disable setting for LFS !6164
- - Add optional 'author' param when making commits. !5822 (dandunckelman)
- - Don't expose a user's token in the `/api/v3/user` API (!6047)
- - Remove redundant js-timeago-pending from user activity log (ClemMakesApps)
- - Ability to manage project issues, snippets, wiki, merge requests and builds access level
- - Remove inconsistent font weight for sidebar's labels (ClemMakesApps)
- - Align add button on repository view (ClemMakesApps)
- - Fix contributions calendar month label truncation (ClemMakesApps)
- - Import release note descriptions from GitHub (EspadaV8)
- - Added tests for diff notes
- - Add pipeline events to Slack integration !5525
- - Add a button to download latest successful artifacts for branches and tags !5142
- - Remove redundant pipeline tooltips (ClemMakesApps)
- - Expire commit info views after one day, instead of two weeks, to allow for user email updates
- - Add delimiter to project stars and forks count (ClemMakesApps)
- - Fix badge count alignment (ClemMakesApps)
- - Remove green outline from `New branch unavailable` button on issue page !5858 (winniehell)
- - Fix repo title alignment (ClemMakesApps)
- - Change update interval of contacted_at
- - Add LFS support to SSH !6043
- - Fix branch title trailing space on hover (ClemMakesApps)
- - Don't include 'Created By' tag line when importing from GitHub if there is a linked GitLab account (EspadaV8)
- - Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
- - Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison)
- - Order award emoji tooltips in order they were added (EspadaV8)
- - Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
- - Update merge_requests.md with a simpler way to check out a merge request. !5944
- - Fix button missing type (ClemMakesApps)
- - Gitlab::Checks is now instrumented
- - Move to project dropdown with infinite scroll for better performance
- - Fix leaking of submit buttons outside the width of a main container !18731 (originally by @pavelloz)
- - Load branches asynchronously in Cherry Pick and Revert dialogs.
- - Convert datetime coffeescript spec to ES6 (ClemMakesApps)
- - Add merge request versions !5467
- - Change using size to use count and caching it for number of group members. !5935
- - Replace play icon font with svg (ClemMakesApps)
- - Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck)
- - Reduce number of database queries on builds tab
- - Wrap text in commit message containers
- - Capitalize mentioned issue timeline notes (ClemMakesApps)
- - Fix inconsistent checkbox alignment (ClemMakesApps)
- - Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
- - Adds response mime type to transaction metric action when it's not HTML
- - Fix hover leading space bug in pipeline graph !5980
- - Avoid conflict with admin labels when importing GitHub labels
- - User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
- - Fix repository page ui issues
- - Avoid protected branches checks when verifying access without branch name
- - Add information about user and manual build start to runner as variables !6201 (Sergey Gnuskov)
- - Fixed invisible scroll controls on build page on iPhone
- - Fix error on raw build trace download for old builds stored in database !4822
- - Refactor the triggers page and documentation !6217
- - Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
- - Use default clone protocol on "check out, review, and merge locally" help page URL
- - Let the user choose a namespace and name on GitHub imports
- - API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
- - Allow bulk update merge requests from merge requests index page
- - Ensure validation messages are shown within the milestone form
- - Add notification_settings API calls !5632 (mahcsig)
- - Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska)
- - Fix URLs with anchors in wiki !6300 (houqp)
- - Deleting source project with existing fork link will close all related merge requests !6177 (Katarzyna Kobierska Ula Budziszeska)
- - Return 204 instead of 404 for /ci/api/v1/builds/register.json if no builds are scheduled for a runner !6225
- - Fix Gitlab::Popen.popen thread-safety issue
- - Add specs to removing project (Katarzyna Kobierska Ula Budziszewska)
- - Clean environment variables when running git hooks
- - Fix Import/Export issues importing protected branches and some specific models
- - Fix non-master branch readme display in tree view
- - Add UX improvements for merge request version diffs
-
-## 8.11.11 (2016-11-07)
-
-- Fix XSS issue in Markdown autolinker
-
-## 8.11.10 (2016-11-02)
-
-- Removes any symlinks before importing a project export file. CVE-2016-9086
-
-## 8.11.9
-
- - Don't send Private-Token (API authentication) headers to Sentry
- - Share projects via the API only with groups the authenticated user can access
-
-## 8.11.8
-
- - Respect the fork_project permission when forking projects
- - Set a restrictive CORS policy on the API for credentialed requests
- - API: disable rails session auth for non-GET/HEAD requests
- - Escape HTML nodes in builds commands in CI linter
-
-## 8.11.7
-
- - Avoid conflict with admin labels when importing GitHub labels. !6158
- - Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234
- - Allow the Rails cookie to be used for API authentication.
- - Login/Register UX upgrade !6328
-
-## 8.11.6
-
- - Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
- - Make merge conflict file size limit 200 KB, to match the docs. !6052
- - Fix an error where we were unable to create a CommitStatus for running state. !6107
- - Optimize discussion notes resolving and unresolving. !6141
- - Fix GitLab import button. !6167
- - Restore SSH Key title auto-population behavior. !6186
- - Fix DB schema to match latest migration. !6256
- - Exclude some pending or inactivated rows in Member scopes.
-
-## 8.11.5
-
- - Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
- - Fix member expiration date picker after update. !6184
- - Fix suggested colors options for new labels in the admin area. !6138
- - Optimize discussion notes resolving and unresolving
- - Fix GitLab import button
- - Fix confidential issues being exposed as public using gitlab.com export
- - Remove gitorious from import_sources. !6180
- - Scope webhooks/services that will run for confidential issues
- - Remove gitorious from import_sources
- - Fix confidential issues being exposed as public using gitlab.com export
- - Use oj gem for faster JSON processing
-
-## 8.11.4
-
- - Fix resolving conflicts on forks. !6082
- - Fix diff commenting on merge requests created prior to 8.10. !6029
- - Fix pipelines tab layout regression. !5952
- - Fix "Wiki" link not appearing in navigation for projects with external wiki. !6057
- - Do not enforce using hash with hidden key in CI configuration. !6079
- - Fix hover leading space bug in pipeline graph !5980
- - Fix sorting issues by "last updated" doesn't work after import from GitHub
- - GitHub importer use default project visibility for non-private projects
- - Creating an issue through our API now emails label subscribers !5720
- - Block concurrent updates for Pipeline
- - Don't create groups for unallowed users when importing projects
- - Fix issue boards leak private label names and descriptions
- - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner)
- - Remove gitorious. !5866
- - Allow compare merge request versions
-
-## 8.11.3
-
- - Allow system info page to handle case where info is unavailable
- - Label list shows all issues (opened or closed) with that label
- - Don't show resolve conflicts link before MR status is updated
- - Fix IE11 fork button bug !5982
- - Don't prevent viewing the MR when git refs for conflicts can't be found on disk
- - Fix external issue tracker "Issues" link leading to 404s
- - Don't try to show merge conflict resolution info if a merge conflict contains non-UTF-8 characters
- - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
- - Issues filters reset button
-
-## 8.11.2
-
- - Show "Create Merge Request" widget for push events to fork projects on the source project. !5978
- - Use gitlab-workhorse 0.7.11 !5983
- - Does not halt the GitHub import process when an error occurs. !5763
- - Fix file links on project page when default view is Files !5933
- - Fixed enter key in search input not working !5888
-
-## 8.11.1
-
- - Pulled due to packaging error.
-
-## 8.11.0 (2016-08-22)
-
- - Use test coverage value from the latest successful pipeline in badge. !5862
- - Add test coverage report badge. !5708
- - Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
- - Add Koding (online IDE) integration
- - Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
- - Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- - Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- - Fix adding line comments on the initial commit to a repo !5900
- - Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- - Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz)
- - Update to Ruby 2.3.1. !4948
- - Add Issues Board !5548
- - Allow resolving merge conflicts in the UI !5479
- - Improve diff performance by eliminating redundant checks for text blobs
- - Ensure that branch names containing escapable characters (e.g. %20) aren't unescaped indiscriminately. !5770 (ewiltshi)
- - Convert switch icon into icon font (ClemMakesApps)
- - API: Endpoints for enabling and disabling deploy keys
- - API: List access requests, request access, approve, and deny access requests to a project or a group. !4833
- - Use long options for curl examples in documentation !5703 (winniehell)
- - Added tooltip listing label names to the labels value in the collapsed issuable sidebar
- - Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- - GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository
- - Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- - Allow naming U2F devices !5833
- - Ignore URLs starting with // in Markdown links !5677 (winniehell)
- - Fix CI status icon link underline (ClemMakesApps)
- - The Repository class is now instrumented
- - Fix commit mention font inconsistency (ClemMakesApps)
- - Do not escape URI when extracting path !5878 (winniehell)
- - Fix filter label tooltip HTML rendering (ClemMakesApps)
- - Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- - Expand commit message width in repo view (ClemMakesApps)
- - Cache highlighted diff lines for merge requests
- - Pre-create all builds for a Pipeline when the new Pipeline is created !5295
- - Allow merge request diff notes and discussions to be explicitly marked as resolved
- - API: Add deployment endpoints
- - API: Add Play endpoint on Builds
- - Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- - Show wall clock time when showing a pipeline. !5734
- - Show member roles to all users on members page
- - Project.visible_to_user is instrumented again
- - Fix awardable button mutuality loading spinners (ClemMakesApps)
- - Sort todos by date and priority
- - Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- - Optimize maximum user access level lookup in loading of notes
- - Send notification emails to users newly mentioned in issue and MR edits !5800
- - Add "No one can push" as an option for protected branches. !5081
- - Improve performance of AutolinkFilter#text_parse by using XPath
- - Add experimental Redis Sentinel support !1877
- - Rendering of SVGs as blobs is now limited to SVGs with a size smaller or equal to 2MB
- - Fix branches page dropdown sort initial state (ClemMakesApps)
- - Environments have an url to link to
- - Various redundant database indexes have been removed
- - Update `timeago` plugin to use multiple string/locale settings
- - Remove unused images (ClemMakesApps)
- - Get issue and merge request description templates from repositories
- - Enforce 2FA restrictions on API authentication endpoints !5820
- - Limit git rev-list output count to one in forced push check
- - Show deployment status on merge requests with external URLs
- - Clean up unused routes (Josef Strzibny)
- - Fix issue on empty project to allow developers to only push to protected branches if given permission
- - API: Add enpoints for pipelines
- - Add green outline to New Branch button. !5447 (winniehell)
- - Optimize generating of cache keys for issues and notes
- - Fix repository push email formatting in Outlook
- - Improve performance of syntax highlighting Markdown code blocks
- - Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
- - Remove delay when hitting "Reply..." button on page with a lot of discussions
- - Retrieve rendered HTML from cache in one request
- - Fix renaming repository when name contains invalid chararacters under project settings
- - Upgrade Grape from 0.13.0 to 0.15.0. !4601
- - Trigram indexes for the "ci_runners" table have been removed to speed up UPDATE queries
- - Fix devise deprecation warnings.
- - Check for 2FA when using Git over HTTP and only allow PersonalAccessTokens as password in that case !5764
- - Update version_sorter and use new interface for faster tag sorting
- - Optimize checking if a user has read access to a list of issues !5370
- - Store all DB secrets in secrets.yml, under descriptive names !5274
- - Fix syntax highlighting in file editor
- - Support slash commands in issue and merge request descriptions as well as comments. !5021
- - Nokogiri's various parsing methods are now instrumented
- - Add archived badge to project list !5798
- - Add simple identifier to public SSH keys (muteor)
- - Admin page now references docs instead of a specific file !5600 (AnAverageHuman)
- - Fix filter input alignment (ClemMakesApps)
- - Include old revision in merge request update hooks (Ben Boeckel)
- - Add build event color in HipChat messages (David Eisner)
- - Make fork counter always clickable. !5463 (winniehell)
- - Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
- - Gitlab::Highlight is now instrumented
- - All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- - Allow users to import cross-repository pull requests from GitHub
- - The overhead of instrumented method calls has been reduced
- - Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- - Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- - Add pipeline events hook
- - Bump gitlab_git to speedup DiffCollection iterations
- - Rewrite description of a blocked user in admin settings. (Elias Werberich)
- - Make branches sortable without push permission !5462 (winniehell)
- - Check for Ci::Build artifacts at database level on pipeline partial
- - Convert image diff background image to CSS (ClemMakesApps)
- - Remove unnecessary index_projects_on_builds_enabled index from the projects table
- - Make "New issue" button in Issue page less obtrusive !5457 (winniehell)
- - Gitlab::Metrics.current_transaction needs to be public for RailsQueueDuration
- - Fix search for notes which belongs to deleted objects
- - Allow Akismet to be trained by submitting issues as spam or ham !5538
- - Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- - Allow branch names ending with .json for graph and network page !5579 (winniehell)
- - Add the `sprockets-es6` gem
- - Improve OAuth2 client documentation (muteor)
- - Fix diff comments inverted toggle bug (ClemMakesApps)
- - Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- - Profile requests when a header is passed
- - Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
- - Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible
- - Add commit stats in commit api. !5517 (dixpac)
- - Add CI configuration button on project page
- - Fix merge request new view not changing code view rendering style
- - edit_blob_link will use blob passed onto the options parameter
- - Make error pages responsive (Takuya Noguchi)
- - The performance of the project dropdown used for moving issues has been improved
- - Fix skip_repo parameter being ignored when destroying a namespace
- - Add all builds into stage/job dropdowns on builds page
- - Change requests_profiles resource constraint to catch virtually any file
- - Bump gitlab_git to lazy load compare commits
- - Reduce number of queries made for merge_requests/:id/diffs
- - Add the option to set the expiration date for the project membership when giving a user access to a project. !5599 (Adam Niedzielski)
- - Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- - Fix bug where destroying a namespace would not always destroy projects
- - Fix RequestProfiler::Middleware error when code is reloaded in development
- - Allow horizontal scrolling of code blocks in issue body
- - Catch what warden might throw when profiling requests to re-throw it
- - Avoid commit lookup on diff_helper passing existing local variable to the helper method
- - Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
- - Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- - Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
- - Adds support for pending invitation project members importing projects
- - Add pipeline visualization/graph on pipeline page
- - Update devise initializer to turn on changed password notification emails. !5648 (tombell)
- - Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
- - Fix importing GitLab projects with an invalid MR source project
- - Sort folders with submodules in Files view !5521
- - Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- - Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
- - Add pipelines tab to merge requests
- - Fix notification_service argument error of declined invitation emails
- - Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- - Speed up todos queries by limiting the projects set we join with
- - Ensure file editing in UI does not overwrite commited changes without warning user
- - Eliminate unneeded calls to Repository#blob_at when listing commits with no path
- - Update gitlab_git gem to 10.4.7
- - Simplify SQL queries of marking a todo as done
-
-## 8.10.13 (2016-11-02)
-
-- Removes any symlinks before importing a project export file. CVE-2016-9086
-
-## 8.10.12
-
- - Don't send Private-Token (API authentication) headers to Sentry
- - Share projects via the API only with groups the authenticated user can access
-
-## 8.10.11
-
- - Respect the fork_project permission when forking projects
- - Set a restrictive CORS policy on the API for credentialed requests
- - API: disable rails session auth for non-GET/HEAD requests
- - Escape HTML nodes in builds commands in CI linter
-
-## 8.10.10
-
- - Allow the Rails cookie to be used for API authentication.
-
-## 8.10.9
-
- - Exclude some pending or inactivated rows in Member scopes
-
-## 8.10.8
-
- - Fix information disclosure in issue boards.
- - Fix privilege escalation in project import.
-
-## 8.10.7
-
- - Upgrade Hamlit to 2.6.1. !5873
- - Upgrade Doorkeeper to 4.2.0. !5881
-
-## 8.10.6
-
- - Upgrade Rails to 4.2.7.1 for security fixes. !5781
- - Restore "Largest repository" sort option on Admin > Projects page. !5797
- - Fix privilege escalation via project export.
- - Require administrator privileges to perform a project import.
-
-## 8.10.5
-
- - Add a data migration to fix some missing timestamps in the members table. !5670
- - Revert the "Defend against 'Host' header injection" change in the source NGINX templates. !5706
- - Cache project count for 5 minutes to reduce DB load. !5746 & !5754
-
-## 8.10.4
-
- - Don't close referenced upstream issues from a forked project.
- - Fixes issue with dropdowns `enter` key not working correctly. !5544
- - Fix Import/Export project import not working in HA mode. !5618
- - Fix Import/Export error checking versions. !5638
-
-## 8.10.3
-
- - Fix Import/Export issue importing milestones and labels not associated properly. !5426
- - Fix timing problems running imports on production. !5523
- - Add a log message when a project is scheduled for destruction for debugging. !5540
- - Fix hooks missing on imported GitLab projects. !5549
- - Properly abort a merge when merge conflicts occur. !5569
- - Fix importer for GitHub Pull Requests when a branch was removed. !5573
- - Ignore invalid IPs in X-Forwarded-For when trusted proxies are configured. !5584
- - Trim extra displayed carriage returns in diffs and files with CRLFs. !5588
- - Fix label already exist error message in the right sidebar.
-
-## 8.10.2
-
- - User can now search branches by name. !5144
- - Page is now properly rendered after committing the first file and creating the first branch. !5399
- - Add branch or tag icon to ref in builds page. !5434
- - Fix backup restore. !5459
- - Use project ID in repository cache to prevent stale data from persisting across projects. !5460
- - Fix issue with autocomplete search not working with enter key. !5466
- - Add iid to MR API response. !5468
- - Disable MySQL foreign key checks before dropping all tables. !5472
- - Ensure relative paths for video are rewritten as we do for images. !5474
- - Ensure current user can retry a build before showing the 'Retry' button. !5476
- - Add ENV variable to skip repository storages validations. !5478
- - Added `*.js.es6 gitlab-language=javascript` to `.gitattributes`. !5486
- - Don't show comment button in gutter of diffs on MR discussion tab. !5493
- - Rescue Rugged::OSError (lock exists) when creating references. !5497
- - Fix expand all diffs button in compare view. !5500
- - Show release notes in tags list. !5503
- - Fix a bug where forking a project from a repository storage to another would fail. !5509
- - Fix missing schema update for `20160722221922`. !5512
- - Update `gitlab-shell` version to 3.2.1 in the 8.9->8.10 update guide. !5516
-
-## 8.10.1
-
- - Refactor repository storages documentation. !5428
- - Gracefully handle case when keep-around references are corrupted or exist already. !5430
- - Add detailed info on storage path mountpoints. !5437
- - Fix Error 500 when creating Wiki pages with hyphens or spaces. !5444
- - Fix bug where replies to commit notes displayed in the MR discussion tab wouldn't show up on the commit page. !5446
- - Ignore invalid trusted proxies in X-Forwarded-For header. !5454
- - Add links to the real markdown.md file for all GFM examples. !5458
-
-## 8.10.0 (2016-07-22)
-
- - Fix profile activity heatmap to show correct day name (eanplatter)
- - Speed up ExternalWikiHelper#get_project_wiki_path
- - Expose {should,force}_remove_source_branch (Ben Boeckel)
- - Add the functionality to be able to rename a file. !5049
- - Disable PostgreSQL statement timeout during migrations
- - Fix projects dropdown loading performance with a simplified api cal. !5113
- - Fix commit builds API, return all builds for all pipelines for given commit. !4849
- - Replace Haml with Hamlit to make view rendering faster. !3666
- - Refresh the branch cache after `git gc` runs
- - Allow to disable request access button on projects/groups
- - Refactor repository paths handling to allow multiple git mount points
- - Optimize system note visibility checking by memoizing the visible reference count. !5070
- - Add Application Setting to configure default Repository Path for new projects
- - Delete award emoji when deleting a user
- - Remove pinTo from Flash and make inline flash messages look nicer. !4854 (winniehell)
- - Add an API for downloading latest successful build from a particular branch or tag. !5347
- - Avoid data-integrity issue when cleaning up repository archive cache.
- - Add link to profile to commit avatar. !5163 (winniehell)
- - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- - Align flash messages with left side of page content. !4959 (winniehell)
- - Display tooltip for "Copy to Clipboard" button. !5164 (winniehell)
- - Use default cursor for table header of project files. !5165 (winniehell)
- - Store when and yaml variables in builds table
- - Display last commit of deleted branch in push events. !4699 (winniehell)
- - Escape file extension when parsing search results. !5141 (winniehell)
- - Add "passing with warnings" to the merge request pipeline possible statuses, this happens when builds that allow failures have failed. !5004
- - Add image border in Markdown preview. !5162 (winniehell)
- - Apply the trusted_proxies config to the rack request object for use with rack_attack
- - Added the ability to block sign ups using a domain blacklist. !5259
- - Upgrade to Rails 4.2.7. !5236
- - Extend exposed environment variables for CI builds
- - Deprecate APIs "projects/:id/keys/...". Use "projects/:id/deploy_keys/..." instead
- - Add API "deploy_keys" for admins to get all deploy keys
- - Allow to pull code with deploy key from public projects
- - Use limit parameter rather than hardcoded value in `ldap:check` rake task (Mike Ricketts)
- - Add Sidekiq queue duration to transaction metrics.
- - Add a new column `artifacts_size` to table `ci_builds`. !4964
- - Let Workhorse serve format-patch diffs
- - Display tooltip for mentioned users and groups. !5261 (winniehell)
- - Allow build email service to be tested
- - Added day name to contribution calendar tooltips
- - Refactor user authorization check for a single project to avoid querying all user projects
- - Make images fit to the size of the viewport. !4810
- - Fix check for New Branch button on Issue page. !4630 (winniehell)
- - Fix GFM autocomplete not working on wiki pages
- - Fixed enter key not triggering click on first row when searching in a dropdown
- - Updated dropdowns in issuable form to use new GitLab dropdown style
- - Make images fit to the size of the viewport !4810
- - Fix check for New Branch button on Issue page !4630 (winniehell)
- - Fix MR-auto-close text added to description. !4836
- - Support U2F devices in Firefox. !5177
- - Fix issue, preventing users w/o push access to sort tags. !5105 (redetection)
- - Add Spring EmojiOne updates.
- - Added Rake task for tracking deployments. !5320
- - Fix fetching LFS objects for private CI projects
- - Add the new 2016 Emoji! Adds 72 new emoji including bacon, facepalm, and selfie. !5237
- - Add syntax for multiline blockquote using `>>>` fence. !3954
- - Fix viewing notification settings when a project is pending deletion
- - Updated compare dropdown menus to use GL dropdown
- - Redirects back to issue after clicking login link
- - Eager load award emoji on notes
- - Allow to define manual actions/builds on Pipelines and Environments
- - Fix pagination when sorting by columns with lots of ties (like priority)
- - The Markdown reference parsers now re-use query results to prevent running the same queries multiple times. !5020
- - Updated project header design
- - Issuable collapsed assignee tooltip is now the users name
- - Fix compare view not changing code view rendering style
- - Exclude email check from the standard health check
- - Updated layout for Projects, Groups, Users on Admin area. !4424
- - Fix changing issue state columns in milestone view
- - Update health_check gem to version 2.1.0
- - Add notification settings dropdown for groups
- - Render inline diffs for multiple changed lines following eachother
- - Wildcards for protected branches. !4665
- - Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- - API: Expose `due_date` for issues (Robert Schilling)
- - API: Todos. !3188 (Robert Schilling)
- - API: Expose shared groups for projects and shared projects for groups. !5050 (Robert Schilling)
- - API: Expose `developers_can_push` and `developers_can_merge` for branches. !5208 (Robert Schilling)
- - Add "Enabled Git access protocols" to Application Settings
- - Diffs will create button/diff form on demand no on server side
- - Reduce size of HTML used by diff comment forms
- - Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
- - Fix user creation with stronger minimum password requirements. !4054 (nathan-pmt)
- - Only show New Snippet button to users that can create snippets.
- - PipelinesFinder uses git cache data
- - Track a user who created a pipeline
- - Actually render old and new sections of parallel diff next to each other
- - Throttle the update of `project.pushes_since_gc` to 1 minute.
- - Allow expanding and collapsing files in diff view. !4990
- - Collapse large diffs by default (!4990)
- - Fix mentioned users list on diff notes
- - Add support for inline videos in GitLab Flavored Markdown. !5215 (original implementation by Eric Hayes)
- - Fix creation of deployment on build that is retried, redeployed or rollback
- - Don't parse Rinku returned value to DocFragment when it didn't change the original html string.
- - Check for conflicts with existing Project's wiki path when creating a new project.
- - Show last push widget in upstream after push to fork
- - Fix stage status shown for pipelines
- - Cache todos pending/done dashboard query counts.
- - Don't instantiate a git tree on Projects show default view
- - Bump Rinku to 2.0.0
- - Remove unused front-end variable -> default_issues_tracker
- - ObjectRenderer retrieve renderer content using Rails.cache.read_multi
- - Better caching of git calls on ProjectsController#show.
- - Avoid to retrieve MR closes_issues as much as possible.
- - Hide project name in project activities. !5068 (winniehell)
- - Add API endpoint for a group issues. !4520 (mahcsig)
- - Add Bugzilla integration. !4930 (iamtjg)
- - Fix new snippet style bug (elliotec)
- - Instrument Rinku usage
- - Be explicit to define merge request discussion variables
- - Use cache for todos counter calling TodoService
- - Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
- - RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
- - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
- - Made project list visibility icon fixed width
- - Set import_url validation to be more strict
- - Memoize MR merged/closed events retrieval
- - Don't render discussion notes when requesting diff tab through AJAX
- - Add basic system information like memory and disk usage to the admin panel
- - Don't garbage collect commits that have related DB records like comments
- - Allow to setup event by channel on slack service
- - More descriptive message for git hooks and file locks
- - Aliases of award emoji should be stored as original name. !5060 (dixpac)
- - Handle custom Git hook result in GitLab UI
- - Allow to access Container Registry for Public and Internal projects
- - Allow '?', or '&' for label names
- - Support redirected blobs for Container Registry integration
- - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- - Add date when user joined the team on the member page
- - Fix 404 redirect after validation fails importing a GitLab project
- - Added setting to set new users by default as external. !4545 (Dravere)
- - Add min value for project limit field on user's form. !3622 (jastkand)
- - Reset project pushes_since_gc when we enqueue the git gc call
- - Add reminder to not paste private SSH keys. !4399 (Ingo Blechschmidt)
- - Collapsed diffs lines/size don't acumulate to overflow diffs.
- - Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
- - Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
- - Fix GitHub client requests when rate limit is disabled
- - Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
- - Redesign Builds and Pipelines pages
- - Change status color and icon for running builds
- - Fix commenting issue in side by side diff view for unchanged lines
- - Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
- - Project export filename now includes the project and namespace path
- - Fix last update timestamp on issues not preserved on gitlab.com and project imports
- - Fix issues importing projects from EE to CE
- - Fix creating group with space in group path
- - Improve cron_jobs loading error messages. !5318 / !5360
- - Prevent toggling sidebar when clipboard icon clicked
- - Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
- - Limit the number of retries on error to 3 for exporting projects
- - Allow empty repositories on project import/export
- - Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska)
- - Allow bulk (un)subscription from issues in issue index
- - Fix MR diff encoding issues exporting GitLab projects
- - Move builds settings out of project settings and rename Pipelines
- - Add builds badge to Pipelines settings page
- - Export and import avatar as part of project import/export
- - Fix migration corrupting import data for old version upgrades
- - Show tooltip on GitLab export link in new project page
- - Fix import_data wrongly saved as a result of an invalid import_url !5206
-
-## 8.9.11
-
- - Respect the fork_project permission when forking projects
- - Set a restrictive CORS policy on the API for credentialed requests
- - API: disable rails session auth for non-GET/HEAD requests
- - Escape HTML nodes in builds commands in CI linter
-
-## 8.9.10
-
- - Allow the Rails cookie to be used for API authentication.
-
-## 8.9.9
-
- - Exclude some pending or inactivated rows in Member scopes
-
-## 8.9.8
-
- - Upgrade Doorkeeper to 4.2.0. !5881
-
-## 8.9.7
-
- - Upgrade Rails to 4.2.7.1 for security fixes. !5781
- - Require administrator privileges to perform a project import.
-
-## 8.9.6
-
- - Fix importing of events under notes for GitLab projects. !5154
- - Fix log statements in import/export. !5129
- - Fix commit avatar alignment in compare view. !5128
- - Fix broken migration in MySQL. !5005
- - Overwrite Host and X-Forwarded-Host headers in NGINX !5213
- - Keeps issue number when importing from Gitlab.com
- - Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
-
-## 8.9.5
-
- - Add more debug info to import/export and memory killer. !5108
- - Fixed avatar alignment in new MR view. !5095
- - Fix diff comments not showing up in activity feed. !5069
- - Add index on both Award Emoji user and name. !5061
- - Downgrade to Redis 3.2.2 due to massive memory leak with Sidekiq. !5056
- - Re-enable import button when import process fails due to namespace already being taken. !5053
- - Fix snippets comments not displayed. !5045
- - Fix emoji paths in relative root configurations. !5027
- - Fix issues importing events in Import/Export. !4987
- - Fixed 'use shortcuts' button on docs. !4979
- - Admin should be able to turn shared runners into specific ones. !4961
- - Update RedCloth to 4.3.2 for CVE-2012-6684. !4929 (Takuya Noguchi)
- - Improve the request / withdraw access button. !4860
-
-## 8.9.4
-
- - Fix privilege escalation issue with OAuth external users.
- - Ensure references to private repos aren't shown to logged-out users.
- - Fixed search field blur not removing focus. !4704
- - Resolve "Sub nav isn't showing on file view". !4890
- - Fixes middle click and double request when navigating through the file browser. !4891
- - Fixed URL on label button when filtering. !4897
- - Fixed commit avatar alignment. !4933
- - Do not show build retry link when build is active. !4967
- - Fix restore Rake task warning message output. !4980
- - Handle external issues in IssueReferenceFilter. !4988
- - Expiry date on pinned nav cookie. !5009
- - Updated breakpoint for sidebar pinning. !5019
-
-## 8.9.3
-
- - Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !4963
- - Fix rendering of commit notes. !4953
- - Resolve "Pin should show up at 1280px min". !4947
- - Switched mobile button icons to ellipsis and angle. !4944
- - Correctly returns todo ID after creating todo. !4941
- - Better debugging for memory killer middleware. !4936
- - Remove duplicate new page btn from edit wiki. !4904
- - Use clock_gettime for all performance timestamps. !4899
- - Use memorized tags array when searching tags by name. !4859
- - Fixed avatar alignment in new MR view. !4901
- - Removed fade when filtering results. !4932
- - Fix missing avatar on system notes. !4954
- - Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973
- - Use update_columns to bypass all the dirty code on active_record. !4985
- - Fix restore Rake task warning message output !4980
-
-## 8.9.2
-
- - Fix visibility of snippets when searching.
- - Fix an information disclosure when requesting access to a group containing private projects.
- - Update omniauth-saml to 1.6.0 !4951
-
-## 8.9.1
-
- - Refactor labels documentation. !3347
- - Eager load award emoji on notes. !4628
- - Fix some CI wording in documentation. !4660
- - Document `GIT_STRATEGY` and `GIT_DEPTH`. !4720
- - Add documentation for the export & import features. !4732
- - Add some docs for Docker Registry configuration. !4738
- - Ensure we don't send the "access request declined" email to access requesters on project deletion. !4744
- - Display group/project access requesters separately in the admin area. !4798
- - Add documentation and examples for configuring cloud storage for registry images. !4812
- - Clarifies documentation about artifact expiry. !4831
- - Fix the Network graph links. !4832
- - Fix MR-auto-close text added to description. !4836
- - Add documentation for award emoji now that comments can be awarded with emojis. !4839
- - Fix typo in export failure email. !4847
- - Fix header vertical centering. !4170
- - Fix subsequent SAML sign ins. !4718
- - Set button label when picking an option from status dropdown. !4771
- - Prevent invalid URLs from raising exceptions in WikiLink Filter. !4775
- - Handle external issues in IssueReferenceFilter. !4789
- - Support for rendering/redacting multiple documents. !4828
- - Update Todos documentation and screenshots to include new functionality. !4840
- - Hide nav arrows by default. !4843
- - Added bottom padding to label color suggestion link. !4845
- - Use jQuery objects in ref dropdown. !4850
- - Fix GitLab project import issues related to notes and builds. !4855
- - Restrict header logo to 36px so it doesn't overflow. !4861
- - Fix unwanted label unassignment. !4863
- - Fix mobile Safari bug where horizontal nav arrows would flicker on scroll. !4869
- - Restore old behavior around diff notes to outdated discussions. !4870
- - Fix merge requests project settings help link anchor. !4873
- - Fix 404 when accessing pipelines as guest user on public projects. !4881
- - Remove width restriction for logo on sign-in page. !4888
- - Bump gitlab_git to 10.2.3 to fix false truncated warnings with ISO-8559 files. !4884
- - Apply selected value as label. !4886
- - Change Retry to Re-deploy on Deployments page
- - Fix temp file being deleted after the request while importing a GitLab project. !4894
- - Fix pagination when sorting by columns with lots of ties (like priority)
- - Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise.
- - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- - Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912
- - Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915
- - Remove duplicate 'New Page' button on edit wiki page
-
-## 8.9.0 (2016-06-22)
-
- - Fix group visibility form layout in application settings
- - Fix builds API response not including commit data
- - Fix error when CI job variables key specified but not defined
- - Fix pipeline status when there are no builds in pipeline
- - Fix Error 500 when using closes_issues API with an external issue tracker
- - Add more information into RSS feed for issues (Alexander Matyushentsev)
- - Bulk assign/unassign labels to issues.
- - Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
- - Show Star and Fork buttons on mobile.
- - Performance improvements on RelativeLinkFilter
- - Fix endless redirections when accessing user OAuth applications when they are disabled
- - Allow enabling wiki page events from Webhook management UI
- - Bump rouge to 1.11.0
- - Fix issue with arrow keys not working in search autocomplete dropdown
- - Fix an issue where note polling stopped working if a window was in the
- background during a refresh.
- - Pre-processing Markdown now only happens when needed
- - Make EmailsOnPushWorker use Sidekiq mailers queue
- - Redesign all Devise emails. !4297
- - Don't show 'Leave Project' to group members
- - Fix wiki page events' webhook to point to the wiki repository
- - Add a border around images to differentiate them from the background.
- - Don't show tags for revert and cherry-pick operations
- - Show image ID on registry page
- - Fix issue todo not remove when leave project !4150 (Long Nguyen)
- - Allow customisable text on the 'nearly there' page after a user signs up
- - Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
- - Fix SVG sanitizer to allow more elements
- - Allow forking projects with restricted visibility level
- - Added descriptions to notification settings dropdown
- - Improve note validation to prevent errors when creating invalid note via API
- - Reduce number of fog gem dependencies
- - Add number of merge requests for a given milestone to the milestones view.
- - Implement a fair usage of shared runners
- - Remove project notification settings associated with deleted projects
- - Fix 404 page when viewing TODOs that contain milestones or labels in different projects
- - Add a metric for the number of new Redis connections created by a transaction
- - Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
- - Redesign navigation for project pages
- - Fix images in sign-up confirmation email
- - Added shortcut 'y' for copying a files content hash URL #14470
- - Fix groups API to list only user's accessible projects
- - Fix horizontal scrollbar for long commit message.
- - GitLab Performance Monitoring now tracks the total method execution time and call count per method
- - Add Environments and Deployments
- - Redesign account and email confirmation emails
- - Don't fail builds for projects that are deleted
- - Support Docker Registry manifest v1
- - `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
- - Bump nokogiri to 1.6.8
- - Use gitlab-shell v3.0.0
- - Fixed alignment of download dropdown in merge requests
- - Upgrade to jQuery 2
- - Adds selected branch name to the dropdown toggle
- - Add API endpoint for Sidekiq Metrics !4653
- - Refactoring Award Emoji with API support for Issues and MergeRequests
- - Use Knapsack to evenly distribute tests across multiple nodes
- - Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
- - Don't allow MRs to be merged when commits were added since the last review / page load
- - Add DB index on users.state
- - Limit email on push diff size to 30 files / 150 KB
- - Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
- - Changed the Slack build message to use the singular duration if necessary (Aran Koning)
- - Fix race condition on merge when build succeeds
- - Added shortcut to focus filter search fields and added documentation #18120
- - Links from a wiki page to other wiki pages should be rewritten as expected
- - Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- - Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393
- - Fix issues filter when ordering by milestone
- - Disable SAML account unlink feature
- - Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
- - Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
- - TeamCity Service: Fix URL handling when base URL contains a path
- - Todos will display target state if issuable target is 'Closed' or 'Merged'
- - Validate only and except regexp
- - Fix bug when sorting issues by milestone due date and filtering by two or more labels
- - POST to API /projects/:id/runners/:runner_id would give 409 if the runner was already enabled for this project
- - Add support for using Yubikeys (U2F) for two-factor authentication
- - Link to blank group icon doesn't throw a 404 anymore
- - Remove 'main language' feature
- - Toggle whitespace button now available for compare branches diffs #17881
- - Pipelines can be canceled only when there are running builds
- - Allow authentication using personal access tokens
- - Use downcased path to container repository as this is expected path by Docker
- - Allow to use CI token to fetch LFS objects
- - Custom notification settings
- - Projects pending deletion will render a 404 page
- - Measure queue duration between gitlab-workhorse and Rails
- - Added Gfm autocomplete for labels
- - Added edit note 'up' shortcut documentation to the help panel and docs screenshot #18114
- - Make Omniauth providers specs to not modify global configuration
- - Remove unused JiraIssue class and replace references with ExternalIssue. !4659 (Ilan Shamir)
- - Make authentication service for Container Registry to be compatible with < Docker 1.11
- - Make it possible to lock a runner from being enabled for other projects
- - Add Application Setting to configure Container Registry token expire delay (default 5min)
- - Cache assigned issue and merge request counts in sidebar nav
- - Use Knapsack only in CI environment
- - Updated project creation page to match new UI #2542
- - Cache project build count in sidebar nav
- - Add milestone expire date to the right sidebar
- - Manually mark a issue or merge request as a todo
- - Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
- - Reduce number of queries needed to render issue labels in the sidebar
- - Improve error handling importing projects
- - Remove duplicated notification settings
- - Put project Files and Commits tabs under Code tab
- - Decouple global notification level from user model
- - Replace Colorize with Rainbow for coloring console output in Rake tasks.
- - Add workhorse controller and API helpers
- - An indicator is now displayed at the top of the comment field for confidential issues.
- - Show categorised search queries in the search autocomplete
- - RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
- - Dropdown for `.gitlab-ci.yml` templates
- - Improve issuables APIs performance when accessing notes !4471
- - Add sorting dropdown to tags page !4423
- - External links now open in a new tab
- - Prevent default actions of disabled buttons and links
- - Markdown editor now correctly resets the input value on edit cancellation !4175
- - Toggling a task list item in a issue/mr description does not creates a Todo for mentions
- - Improved UX of date pickers on issue & milestone forms
- - Cache on the database if a project has an active external issue tracker.
- - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
- - GitLab project import and export functionality
- - All classes in the Banzai::ReferenceParser namespace are now instrumented
- - Remove deprecated issues_tracker and issues_tracker_id from project model
- - Allow users to create confidential issues in private projects
- - Measure CPU time for instrumented methods
- - Instrument private methods and private instance methods by default instead just public methods
- - Only show notes through JSON on confidential issues that the user has access to
- - Updated the allocations Gem to version 1.0.5
- - The background sampler now ignores classes without names
- - Update design for `Close` buttons
- - New custom icons for navigation
- - Horizontally scrolling navigation on project, group, and profile settings pages
- - Hide global side navigation by default
- - Fix project Star/Unstar project button tooltip
- - Remove tanuki logo from side navigation; center on top nav
- - Include user relationships when retrieving award_emoji
- - Various associations are now eager loaded when parsing issue references to reduce the number of queries executed
- - Set inverse_of for Project/Service association to reduce the number of queries
- - Update tanuki logo highlight/loading colors
- - Remove explicit Gitlab::Metrics.action assignments, are already automatic.
- - Use Git cached counters for branches and tags on project page
- - Cache participable participants in an instance variable.
- - Filter parameters for request_uri value on instrumented transactions.
- - Remove duplicated keys add UNIQUE index to keys fingerprint column
- - ExtractsPath get ref_names from repository cache, if not there access git.
- - Show a flash warning about the error detail of XHR requests which failed with status code 404 and 500
- - Cache user todo counts from TodoService
- - Ensure Todos counters doesn't count Todos for projects pending delete
- - Add left/right arrows horizontal navigation
- - Add tooltip to pin/unpin navbar
- - Add new sub nav style to Wiki and Graphs sub navigation
-
-## 8.8.9
-
- - Upgrade Doorkeeper to 4.2.0. !5881
-
-## 8.8.8
-
- - Upgrade Rails to 4.2.7.1 for security fixes. !5781
-
-## 8.8.7
-
- - Fix privilege escalation issue with OAuth external users.
- - Ensure references to private repos aren't shown to logged-out users.
-
-## 8.8.6
-
- - Fix visibility of snippets when searching.
- - Update omniauth-saml to 1.6.0 !4951
-
-## 8.8.5
-
- - Import GitHub repositories respecting the API rate limit !4166
- - Fix todos page throwing errors when you have a project pending deletion !4300
- - Disable Webhooks before proceeding with the GitHub import !4470
- - Fix importer for GitHub comments on diff !4488
- - Adjust the SAML control flow to allow LDAP identities to be added to an existing SAML user !4498
- - Fix incremental trace upload API when using multi-byte UTF-8 chars in trace !4541
- - Prevent unauthorized access for projects build traces
- - Forbid scripting for wiki files
- - Only show notes through JSON on confidential issues that the user has access to
- - Banzai::Filter::UploadLinkFilter use XPath instead CSS expressions
- - Banzai::Filter::ExternalLinkFilter use XPath instead CSS expressions
-
-## 8.8.4
-
- - Fix LDAP-based login for users with 2FA enabled. !4493
- - Added descriptions to notification settings dropdown
- - Due date can be removed from milestones
-
-## 8.8.3
-
- - Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
- - Fixed JS error when trying to remove discussion form. !4303
- - Fixed issue with button color when no CI enabled. !4287
- - Fixed potential issue with 2 CI status polling events happening. !3869
- - Improve design of Pipeline view. !4230
- - Fix gitlab importer failing to import new projects due to missing credentials. !4301
- - Fix import URL migration not rescuing with the correct Error. !4321
- - Fix health check access token changing due to old application settings being used. !4332
- - Make authentication service for Container Registry to be compatible with Docker versions before 1.11. !4363
- - Add Application Setting to configure Container Registry token expire delay (default 5 min). !4364
- - Pass the "Remember me" value to the 2FA token form. !4369
- - Fix incorrect links on pipeline page when merge request created from fork. !4376
- - Use downcased path to container repository as this is expected path by Docker. !4420
- - Fix wiki project clone address error (chujinjin). !4429
- - Fix serious performance bug with rendering Markdown with InlineDiffFilter. !4392
- - Fix missing number on generated ordered list element. !4437
- - Prevent disclosure of notes on confidential issues in search results.
-
-## 8.8.2
-
- - Added remove due date button. !4209
- - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242
- - Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245
- - Fix table UI on CI builds page. !4249
- - Fix backups if registry is disabled. !4263
- - Fixed issue with merge button color. !4211
- - Fixed issue with enter key selecting wrong option in dropdown. !4210
- - When creating a .gitignore file a dropdown with templates will be provided. !4075
- - Fix concurrent request when updating build log in browser. !4183
-
-## 8.8.1
-
- - Add documentation for the "Health Check" feature
- - Allow anonymous users to access a public project's pipelines !4233
- - Fix MySQL compatibility in zero downtime migrations helpers
- - Fix the CI login to Container Registry (the gitlab-ci-token user)
-
-## 8.8.0 (2016-05-22)
-
- - Implement GFM references for milestones (Alejandro Rodríguez)
- - Snippets tab under user profile. !4001 (Long Nguyen)
- - Fix error when using link to uploads in global snippets
- - Fix Error 500 when attempting to retrieve project license when HEAD points to non-existent ref
- - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
- - Use a case-insensitive comparison in sanitizing URI schemes
- - Toggle sign-up confirmation emails in application settings
- - Make it possible to prevent tagged runner from picking untagged jobs
- - Added `InlineDiffFilter` to the markdown parser. (Adam Butler)
- - Added inline diff styling for `change_title` system notes. (Adam Butler)
- - Project#open_branches has been cleaned up and no longer loads entire records into memory.
- - Escape HTML in commit titles in system note messages
- - Improve design of Pipeline View
- - Fix scope used when accessing container registry
- - Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios
- - Improve multiple branch push performance by memoizing permission checking
- - Log to application.log when an admin starts and stops impersonating a user
- - Changing the confidentiality of an issue now creates a new system note (Alex Moore-Niemi)
- - Updated gitlab_git to 10.1.0
- - GitAccess#protected_tag? no longer loads all tags just to check if a single one exists
- - Reduce delay in destroying a project from 1-minute to immediately
- - Make build status canceled if any of the jobs was canceled and none failed
- - Upgrade Sidekiq to 4.1.2
- - Added /health_check endpoint for checking service status
- - Make 'upcoming' filter for milestones work better across projects
- - Sanitize repo paths in new project error message
- - Bump mail_room to 0.7.0 to fix stuck IDLE connections
- - Remove future dates from contribution calendar graph.
- - Support e-mail notifications for comments on project snippets
- - Fix API leak of notes of unauthorized issues, snippets and merge requests
- - Use ActionDispatch Remote IP for Akismet checking
- - Fix error when visiting commit builds page before build was updated
- - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
- - Update SVG sanitizer to conform to SVG 1.1
- - Speed up push emails with multiple recipients by only generating the email once
- - Updated search UI
- - Added authentication service for Container Registry
- - Display informative message when new milestone is created
- - Sanitize milestones and labels titles
- - Support multi-line tag messages. !3833 (Calin Seciu)
- - Force users to reset their password after an admin changes it
- - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
- - Added button to toggle whitespaces changes on diff view
- - Backport GitHub Enterprise import support from EE
- - Create tags using Rugged for performance reasons. !3745
- - Allow guests to set notification level in projects
- - API: Expose Issue#user_notes_count. !3126 (Anton Popov)
- - Don't show forks button when user can't view forks
- - Fix atom feed links and rendering
- - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718
- - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes)
- - Add eager load paths to help prevent dependency load issues in Sidekiq workers. !3724
- - Added multiple colors for labels in dropdowns when dups happen.
- - Show commits in the same order as `git log`
- - Improve description for the Two-factor Authentication sign-in screen. (Connor Shea)
- - API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
- - Expire repository exists? and has_visible_content? caches after a push if necessary
- - Fix unintentional filtering bug in Issue/MR sorted by milestone due (Takuya Noguchi)
- - Fix adding a todo for private group members (Ahmad Sherif)
- - Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
- - Total method execution timings are no longer tracked
- - Allow Admins to remove the Login with buttons for OAuth services and still be able to import !4034. (Andrei Gliga)
- - Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif)
- - Hide left sidebar on phone screens to give more space for content
- - Redesign navigation for profile and group pages
- - Add counter metrics for rails cache
- - Import pull requests from GitHub where the source or target branches were removed
- - All Grape API helpers are now instrumented
- - Improve Issue formatting for the Slack Service (Jeroen van Baarsen)
- - Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine)
- - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs)
- - When creating a .gitignore file a dropdown with templates will be provided
- - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
-
-## 8.7.9
-
- - Fix privilege escalation issue with OAuth external users.
- - Ensure references to private repos aren't shown to logged-out users.
-
-## 8.7.8
-
- - Fix visibility of snippets when searching.
- - Update omniauth-saml to 1.6.0 !4951
-
-## 8.7.7
-
- - Fix import by `Any Git URL` broken if the URL contains a space
- - Prevent unauthorized access to other projects build traces
- - Forbid scripting for wiki files
- - Only show notes through JSON on confidential issues that the user has access to
-
-## 8.7.6
-
- - Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
- - Fix import from GitLab.com to a private instance failure. !4181
- - Fix external imports not finding the import data. !4106
- - Fix notification delay when changing status of an issue
- - Bump Workhorse to 0.7.5 so it can serve raw diffs
-
-## 8.7.5
-
- - Fix relative links in wiki pages. !4050
- - Fix always showing build notification message when switching between merge requests !4086
- - Fix an issue when filtering merge requests with more than one label. !3886
- - Fix short note for the default scope on build page (Takuya Noguchi)
-
-## 8.7.4
-
- - Links for Redmine issue references are generated correctly again !4048 (Benedikt Huss)
- - Fix setting trusted proxies !3970
- - Fix BitBucket importer bug when throwing exceptions !3941
- - Use sign out path only if not empty !3989
- - Running rake gitlab:db:drop_tables now drops tables with cascade !4020
- - Running rake gitlab:db:drop_tables uses "IF EXISTS" as a precaution !4100
- - Use a case-insensitive comparison in sanitizing URI schemes
-
-## 8.7.3
-
- - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented
- - Merge request widget displays TeamCity build state and code coverage correctly again.
- - Fix the line code when importing PR review comments from GitHub. !4010
- - Wikis are now initialized on legacy projects when checking repositories
- - Remove animate.css in favor of a smaller subset of animations. !3937 (Connor Shea)
-
-## 8.7.2
-
- - The "New Branch" button is now loaded asynchronously
- - Fix error 500 when trying to create a wiki page
- - Updated spacing between notification label and button
- - Label titles in filters are now escaped properly
-
-## 8.7.1
-
- - Throttle the update of `project.last_activity_at` to 1 minute. !3848
- - Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849
- - Fix license detection to detect all license files, not only known licenses. !3878
- - Use the `can?` helper instead of `current_user.can?`. !3882
- - Prevent users from deleting Webhooks via API they do not own
- - Fix Error 500 due to stale cache when projects are renamed or transferred
- - Update width of search box to fix Safari bug. !3900 (Jedidiah)
- - Use the `can?` helper instead of `current_user.can?`
-
-## 8.7.0 (2016-04-22)
-
- - Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
- - Fix vulnerability that made it possible to gain access to private labels and milestones
- - The number of InfluxDB points stored per UDP packet can now be configured
- - Fix error when cross-project label reference used with non-existent project
- - Transactions for /internal/allowed now have an "action" tag set
- - Method instrumentation now uses Module#prepend instead of aliasing methods
- - Repository.clean_old_archives is now instrumented
- - Add support for environment variables on a job level in CI configuration file
- - SQL query counts are now tracked per transaction
- - The Projects::HousekeepingService class has extra instrumentation
- - All service classes (those residing in app/services) are now instrumented
- - Developers can now add custom tags to transactions
- - Loading of an issue's referenced merge requests and related branches is now done asynchronously
- - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
- - Add support to cherry-pick any commit into any branch in the web interface (Minqi Pan)
- - Project switcher uses new dropdown styling
- - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
- - Restrict user profiles when public visibility level is restricted.
- - Add ability set due date to issues, sort and filter issues by due date (Mehmet Beydogan)
- - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- - Add setting for customizing the list of trusted proxies !3524
- - Allow projects to be transfered to a lower visibility level group
- - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
- - Improved Markdown rendering performance !3389
- - Make shared runners text in box configurable
- - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
- - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
- - Expose project badges in project settings
- - Make /profile/keys/new redirect to /profile/keys for back-compat. !3717
- - Preserve time notes/comments have been updated at when moving issue
- - Make HTTP(s) label consistent on clone bar (Stan Hu)
- - Add support for `after_script`, requires Runner 1.2 (Kamil Trzciński)
- - Expose label description in API (Mariusz Jachimowicz)
- - API: Ability to update a group (Robert Schilling)
- - API: Ability to move issues (Robert Schilling)
- - Fix Error 500 after renaming a project path (Stan Hu)
- - Fix a bug whith trailing slash in teamcity_url (Charles May)
- - Allow back dating on issues when created or updated through the API
- - Allow back dating on issue notes when created through the API
- - Propose license template when creating a new LICENSE file
- - API: Expose /licenses and /licenses/:key
- - Fix avatar stretching by providing a cropping feature
- - API: Expose `subscribed` for issues and merge requests (Robert Schilling)
- - Allow SAML to handle external users based on user's information !3530
- - Allow Omniauth providers to be marked as `external` !3657
- - Add endpoints to archive or unarchive a project !3372
- - Fix a bug whith trailing slash in bamboo_url
- - Add links to CI setup documentation from project settings and builds pages
- - Display project members page to all members
- - Handle nil descriptions in Slack issue messages (Stan Hu)
- - Add automated repository integrity checks (OFF by default)
- - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
- - API: Ability to star and unstar a project (Robert Schilling)
- - Add default scope to projects to exclude projects pending deletion
- - Allow to close merge requests which source projects(forks) are deleted.
- - Ensure empty recipients are rejected in BuildsEmailService
- - Use rugged to change HEAD in Project#change_head (P.S.V.R)
- - API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
- - API: Fix milestone filtering by `iid` (Robert Schilling)
- - Make before_script and after_script overridable on per-job (Kamil Trzciński)
- - API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
- - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- - Better errors handling when creating milestones inside groups
- - Fix high CPU usage when PostReceive receives refs/merge-requests/<id>
- - Hide `Create a group` help block when creating a new project in a group
- - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- - Allow issues and merge requests to be assigned to the author !2765
- - Make Ci::Commit to group only similar builds and make it stateful (ref, tag)
- - Gracefully handle notes on deleted commits in merge requests (Stan Hu)
- - Decouple membership and notifications
- - Fix creation of merge requests for orphaned branches (Stan Hu)
- - API: Ability to retrieve a single tag (Robert Schilling)
- - While signing up, don't persist the user password across form redisplays
- - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
- - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- - Fix admin/projects when using visibility levels on search (PotHix)
- - Build status notifications
- - Update email confirmation interface
- - API: Expose user location (Robert Schilling)
- - API: Do not leak group existence via return code (Robert Schilling)
- - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
- - Update number of Todos in the sidebar when it's marked as "Done". !3600
- - Sanitize branch names created for confidential issues
- - API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
- - API: User can leave a project through the API when not master or owner. !3613
- - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
- - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
- - Improved markdown forms
- - Diff design updates (colors, button styles, etc)
- - Copying and pasting a diff no longer pastes the line numbers or +/-
- - Add null check to formData when updating profile content to fix Firefox bug
- - Disable spellcheck and autocorrect for username field in admin page
- - Delete tags using Rugged for performance reasons (Robert Schilling)
- - Add Slack notifications when Wiki is edited (Sebastian Klier)
- - Diffs load at the correct point when linking from from number
- - Selected diff rows highlight
- - Fix emoji categories in the emoji picker
- - API: Properly display annotated tags for GET /projects/:id/repository/tags (Robert Schilling)
- - Add encrypted credentials for imported projects and migrate old ones
- - Properly format all merge request references with ! rather than # !3740 (Ben Bodenmiller)
- - Author and participants are displayed first on users autocompletion
- - Show number sign on external issue reference text (Florent Baldino)
- - Updated print style for issues
- - Use GitHub Issue/PR number as iid to keep references
- - Import GitHub labels
- - Add option to filter by "Owned projects" on dashboard page
- - Import GitHub milestones
- - Execute system web hooks on push to the project
- - Allow enable/disable push events for system hooks
- - Fix GitHub project's link in the import page when provider has a custom URL
- - Add RAW build trace output and button on build page
- - Add incremental build trace update into CI API
-
-## 8.6.9
-
- - Prevent unauthorized access to other projects build traces
- - Forbid scripting for wiki files
- - Only show notes through JSON on confidential issues that the user has access to
-
-## 8.6.8
-
- - Prevent privilege escalation via "impersonate" feature
- - Prevent privilege escalation via notes API
- - Prevent privilege escalation via project webhook API
- - Prevent XSS via Git branch and tag names
- - Prevent XSS via custom issue tracker URL
- - Prevent XSS via `window.opener`
- - Prevent XSS via label drop-down
- - Prevent information disclosure via milestone API
- - Prevent information disclosure via snippet API
- - Prevent information disclosure via project labels
- - Prevent information disclosure via new merge request page
-
-## 8.6.7
-
- - Fix persistent XSS vulnerability in `commit_person_link` helper
- - Fix persistent XSS vulnerability in Label and Milestone dropdowns
- - Fix vulnerability that made it possible to enumerate private projects belonging to group
-
-## 8.6.6
-
- - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
- - Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
- - Fix revoking of authorized OAuth applications (Connor Shea). !3690
- - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
- - Issuable header is consistent between issues and merge requests
- - Improved spacing in issuable header on mobile
-
-## 8.6.5
-
- - Fix importing from GitHub Enterprise. !3529
- - Perform the language detection after updating merge requests in `GitPushService`, leading to faster visual feedback for the end-user. !3533
- - Check permissions when user attempts to import members from another project. !3535
- - Only update repository language if it is not set to improve performance. !3556
- - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu). !3583
- - Unblock user when active_directory is disabled and it can be found !3550
- - Fix a 2FA authentication spoofing vulnerability.
-
-## 8.6.4
-
- - Don't attempt to fetch any tags from a forked repo (Stan Hu)
- - Redesign the Labels page
-
-## 8.6.3
-
- - Mentions on confidential issues doesn't create todos for non-members. !3374
- - Destroy related todos when an Issue/MR is deleted. !3376
- - Fix error 500 when target is nil on todo list. !3376
- - Fix copying uploads when moving issue to another project. !3382
- - Ensuring Merge Request API returns boolean values for work_in_progress (Abhi Rao). !3432
- - Fix raw/rendered diff producing different results on merge requests. !3450
- - Fix commit comment alignment (Stan Hu). !3466
- - Fix Error 500 when searching for a comment in a project snippet. !3468
- - Allow temporary email as notification email. !3477
- - Fix issue with dropdowns not selecting values. !3478
- - Update gitlab-shell version and doc to 2.6.12. gitlab-org/gitlab-ee!280
-
-## 8.6.2
-
- - Fix dropdown alignment. !3298
- - Fix issuable sidebar overlaps on tablet. !3299
- - Make dropdowns pixel perfect. !3337
- - Fix order of steps to prevent PostgreSQL errors when running migration. !3355
- - Fix bold text in issuable sidebar. !3358
- - Fix error with anonymous token in applications settings. !3362
- - Fix the milestone 'upcoming' filter. !3364 + !3368
- - Fix comments on confidential issues showing up in activity feed to non-members. !3375
- - Fix `NoMethodError` when visiting CI root path at `/ci`. !3377
- - Add a tooltip to new branch button in issue page. !3380
- - Fix an issue hiding the password form when signed-in with a linked account. !3381
- - Add links to CI setup documentation from project settings and builds pages. !3384
- - Fix an issue with width of project select dropdown. !3386
- - Remove redundant `require`s from Banzai files. !3391
- - Fix error 500 with cancel button on issuable edit form. !3392 + !3417
- - Fix background when editing a highlighted note. !3423
- - Remove tabstop from the WIP toggle links. !3426
- - Ensure private project snippets are not viewable by unauthorized people.
- - Gracefully handle notes on deleted commits in merge requests (Stan Hu). !3402
- - Fixed issue with notification settings not saving. !3452
-
-## 8.6.1
-
- - Add option to reload the schema before restoring a database backup. !2807
- - Display navigation controls on mobile. !3214
- - Fixed bug where participants would not work correctly on merge requests. !3329
- - Fix sorting issues by votes on the groups issues page results in SQL errors. !3333
- - Restrict notifications for confidential issues. !3334
- - Do not allow to move issue if it has not been persisted. !3340
- - Add a confirmation step before deleting an issuable. !3341
- - Fixes issue with signin button overflowing on mobile. !3342
- - Auto collapses the navigation sidebar when resizing. !3343
- - Fix build dependencies, when the dependency is a string. !3344
- - Shows error messages when trying to create label in dropdown menu. !3345
- - Fixes issue with assign milestone not loading milestone list. !3346
- - Fix an issue causing the Dashboard/Milestones page to be blank. !3348
-
-## 8.6.0 (2016-03-22)
-
- - Add ability to move issue to another project
- - Prevent tokens in the import URL to be showed by the UI
- - Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
- - Add confidential issues
- - Bump gitlab_git to 9.0.3 (Stan Hu)
- - Fix diff image view modes (2-up, swipe, onion skin) not working (Stan Hu)
- - Support Golang subpackage fetching (Stan Hu)
- - Bump Capybara gem to 2.6.2 (Stan Hu)
- - New branch button appears on issues where applicable
- - Contributions to forked projects are included in calendar
- - Improve the formatting for the user page bio (Connor Shea)
- - Easily (un)mark merge request as WIP using link
- - Use specialized system notes when MR is (un)marked as WIP
- - Removed the default password from the initial admin account created during
- setup. A password can be provided during setup (see installation docs), or
- GitLab will ask the user to create a new one upon first visit.
- - Fix issue when pushing to projects ending in .wiki
- - Properly display YAML front matter in Markdown
- - Add support for wiki with UTF-8 page names (Hiroyuki Sato)
- - Fix wiki search results point to raw source (Hiroyuki Sato)
- - Don't load all of GitLab in mail_room
- - Add information about `image` and `services` field at `job` level in the `.gitlab-ci.yml` documentation (Pat Turner)
- - HTTP error pages work independently from location and config (Artem Sidorenko)
- - Update `omniauth-saml` to 1.5.0 to allow for custom response attributes to be set
- - Memoize @group in Admin::GroupsController (Yatish Mehta)
- - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie)
- - Added omniauth-auth0 Gem (Daniel Carraro)
- - Add label description in tooltip to labels in issue index and sidebar
- - Strip leading and trailing spaces in URL validator (evuez)
- - Add "last_sign_in_at" and "confirmed_at" to GET /users/* API endpoints for admins (evuez)
- - Return empty array instead of 404 when commit has no statuses in commit status API
- - Decrease the font size and the padding of the `.anchor` icons used in the README (Roberto Dip)
- - Rewrite logo to simplify SVG code (Sean Lang)
- - Allow to use YAML anchors when parsing the `.gitlab-ci.yml` (Pascal Bach)
- - Ignore jobs that start with `.` (hidden jobs)
- - Hide builds from project's settings when the feature is disabled
- - Allow to pass name of created artifacts archive in `.gitlab-ci.yml`
- - Refactor and greatly improve search performance
- - Add support for cross-project label references
- - Ensure "new SSH key" email do not ends up as dead Sidekiq jobs
- - Update documentation to reflect Guest role not being enforced on internal projects
- - Allow search for logged out users
- - Allow to define on which builds the current one depends on
- - Allow user subscription to a label: get notified for issues/merge requests related to that label (Timothy Andrew)
- - Fix bug where Bitbucket `closed` issues were imported as `opened` (Iuri de Silvio)
- - Don't show Issues/MRs from archived projects in Groups view
- - Fix wrong "iid of max iid" in Issuable sidebar for some merged MRs
- - Fix empty source_sha on Merge Request when there is no diff (Pierre de La Morinerie)
- - Increase the notes polling timeout over time (Roberto Dip)
- - Add shortcut to toggle markdown preview (Florent Baldino)
- - Show labels in dashboard and group milestone views
- - Fix an issue when the target branch of a MR had been deleted
- - Add main language of a project in the list of projects (Tiago Botelho)
- - Add #upcoming filter to Milestone filter (Tiago Botelho)
- - Add ability to show archived projects on dashboard, explore and group pages
- - Remove fork link closes all merge requests opened on source project (Florent Baldino)
- - Move group activity to separate page
- - Create external users which are excluded of internal and private projects unless access was explicitly granted
- - Continue parameters are checked to ensure redirection goes to the same instance
- - User deletion is now done in the background so the request can not time out
- - Canceled builds are now ignored in compound build status if marked as `allowed to fail`
- - Trigger a todo for mentions on commits page
- - Let project owners and admins soft delete issues and merge requests
-
-## 8.5.13
-
- - Prevent unauthorized access to other projects build traces
- - Forbid scripting for wiki files
-
-## 8.5.12
-
- - Prevent privilege escalation via "impersonate" feature
- - Prevent privilege escalation via notes API
- - Prevent privilege escalation via project webhook API
- - Prevent XSS via Git branch and tag names
- - Prevent XSS via custom issue tracker URL
- - Prevent XSS via `window.opener`
- - Prevent information disclosure via snippet API
- - Prevent information disclosure via project labels
- - Prevent information disclosure via new merge request page
-
-## 8.5.11
-
- - Fix persistent XSS vulnerability in `commit_person_link` helper
-
-## 8.5.10
-
- - Fix a 2FA authentication spoofing vulnerability.
-
-## 8.5.9
-
- - Don't attempt to fetch any tags from a forked repo (Stan Hu).
-
-## 8.5.8
-
- - Bump Git version requirement to 2.7.4
-
-## 8.5.7
-
- - Bump Git version requirement to 2.7.3
-
-## 8.5.6
-
- - Obtain a lease before querying LDAP
-
-## 8.5.5
-
- - Ensure removing a project removes associated Todo entries
- - Prevent a 500 error in Todos when author was removed
- - Fix pagination for filtered dashboard and explore pages
- - Fix "Show all" link behavior
-
-## 8.5.4
-
- - Do not cache requests for badges (including builds badge)
-
-## 8.5.3
-
- - Flush repository caches before renaming projects
- - Sort starred projects on dashboard based on last activity by default
- - Show commit message in JIRA mention comment
- - Makes issue page and merge request page usable on mobile browsers.
- - Improved UI for profile settings
-
-## 8.5.2
-
- - Fix sidebar overlapping content when screen width was below 1200px
- - Don't repeat labels listed on Labels tab
- - Bring the "branded appearance" feature from EE to CE
- - Fix error 500 when commenting on a commit
- - Show days remaining instead of elapsed time for Milestone
- - Fix broken icons on installations with relative URL (Artem Sidorenko)
- - Fix issue where tag list wasn't refreshed after deleting a tag
- - Fix import from gitlab.com (KazSawada)
- - Improve implementation to check read access to forks and add pagination
- - Don't show any "2FA required" message if it's not actually required
- - Fix help keyboard shortcut on relative URL setups (Artem Sidorenko)
- - Update Rails to 4.2.5.2
- - Fix permissions for deprecated CI build status badge
- - Don't show "Welcome to GitLab" when the search didn't return any projects
- - Add Todos documentation
-
-## 8.5.1
-
- - Fix group projects styles
- - Show Crowd login tab when sign in is disabled and Crowd is enabled (Peter Hudec)
- - Fix a set of small UI glitches in project, profile, and wiki pages
- - Restrict permissions on public/uploads
- - Fix the merge request side-by-side view after loading diff results
- - Fix the look of tooltip for the "Revert" button
- - Add when the Builds & Runners API changes got introduced
- - Fix error 500 on some merged merge requests
- - Fix an issue causing the content of the issuable sidebar to disappear
- - Fix error 500 when trying to mark an already done todo as "done"
- - Fix an issue where MRs weren't sortable
- - Issues can now be dragged & dropped into empty milestone lists. This is also
- possible with MRs
- - Changed padding & background color for highlighted notes
- - Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu)
- - Update sentry-raven gem to 0.15.6
- - Add build coverage in project's builds page (Steffen Köhler)
- - Changed # to ! for merge requests in activity view
-
-## 8.5.0 (2016-02-22)
-
- - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
- - Cache various Repository methods to improve performance
- - Fix duplicated branch creation/deletion Webhooks/service notifications when using Web UI (Stan Hu)
- - Ensure rake tasks that don't need a DB connection can be run without one
- - Update New Relic gem to 3.14.1.311 (Stan Hu)
- - Add "visibility" flag to GET /projects api endpoint
- - Add an option to supply root email through an environmental variable (Koichiro Mikami)
- - Ignore binary files in code search to prevent Error 500 (Stan Hu)
- - Render sanitized SVG images (Stan Hu)
- - Support download access by PRIVATE-TOKEN header (Stan Hu)
- - Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
- - Add option to include the sender name in body of Notify email (Jason Lee)
- - New UI for pagination
- - Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
- set it up
- - API: Added "merge_requests/:merge_request_id/closes_issues" (Gal Schlezinger)
- - Fix diff comments loaded by AJAX to load comment with diff in discussion tab
- - Fix relative links in other markup formats (Ben Boeckel)
- - Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
- - Fix label links for a merge request pointing to issues list
- - Don't vendor minified JS
- - Increase project import timeout to 15 minutes
- - Be more permissive with email address validation: it only has to contain a single '@'
- - Display 404 error on group not found
- - Track project import failure
- - Support Two-factor Authentication for LDAP users
- - Display database type and version in Administration dashboard
- - Allow limited Markdown in Broadcast Messages
- - Fix visibility level text in admin area (Zeger-Jan van de Weg)
- - Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
- - Update the ExternalIssue regex pattern (Blake Hitchcock)
- - Remember user's inline/side-by-side diff view preference in a cookie (Kirill Katsnelson)
- - Optimized performance of finding issues to be closed by a merge request
- - Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`, `path_with_namespace`
- and `default_branch` in `project` in push, issue, merge-request and note webhooks data (Kirill Zaitsev)
- - Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in favor of `git_http_url`
- in `project` for push, issue, merge-request and note webhooks data (Kirill Zaitsev)
- - Deprecate the `repository` key in push, issue, merge-request and note webhooks data, use `project` instead (Kirill Zaitsev)
- - API: Expose MergeRequest#merge_status (Andrei Dziahel)
- - Revert "Add IP check against DNSBLs at account sign-up"
- - Actually use the `skip_merges` option in Repository#commits (Tony Chu)
- - Fix API to keep request parameters in Link header (Michael Potthoff)
- - Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
- - Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
- - Prevent parse error when name of project ends with .atom and prevent path issues
- - Discover branches for commit statuses ref-less when doing merge when succeeded
- - Mark inline difference between old and new paths when a file is renamed
- - Support Akismet spam checking for creation of issues via API (Stan Hu)
- - API: Allow to set or update a merge-request's milestone (Kirill Skachkov)
- - Improve UI consistency between projects and groups lists
- - Add sort dropdown to dashboard projects page
- - Fixed logo animation on Safari (Roman Rott)
- - Fix Merge When Succeeded when multiple stages
- - Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
- - In seach autocomplete show only groups and projects you are member of
- - Don't process cross-reference notes from forks
- - Fix: init.d script not working on OS X
- - Faster snippet search
- - Added API to download build artifacts
- - Title for milestones should be unique (Zeger-Jan van de Weg)
- - Validate correctness of maximum attachment size application setting
- - Replaces "Create merge request" link with one to the "Merge Request" when one exists
- - Fix CI builds badge, add a new link to builds badge, deprecate the old one
- - Fix broken link to project in build notification emails
- - Ability to see and sort on vote count from Issues and MR lists
- - Fix builds scheduler when first build in stage was allowed to fail
- - User project limit is reached notice is hidden if the projects limit is zero
- - Add API support for managing runners and project's runners
- - Allow SAML users to login with no previous account without having to allow
- all Omniauth providers to do so.
- - Allow existing users to auto link their SAML credentials by logging in via SAML
- - Make it possible to erase a build (trace, artifacts) using UI and API
- - Ability to revert changes from a Merge Request or Commit
- - Emoji comment on diffs are not award emoji
- - Add label description (Nuttanart Pornprasitsakul)
- - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- - Add Todos
-
-## 8.4.11
-
- - Prevent unauthorized access to other projects build traces
- - Forbid scripting for wiki files
-
-## 8.4.10
-
- - Prevent privilege escalation via "impersonate" feature
- - Prevent privilege escalation via notes API
- - Prevent privilege escalation via project webhook API
- - Prevent XSS via Git branch and tag names
- - Prevent XSS via custom issue tracker URL
- - Prevent XSS via `window.opener`
- - Prevent information disclosure via snippet API
- - Prevent information disclosure via project labels
- - Prevent information disclosure via new merge request page
-
-## 8.4.9
-
- - Fix persistent XSS vulnerability in `commit_person_link` helper
-
-## 8.4.8
-
- - Fix a 2FA authentication spoofing vulnerability.
-
-## 8.4.7
-
- - Don't attempt to fetch any tags from a forked repo (Stan Hu).
-
-## 8.4.6
-
- - Bump Git version requirement to 2.7.4
-
-## 8.4.5
-
- - No CE-specific changes
-
-## 8.4.4
-
- - Update omniauth-saml gem to 1.4.2
- - Prevent long-running backup tasks from timing out the database connection
- - Add a Project setting to allow guests to view build logs (defaults to true)
- - Sort project milestones by due date including issue editor (Oliver Rogers / Orih)
-
-## 8.4.3
-
- - Increase lfs_objects size column to 8-byte integer to allow files larger
- than 2.1GB
- - Correctly highlight MR diff when MR has merge conflicts
- - Fix highlighting in blame view
- - Update sentry-raven gem to prevent "Not a git repository" console output
- when running certain commands
- - Add instrumentation to additional Gitlab::Git and Rugged methods for
- performance monitoring
- - Allow autosize textareas to also be manually resized
-
-## 8.4.2
-
- - Bump required gitlab-workhorse version to bring in a fix for missing
- artifacts in the build artifacts browser
- - Get rid of those ugly borders on the file tree view
- - Fix updating the runner information when asking for builds
- - Bump gitlab_git version to 7.2.24 in order to bring in a performance
- improvement when checking if a repository was empty
- - Add instrumentation for Gitlab::Git::Repository instance methods so we can
- track them in Performance Monitoring.
- - Increase contrast between highlighted code comments and inline diff marker
- - Fix method undefined when using external commit status in builds
- - Fix highlighting in blame view.
-
-## 8.4.1
-
- - Apply security updates for Rails (4.2.5.1), rails-html-sanitizer (1.0.3),
- and Nokogiri (1.6.7.2)
- - Fix redirect loop during import
- - Fix diff highlighting for all syntax themes
- - Delete project and associations in a background worker
-
-## 8.4.0 (2016-01-22)
-
- - Allow LDAP users to change their email if it was not set by the LDAP server
- - Ensure Gravatar host looks like an actual host
- - Consider re-assign as a mention from a notification point of view
- - Add pagination headers to already paginated API resources
- - Properly generate diff of orphan commits, like the first commit in a repository
- - Improve the consistency of commit titles, branch names, tag names, issue/MR titles, on their respective project pages
- - Autocomplete data is now always loaded, instead of when focusing a comment text area
- - Improved performance of finding issues for an entire group
- - Added custom application performance measuring system powered by InfluxDB
- - Add syntax highlighting to diffs
- - Gracefully handle invalid UTF-8 sequences in Markdown links (Stan Hu)
- - Bump fog to 1.36.0 (Stan Hu)
- - Add user's last used IP addresses to admin page (Stan Hu)
- - Add housekeeping function to project settings page
- - The default GitLab logo now acts as a loading indicator
- - Fix caching issue where build status was not updating in project dashboard (Stan Hu)
- - Accept 2xx status codes for successful Webhook triggers (Stan Hu)
- - Fix missing date of month in network graph when commits span a month (Stan Hu)
- - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
- - Don't notify users twice if they are both project watchers and subscribers (Stan Hu)
- - Remove gray background from layout in UI
- - Fix signup for OAuth providers that don't provide a name
- - Implement new UI for group page
- - Implement search inside emoji picker
- - Let the CI runner know about builds that this build depends on
- - Add API support for looking up a user by username (Stan Hu)
- - Add project permissions to all project API endpoints (Stan Hu)
- - Link to milestone in "Milestone changed" system note
- - Only allow group/project members to mention `@all`
- - Expose Git's version in the admin area (Trey Davis)
- - Add "Frequently used" category to emoji picker
- - Add CAS support (tduehr)
- - Add link to merge request on build detail page
- - Fix: Problem with projects ending with .keys (Jose Corcuera)
- - Revert back upvote and downvote button to the issue and MR pages
- - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg)
- - Add system hook messages for project rename and transfer (Steve Norman)
- - Fix version check image in Safari
- - Show 'All' tab by default in the builds page
- - Add Open Graph and Twitter Card data to all pages
- - Fix API project lookups when querying with a namespace with dots (Stan Hu)
- - Enable forcing Two-factor authentication sitewide, with optional grace period
- - Import GitHub Pull Requests into GitLab
- - Change single user API endpoint to return more detailed data (Michael Potthoff)
- - Update version check images to use SVG
- - Validate README format before displaying
- - Enable Microsoft Azure OAuth2 support (Janis Meybohm)
- - Properly set task-list class on single item task lists
- - Add file finder feature in tree view (Kyungchul Shin)
- - Ajax filter by message for commits page
- - API: Add support for deleting a tag via the API (Robert Schilling)
- - Allow subsequent validations in CI Linter
- - Show referenced MRs & Issues only when the current viewer can access them
- - Fix Encoding::CompatibilityError bug when markdown content has some complex URL (Jason Lee)
- - Add API support for managing project's builds
- - Add API support for managing project's build triggers
- - Add API support for managing project's build variables
- - Allow broadcast messages to be edited
- - Autosize Markdown textareas
- - Import GitHub wiki into GitLab
- - Add reporters ability to download and browse build artifacts (Andrew Johnson)
- - Autofill referring url in message box when reporting user abuse.
- - Remove leading comma on award emoji when the user is the first to award the emoji (Zeger-Jan van de Weg)
- - Add build artifacts browser
- - Improve UX in builds artifacts browser
- - Increase default size of `data` column in `events` table when using MySQL
- - Expose button to CI Lint tool on project builds page
- - Fix: Creator should be added as a master of the project on creation
- - Added X-GitLab-... headers to emails from CI and Email On Push services (Anton Baklanov)
- - Add IP check against DNSBLs at account sign-up
- - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
-
-## 8.3.10
-
- - Prevent unauthorized access to other projects build traces
- - Forbid scripting for wiki files
-
-## 8.3.9
-
- - Prevent privilege escalation via "impersonate" feature
- - Prevent privilege escalation via notes API
- - Prevent privilege escalation via project webhook API
- - Prevent XSS via custom issue tracker URL
- - Prevent XSS via `window.opener`
- - Prevent information disclosure via project labels
- - Prevent information disclosure via new merge request page
-
-## 8.3.8
-
- - Fix persistent XSS vulnerability in `commit_person_link` helper
-
-## 8.3.7
-
- - Fix a 2FA authentication spoofing vulnerability.
-
-## 8.3.6
-
- - Don't attempt to fetch any tags from a forked repo (Stan Hu).
-
-## 8.3.5
-
- - Bump Git version requirement to 2.7.4
-
-## 8.3.4
-
- - Use gitlab-workhorse 0.5.4 (fixes API routing bug)
-
-## 8.3.3
-
- - Preserve CE behavior with JIRA integration by only calling API if URL is set
- - Fix duplicated branch creation/deletion events when using Web UI (Stan Hu)
- - Add configurable LDAP server query timeout
- - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running
- - Suppress e-mails on failed builds if allow_failure is set (Stan Hu)
- - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu)
- - Better support for referencing and closing issues in Asana service (Mike Wyatt)
- - Enable "Add key" button when user fills in a proper key (Stan Hu)
- - Fix error in processing reply-by-email messages (Jason Lee)
- - Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu)
- - Use WOFF versions of SourceSansPro fonts
- - Fix regression when builds were not generated for tags created through web/api interface
- - Fix: maintain milestone filter between Open and Closed tabs (Greg Smethells)
- - Fix missing artifacts and build traces for build created before 8.3
-
-## 8.3.2
-
- - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
- - Add support for Google reCAPTCHA in user registration
-
-## 8.3.1
-
- - Fix Error 500 when global milestones have slashes (Stan Hu)
- - Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
- - Fix LDAP identity and user retrieval when special characters are used
- - Move Sidekiq-cron configuration to gitlab.yml
-
-## 8.3.0 (2015-12-22)
-
- - Bump rack-attack to 4.3.1 for security fix (Stan Hu)
- - API support for starred projects for authorized user (Zeger-Jan van de Weg)
- - Add open_issues_count to project API (Stan Hu)
- - Expand character set of usernames created by Omniauth (Corey Hinshaw)
- - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
- - Add unsubscribe link in the email footer (Zeger-Jan van de Weg)
- - Provide better diagnostic message upon project creation errors (Stan Hu)
- - Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
- - Remove api credentials from link to build_page
- - Deprecate GitLabCiService making it to always be inactive
- - Bump gollum-lib to 4.1.0 (Stan Hu)
- - Fix broken group avatar upload under "New group" (Stan Hu)
- - Update project repositorize size and commit count during import:repos task (Stan Hu)
- - Fix API setting of 'public' attribute to false will make a project private (Stan Hu)
- - Handle and report SSL errors in Webhook test (Stan Hu)
- - Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu)
- - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
- - WIP identifier on merge requests no longer requires trailing space
- - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
- - Fix 500 error when update group member permission
- - Fix: As an admin, cannot add oneself as a member to a group/project
- - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
- - Recognize issue/MR/snippet/commit links as references
- - Backport JIRA features from EE to CE
- - Add ignore whitespace change option to commit view
- - Fire update hook from GitLab
- - Allow account unlock via email
- - Style warning about mentioning many people in a comment
- - Fix: sort milestones by due date once again (Greg Smethells)
- - Migrate all CI::Services and CI::WebHooks to Services and WebHooks
- - Don't show project fork event as "imported"
- - Add API endpoint to fetch merge request commits list
- - Don't create CI status for refs that doesn't have .gitlab-ci.yml, even if the builds are enabled
- - Expose events API with comment information and author info
- - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
- - Run custom Git hooks when branch is created or deleted.
- - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch
- - Add languages page to graphs
- - Block LDAP user when they are no longer found in the LDAP server
- - Improve wording on project visibility levels (Zeger-Jan van de Weg)
- - Fix editing notes on a merge request diff
- - Automatically select default clone protocol based on user preferences (Eirik Lygre)
- - Make Network page as sub tab of Commits
- - Add copy-to-clipboard button for Snippets
- - Add indication to merge request list item that MR cannot be merged automatically
- - Default target branch to patch-n when editing file in protected branch
- - Add Builds tab to merge request detail page
- - Allow milestones, issues and MRs to be created from dashboard and group indexes
- - Use new style for wiki
- - Use new style for milestone detail page
- - Fix sidebar tooltips when collapsed
- - Prevent possible XSS attack with award-emoji
- - Upgraded Sidekiq to 4.x
- - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg)
- - Fix emoji aliases problem
- - Fix award-emojis Flash alert's width
- - Fix deleting notes on a merge request diff
- - Display referenced merge request statuses in the issue description (Greg Smethells)
- - Implement new sidebar for issue and merge request pages
- - Emoji picker improvements
- - Suppress warning about missing `.gitlab-ci.yml` if builds are disabled
- - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
- - Persist runners registration token in database
- - Fix online editor should not remove newlines at the end of the file
- - Expose Git's version in the admin area
- - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
-
-## 8.2.6
-
- - Prevent unauthorized access to other projects build traces
- - Forbid scripting for wiki files
-
-## 8.2.5
-
- - Prevent privilege escalation via "impersonate" feature
- - Prevent privilege escalation via notes API
- - Prevent privilege escalation via project webhook API
- - Prevent XSS via `window.opener`
- - Prevent information disclosure via project labels
- - Prevent information disclosure via new merge request page
-
-## 8.2.4
-
- - Bump Git version requirement to 2.7.4
-
-## 8.2.3
-
- - Fix application settings cache not expiring after changes (Stan Hu)
- - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
- - Update documentation for "Guest" permissions
- - Properly convert Emoji-only comments into Award Emojis
- - Enable devise paranoid mode to prevent user enumeration attack
- - Webhook payload has an added, modified and removed properties for each commit
- - Fix 500 error when creating a merge request that removes a submodule
-
-## 8.2.2
-
- - Fix 404 in redirection after removing a project (Stan Hu)
- - Ensure cached application settings are refreshed at startup (Stan Hu)
- - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- - Fix: Raw private snippets access workflow
- - Prevent "413 Request entity too large" errors when pushing large files with LFS
- - Fix invalid links within projects dashboard header
- - Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
- - Fix: duplicate email notifications on issue comments
-
-## 8.2.1
-
- - Forcefully update builds that didn't want to update with state machine
- - Fix: saving GitLabCiService as Admin Template
-
-## 8.2.0 (2015-11-22)
-
- - Improved performance of finding projects and groups in various places
- - Improved performance of rendering user profile pages and Atom feeds
- - Expose build artifacts path as config option
- - Fix grouping of contributors by email in graph.
- - Improved performance of finding issues with/without labels
- - Fix Drone CI service template not saving properly (Stan Hu)
- - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu)
- - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
- - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
- - Improved performance of finding users by one of their Email addresses
- - Add allow_failure field to commit status API (Stan Hu)
- - Commits without .gitlab-ci.yml are marked as skipped
- - Save detailed error when YAML syntax is invalid
- - Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml
- - Added build artifacts
- - Improved performance of replacing references in comments
- - Show last project commit to default branch on project home page
- - Highlight comment based on anchor in URL
- - Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
- - Improved performance of sorting milestone issues
- - Allow users to select the Files view as default project view (Cristian Bica)
- - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy)
- - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
- - Use git follow flag for commits page when retrieve history for file or directory
- - Show merge request CI status on merge requests index page
- - Send build name and stage in CI notification e-mail
- - Extend yml syntax for only and except to support specifying repository path
- - Enable shared runners to all new projects
- - Bump GitLab-Workhorse to 0.4.1
- - Allow to define cache in `.gitlab-ci.yml`
- - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
- - Remove deprecated CI events from project settings page
- - Use issue editor as cross reference comment author when issue is edited with a new mention.
- - Add graphs of commits ahead and behind default branch (Jeff Stubler)
- - Improve personal snippet access workflow (Douglas Alexandre)
- - [API] Add ability to fetch the commit ID of the last commit that actually touched a file
- - Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
- - Add "New file" link to dropdown on project page
- - Include commit logs in project search
- - Add "added", "modified" and "removed" properties to commit object in webhook
- - Rename "Back to" links to "Go to" because its not always a case it point to place user come from
- - Allow groups to appear in the search results if the group owner allows it
- - Add email notification to former assignee upon unassignment (Adam Lieskovský)
- - New design for project graphs page
- - Remove deprecated dumped yaml file generated from previous job definitions
- - Show specific runners from projects where user is master or owner
- - MR target branch is now visible on a list view when it is different from project's default one
- - Improve Continuous Integration graphs page
- - Make color of "Accept Merge Request" button consistent with current build status
- - Add ignore white space option in merge request diff and commit and compare view
- - Ability to add release notes (markdown text and attachments) to git tags (aka Releases)
- - Relative links from a repositories README.md now link to the default branch
- - Fix trailing whitespace issue in merge request/issue title
- - Fix bug when milestone/label filter was empty for dashboard issues page
- - Add ability to create milestone in group projects from single form
- - Add option to create merge request when editing/creating a file (Dirceu Tiegs)
- - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
- - Add Award Emoji to issue and merge request pages
-
-## 8.1.4
-
- - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
- - Prevent redirect loop when home_page_url is set to the root URL
- - Fix incoming email config defaults
- - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
-
-## 8.1.3
-
- - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
- - Spread out runner contacted_at updates
- - Use issue editor as cross reference comment author when issue is edited with a new mention
- - Add Facebook authentication
-
-## 8.1.2
-
- - Fix cloning Wiki repositories via HTTP (Stan Hu)
- - Add migration to remove satellites directory
- - Fix specific runners visibility
- - Fix 500 when editing CI service
- - Require CI jobs to be named
- - Fix CSS for runner status
- - Fix CI badge
- - Allow developer to manage builds
-
-## 8.1.1
-
- - Removed, see 8.1.2
-
-## 8.1.0 (2015-10-22)
-
- - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
- - Fix duplicate repositories in GitHub import page (Stan Hu)
- - Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
- - Adds ability to create directories using the web editor (Ben Ford)
- - Cleanup stuck CI builds
- - Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
- - Show notifications button when user is member of group rather than project (Grzegorz Bizon)
- - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
- - Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
- - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu)
- - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
- - Speed up load times of issue detail pages by roughly 1.5x
- - Fix CI rendering regressions
- - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
- - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
- - Make diff file view easier to use on mobile screens (Stan Hu)
- - Improved performance of finding users by username or Email address
- - Fix bug where merge request comments created by API would not trigger notifications (Stan Hu)
- - Add support for creating directories from Files page (Stan Hu)
- - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
- - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
- - Improved performance of the trending projects page
- - Remove CI migration task
- - Improved performance of finding projects by their namespace
- - Add assignee data to Issuables' hook_data (Bram Daams)
- - Fix bug where transferring a project would result in stale commit links (Stan Hu)
- - Fix build trace updating
- - Include full path of source and target branch names in New Merge Request page (Stan Hu)
- - Add user preference to view activities as default dashboard (Stan Hu)
- - Add option to admin area to sign in as a specific user (Pavel Forkert)
- - Show CI status on all pages where commits list is rendered
- - Automatically enable CI when push .gitlab-ci.yml file to repository
- - Move CI charts to project graphs area
- - Fix cases where Markdown did not render links in activity feed (Stan Hu)
- - Add first and last to pagination (Zeger-Jan van de Weg)
- - Added Commit Status API
- - Added Builds View
- - Added when to .gitlab-ci.yml
- - Show CI status on commit page
- - Added CI_BUILD_TAG, _STAGE, _NAME and _TRIGGERED to CI builds
- - Show CI status on Your projects page and Starred projects page
- - Remove "Continuous Integration" page from dashboard
- - Add notes and SSL verification entries to hook APIs (Ben Boeckel)
- - Fix grammar in admin area "labels" .nothing-here-block when no labels exist.
- - Move CI runners page to project settings area
- - Move CI variables page to project settings area
- - Move CI triggers page to project settings area
- - Move CI project settings page to CE project settings area
- - Fix bug when removed file was not appearing in merge request diff
- - Show warning when build cannot be served by any of the available CI runners
- - Note the original location of a moved project when notifying users of the move
- - Improve error message when merging fails
- - Add support of multibyte characters in LDAP UID (Roman Petrov)
- - Show additions/deletions stats on merge request diff
- - Remove footer text in emails (Zeger-Jan van de Weg)
- - Ensure code blocks are properly highlighted after a note is updated
- - Fix wrong access level badge on MR comments
- - Hide password in the service settings form
- - Move CI webhooks page to project settings area
- - Fix User Identities API. It now allows you to properly create or update user's identities.
- - Add user preference to change layout width (Peter Göbel)
- - Use commit status in merge request widget as preferred source of CI status
- - Integrate CI commit and build pages into project pages
- - Move CI services page to project settings area
- - Add "Quick Submit" behavior to input fields throughout the application. Use
- Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux.
- - Fix position of hamburger in header for smaller screens (Han Loong Liauw)
- - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
- - Persist filters when sorting on admin user page (Jerry Lukins)
- - Update style of snippets pages (Han Loong Liauw)
- - Allow dashboard and group issues/MRs to be filtered by label
- - Add spellcheck=false to certain input fields
- - Invalidate stored service password if the endpoint URL is changed
- - Project names are not fully shown if group name is too big, even on group page view
- - Apply new design for Files page
- - Add "New Page" button to Wiki Pages tab (Stan Hu)
- - Only render 404 page from /public
- - Hide passwords from services API (Alex Lossent)
- - Fix: Images cannot show when projects' path was changed
- - Let gitlab-git-http-server generate and serve 'git archive' downloads
- - Optimize query when filtering on issuables (Zeger-Jan van de Weg)
- - Fix padding of outdated discussion item.
- - Animate the logo on hover
-
-## 8.0.5
-
- - Correct lookup-by-email for LDAP logins
- - Fix loading spinner sometimes not being hidden on Merge Request tab switches
-
-## 8.0.4
-
- - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
- - Fix referrals for :back and relative URL installs
- - Fix anchors to comments in diffs
- - Remove CI token from build traces
- - Fix "Assign All" button on Runner admin page
- - Fix search in Files
- - Add full project namespace to payload of system webhooks (Ricardo Band)
-
-## 8.0.3
-
- - Fix URL shown in Slack notifications
- - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
- - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
- - Add work_in_progress key to MR webhooks (Ben Boeckel)
-
-## 8.0.2
-
- - Fix default avatar not rendering in network graph (Stan Hu)
- - Skip check_initd_configured_correctly on omnibus installs
- - Prevent double-prefixing of help page paths
- - Clarify confirmation text on user deletion
- - Make commit graphs responsive to window width changes (Stan Hu)
- - Fix top margin for sign-in button on public pages
- - Fix LDAP attribute mapping
- - Remove git refs used internally by GitLab from network graph (Stan Hu)
- - Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu)
- - Fix Reply by email for non-UTF-8 messages.
- - Add option to use StartTLS with Reply by email IMAP server.
- - Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
-
-## 8.0.1
-
- - Improve CI migration procedure and documentation
-
-## 8.0.0 (2015-09-22)
-
- - Fix Markdown links not showing up in dashboard activity feed (Stan Hu)
- - Remove milestones from merge requests when milestones are deleted (Stan Hu)
- - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu)
- - Fix broken sort in merge request API (Stan Hu)
- - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu)
- - Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu)
- - Add "replace" and "upload" functionalities to allow user replace existing file and upload new file into current repository
- - Fix URL construction for merge requests, issues, notes, and commits for relative URL config (Stan Hu)
- - Fix emoji URLs in Markdown when relative_url_root is used (Stan Hu)
- - Omit filename in Content-Disposition header in raw file download to avoid RFC 6266 encoding issues (Stan HU)
- - Fix broken Wiki Page History (Stan Hu)
- - Import forked repositories asynchronously to prevent large repositories from timing out (Stan Hu)
- - Prevent anchors from being hidden by header (Stan Hu)
- - Fix bug where only the first 15 Bitbucket issues would be imported (Stan Hu)
- - Sort issues by creation date in Bitbucket importer (Stan Hu)
- - Prevent too many redirects upon login when home page URL is set to external_url (Stan Hu)
- - Improve dropdown positioning on the project home page (Hannes Rosenögger)
- - Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibilty mode (Stan Hu)
- - Remove user OAuth tokens from the database and request new tokens each session (Stan Hu)
- - Restrict users API endpoints to use integer IDs (Stan Hu)
- - Only show recent push event if the branch still exists or a recent merge request has not been created (Stan Hu)
- - Remove satellites
- - Better performance for web editor (switched from satellites to rugged)
- - Faster merge
- - Ability to fetch merge requests from refs/merge-requests/:id
- - Allow displaying of archived projects in the admin interface (Artem Sidorenko)
- - Allow configuration of import sources for new projects (Artem Sidorenko)
- - Search for comments should be case insensetive
- - Create cross-reference for closing references on commits pushed to non-default branches (Maël Valais)
- - Ability to search milestones
- - Gracefully handle SMTP user input errors (e.g. incorrect email addresses) to prevent Sidekiq retries (Stan Hu)
- - Move dashboard activity to separate page (for your projects and starred projects)
- - Improve performance of git blame
- - Limit content width to 1200px for most of pages to improve readability on big screens
- - Fix 500 error when submit project snippet without body
- - Improve search page usability
- - Bring more UI consistency in way how projects, snippets and groups lists are rendered
- - Make all profiles and group public
- - Fixed login failure when extern_uid changes (Joel Koglin)
- - Don't notify users without access to the project when they are (accidentally) mentioned in a note.
- - Retrieving oauth token with LDAP credentials
- - Load Application settings from running database unless env var USE_DB=false
- - Added Drone CI integration (Kirill Zaitsev)
- - Allow developers to retry builds
- - Hide advanced project options for non-admin users
- - Fail builds if no .gitlab-ci.yml is found
- - Refactored service API and added automatically service docs generator (Kirill Zaitsev)
- - Added web_url key project hook_attrs (Kirill Zaitsev)
- - Add ability to get user information by ID of an SSH key via the API
- - Fix bug which IE cannot show image at markdown when the image is raw file of gitlab
- - Add support for Crowd
- - Global Labels that are available to all projects
- - Fix highlighting of deleted lines in diffs.
- - Project notification level can be set on the project page itself
- - Added service API endpoint to retrieve service parameters (Petheő Bence)
- - Add FogBugz project import (Jared Szechy)
- - Sort users autocomplete lists by user (Allister Antosik)
- - Webhook for issue now contains repository field (Jungkook Park)
- - Add ability to add custom text to the help page (Jeroen van Baarsen)
- - Add pg_schema to backup config
- - Fix references to target project issues in Merge Requests markdown preview and textareas (Francesco Levorato)
- - Redirect from incorrectly cased group or project path to correct one (Francesco Levorato)
- - Removed API calls from CE to CI
-
-## 7.14.3 through 0.8.0
+## 8.15.8 through 0.8.0
- See [changelogs/archive.md](changelogs/archive.md)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 106d4ac0005..b7c0622b4f4 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.69.0
+0.74.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index c68d476cc8e..9b9a244206f 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-5.11.0
+6.0.2
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 1545d966571..d5c0c991428 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-3.5.0
+3.5.1
diff --git a/Gemfile b/Gemfile
index 9e1e0b3c0c6..ee576c53fe9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -233,6 +233,9 @@ gem 'charlock_holmes', '~> 0.7.5'
# Faster JSON
gem 'oj', '~> 2.17.4'
+# Faster blank
+gem 'fast_blank'
+
# Parse time & duration
gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6'
@@ -338,10 +341,10 @@ group :development, :test do
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0'
- gem 'gitlab-styles', '~> 2.2.0', require: false
+ gem 'gitlab-styles', '~> 2.3', require: false
# Pin these dependencies, otherwise a new rule could break the CI pipelines
- gem 'rubocop', '~> 0.52.0'
- gem 'rubocop-rspec', '~> 1.20.1'
+ gem 'rubocop', '~> 0.52.1'
+ gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.26.0', require: false
@@ -403,7 +406,7 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.73.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.78.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 10e2585a0e8..5532888d179 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -207,6 +207,7 @@ GEM
faraday_middleware-multi_json (0.0.6)
faraday_middleware
multi_json
+ fast_blank (1.0.0)
fast_gettext (1.4.0)
ffaker (2.4.0)
ffi (1.9.18)
@@ -284,7 +285,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
- gitaly-proto (0.73.0)
+ gitaly-proto (0.78.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@@ -303,7 +304,7 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.3)
- gitlab-styles (2.2.0)
+ gitlab-styles (2.3.1)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
@@ -582,7 +583,7 @@ GEM
rubypants (~> 0.2)
orm_adapter (0.5.0)
os (0.9.6)
- parallel (1.12.0)
+ parallel (1.12.1)
parser (2.4.0.2)
ast (~> 2.3)
parslet (1.5.0)
@@ -785,7 +786,7 @@ GEM
pg
rails
sqlite3
- rubocop (0.52.0)
+ rubocop (0.52.1)
parallel (~> 1.10)
parser (>= 2.4.0.2, < 3.0)
powerpack (~> 0.1)
@@ -794,8 +795,8 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-rspec (1.20.1)
- rubocop (>= 0.51.0)
+ rubocop-rspec (1.22.1)
+ rubocop (>= 0.52.1)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.16.2)
@@ -1034,6 +1035,7 @@ DEPENDENCIES
email_spec (~> 1.6.0)
factory_bot_rails (~> 4.8.2)
faraday (~> 0.12)
+ fast_blank
ffaker (~> 2.4)
flay (~> 2.8.0)
flipper (~> 0.11.0)
@@ -1054,11 +1056,11 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
- gitaly-proto (~> 0.73.0)
+ gitaly-proto (~> 0.78.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2)
- gitlab-styles (~> 2.2.0)
+ gitlab-styles (~> 2.3)
gitlab_omniauth-ldap (~> 2.0.4)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4)
@@ -1160,8 +1162,8 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.52.0)
- rubocop-rspec (~> 1.20.1)
+ rubocop (~> 0.52.1)
+ rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
ruby_parser (~> 3.8)
diff --git a/PROCESS.md b/PROCESS.md
index 3fcf676b302..99af3be7f14 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -85,7 +85,8 @@ These types of merge requests for the upcoming release need special consideratio
and a dedicated team with front-end, back-end, and UX.
* **Small features**: any other feature request.
-**Large features** must be with a maintainer **by the 1st**. This means that:
+It is strongly recommended that **large features** be with a maintainer **by the
+1st**. This means that:
* There is a merge request (even if it's WIP).
* The person (or people, if it needs a frontend and backend maintainer) who will
@@ -100,14 +101,37 @@ The maintainer can also choose to assign a reviewer to perform an initial
review, but this way the maintainer is unlikely to be surprised by receiving an
MR later in the cycle.
-**Small features** must be with a reviewer (not necessarily maintainer) **by the
-3rd**.
+It is strongly recommended that **small features** be with a reviewer (not
+necessarily a maintainer) **by the 3rd**.
Most merge requests from the community do not have a specific release
target. However, if one does and falls into either of the above categories, it's
the reviewer's responsibility to manage the above communication and assignment
on behalf of the community member.
+#### What happens if these deadlines are missed?
+
+If a small or large feature is _not_ with a maintainer or reviewer by the
+recommended date, this does _not_ mean that maintainers or reviewers will refuse
+to review or merge it, or that the feature will definitely not make it in before
+the feature freeze.
+
+However, with every day that passes without review, it will become more likely
+that the feature will slip, because maintainers and reviewers may not have
+enough time to do a thorough review, and developers may not have enough time to
+adequately address any feedback that may come back.
+
+A maintainer or reviewer may also determine that it will not be possible to
+finish the current scope of the feature in time, but that it is possible to
+reduce the scope so that something can still ship this month, with the remaining
+scope moving to the next release. The sooner this decision is made, in
+conversation with the Product Manager and developer, the more time there is to
+extract that which is now out of scope, and to finish that which remains in scope.
+
+For these reasons, it is strongly recommended to follow the guidelines above,
+to maximize the chances of your feature making it in before the feature freeze,
+and to prevent any last minute surprises.
+
### On the 7th
Merge requests should still be complete, following the
diff --git a/VERSION b/VERSION
index 80959b81ba4..7f6774b38ba 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-10.4.0-pre
+10.5.0-pre
diff --git a/app/assets/images/icons.json b/app/assets/images/icons.json
index 296cb856734..132a373baec 100644
--- a/app/assets/images/icons.json
+++ b/app/assets/images/icons.json
@@ -1 +1 @@
-{"iconCount":189,"spriteSize":85766,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o-open","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]} \ No newline at end of file
+{"iconCount":189,"spriteSize":85900,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_notfound","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]} \ No newline at end of file
diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg
index 8d5426da19c..09efe331f93 100644
--- a/app/assets/images/icons.svg
+++ b/app/assets/images/icons.svg
@@ -1 +1 @@
-<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.536 7.95L1.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 1 1-1.414-1.415L5.536 7.95zm7 0L8.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.414-1.415l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-down" xmlns="http://www.w3.org/2000/svg"><path d="M10.472 7.282a.862.862 0 0 1 1.26-.006c.357.364.357.958 0 1.285L8.627 11.73A.886.886 0 0 1 8 12a.849.849 0 0 1-.627-.27L4.275 8.561a.904.904 0 0 1-.013-1.285.861.861 0 0 1 1.26-.007l2.486 2.527z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4 12.5v-9A1.5 1.5 0 0 1 5.5 2h2.104c2.182 0 3.879.681 3.879 2.982 0 1.067-.517 2.227-1.374 2.595v.073C11.176 7.963 12 8.865 12 10.466 12 12.914 10.19 14 7.911 14H5.5A1.5 1.5 0 0 1 4 12.5zm2.376-5.696H7.49c1.164 0 1.665-.552 1.665-1.417 0-.94-.534-1.289-1.649-1.289h-1.13v2.706zm0 5.098h1.341c1.293 0 1.956-.515 1.956-1.62 0-1.049-.647-1.472-1.956-1.472H6.376v3.092z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="bookmark" xmlns="http://www.w3.org/2000/svg"><path d="M6.746 10.505a2 2 0 0 1 2.508 0L11 11.911V3H5v8.91l1.746-1.405zM5 1h6a2 2 0 0 1 2 2v10.999a1 1 0 0 1-1.627.779L8 12.064l-3.373 2.714A1 1 0 0 1 3 13.998V3a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="bullhorn" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.143 10H7V4H3a3 3 0 1 0 0 6h.143l.734 5.141a1 1 0 0 0 .99.859h1.556a.5.5 0 0 0 .495-.57L6.143 10zM8 4c1.034.02 2.039-.274 3.014-.883.727-.455 1.836-1.334 3.328-2.637A1 1 0 0 1 16 1.233v10.764a1 1 0 0 1-1.595.803c-1.658-1.227-2.788-1.992-3.392-2.294-.781-.39-1.785-.559-3.013-.506V4z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chart" xmlns="http://www.w3.org/2000/svg"><path d="M15 14a1 1 0 0 1 0 2H2a2 2 0 0 1-2-2V1a1 1 0 1 1 2 0v13h13zM3.142 8.735l2.502-2.561a.5.5 0 0 1 .714-.003L8 7.833l3.592-4.553a.5.5 0 0 1 .796.015l2.516 3.454a.5.5 0 0 1 .096.295V12.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V9.085a.5.5 0 0 1 .142-.35z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 9 13" id="collapse"><path d="M.084.25C.01.18-.015.12.008.071.031.024.093 0 .194 0h8.521c.1 0 .162.024.185.072.023.048-.002.107-.075.177l-4.11 3.935a.372.372 0 0 1-.11.072h-.301a.508.508 0 0 1-.11-.072L.084.249zM.377 6.88a.364.364 0 0 1-.26-.105.334.334 0 0 1-.11-.25v-.709c0-.096.036-.179.11-.249a.364.364 0 0 1 .26-.105h8.15c.101 0 .188.035.261.105.074.07.11.153.11.25v.709c0 .096-.036.179-.11.249a.364.364 0 0 1-.26.105H.377zM.084 12.132c-.074.07-.099.129-.076.177.023.048.085.072.186.072h8.521c.1 0 .162-.024.185-.072.023-.048-.002-.107-.075-.177l-4.11-3.935a.372.372 0 0 0-.11-.072h-.301a.508.508 0 0 0-.11.072l-4.11 3.935z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="cut" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="2" y="7" fill-rule="evenodd" rx="1"/></symbol><symbol viewBox="0 0 16 16" id="dashboard" xmlns="http://www.w3.org/2000/svg"><path d="M7.709 10.021l.696-2.6a.5.5 0 0 1 .966.26l-.657 2.45A2 2 0 0 1 10 12H6a2 2 0 0 1 1.709-1.979zM0 8.9a8 8 0 0 1 15.998 0H16v3.6a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5V8.9zM14 9A6 6 0 1 0 2 9v3.5a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9zM3.5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm9 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-7-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm5 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 105 26" id="double-headed-arrow" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.018 11.089L15.138.614c1.23-.911 3.086-.795 4.147.26.461.46.715 1.045.715 1.651v20.95C20 24.869 18.684 26 17.06 26a3.238 3.238 0 0 1-1.921-.614L1.019 14.911C-.212 14-.347 12.405.714 11.35c.094-.094.195-.18.303-.261zm102.964 0c.108.08.21.167.303.26 1.061 1.056.925 2.65-.303 3.562l-14.12 10.475A3.238 3.238 0 0 1 87.94 26C86.316 26 85 24.87 85 23.475V2.525c0-.606.254-1.192.715-1.65 1.061-1.056 2.917-1.172 4.146-.26l14.12 10.474zM35 17a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 1600 1600" id="ellipsis_v" xmlns="http://www.w3.org/2000/svg"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></symbol><symbol viewBox="0 0 18 18" id="emoji_slightly_smiling_face" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369.721.721 0 0 1 .568.047.715.715 0 0 1 .37.445 2.91 2.91 0 0 0 1.084 1.518A2.93 2.93 0 0 0 9 12.75a2.93 2.93 0 0 0 1.775-.58 2.913 2.913 0 0 0 1.084-1.518.711.711 0 0 1 .375-.445.737.737 0 0 1 .575-.047c.195.063.34.186.433.37.094.183.11.372.047.568zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smile" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568zM14 6.37c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm-6.5 0c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm9 2.63a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smiley" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568h.001zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6c.92.397 1.91.6 2.912.598a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39c.397-.92.6-1.91.598-2.912zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z"/></symbol><symbol viewBox="0 0 16 16" id="epic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.985 8.044l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637A2 2 0 0 0 1.618 9h11.661a2 2 0 0 0 1.706-.956zm0 3l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637a2 2 0 0 0 .576.084h11.661a2 2 0 0 0 1.706-.956zM3.618 2h10.995a1 1 0 0 1 .948 1.316l-1.333 4a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l2-4A1 1 0 0 1 3.618 2zm-.382 4h9.322l.667-2H4.236l-1 2z"/></symbol><symbol viewBox="0 0 16 16" id="external-link" xmlns="http://www.w3.org/2000/svg"><path d="M13.121 4.177l-4.95 4.95a1 1 0 1 1-1.414-1.414l4.95-4.95-1.386-1.386a.5.5 0 0 1 .299-.85l4.709-.524a.5.5 0 0 1 .552.552l-.523 4.71a.5.5 0 0 1-.851.297l-1.386-1.385zM12 8.884a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-8a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-4z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-addition" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-o" xmlns="http://www.w3.org/2000/svg"><path d="M13 5l-4.365-.005a2 2 0 0 1-1.882-1.33A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zm0-2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-o-open" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.59 5.464a2.998 2.998 0 0 1 1.096 3.845l-1.666 3.436A4 4 0 0 1 10.46 15H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.558a2 2 0 0 1 1.898 1.368l.21.632h4.973a2 2 0 0 1 2 2 2 2 0 0 1-.027.329l-.023.135zM5.285 7a1 1 0 0 0-.9.564l-1.939 4a1 1 0 0 0 .9 1.436h7.074a2 2 0 0 0 1.8-1.128l1.665-3.436a1 1 0 0 0-.9-1.436h-7.7z"/></symbol><symbol viewBox="0 0 16 16" id="folder-open" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 6H5.333a2 2 0 0 0-1.664.89l-3.333 5a2 2 0 0 0-.285.662A3.017 3.017 0 0 1 0 12V3a2 2 0 0 1 2-2h2.838a3 3 0 0 1 2.828 2H11a3 3 0 0 1 3 3zM5.333 7h9.53a1 1 0 0 1 .875 1.486l-2.492 4.485A2 2 0 0 1 11.498 14H2a1 1 0 0 1-.832-1.555l3.333-5A1 1 0 0 1 5.333 7z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M9 13h3v-3H4v3h3v-1a1 1 0 0 1 2 0v1zm5-3v3.659c0 .729-.657 1.341-1.5 1.341h-9c-.843 0-1.5-.612-1.5-1.341V10h-.88C.502 10 0 9.486 0 8.853c0-.307.12-.601.333-.816l6.405-6.463a1.56 1.56 0 0 1 2.374-.052L15.66 8.03c.444.441.455 1.167.024 1.622a1.108 1.108 0 0 1-.804.348H14zM7.95 3.273l-4.595 4.64h9.264l-4.67-4.64z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="hourglass" xmlns="http://www.w3.org/2000/svg"><path d="M10.331 4.889A2.988 2.988 0 0 0 11 3V2H5v1c0 .362.064.709.182 1.03l5.15.859zM3 14v-1c0-1.78.93-3.342 2.33-4.228.447-.327.67-.582.67-.764 0-.19-.242-.46-.725-.815A4.996 4.996 0 0 1 3 3V2H2a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2h-1v1a4.997 4.997 0 0 1-2.39 4.266c-.407.3-.61.545-.61.734 0 .19.203.434.61.734A4.997 4.997 0 0 1 13 13v1h1a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2h1zm8 0v-1a3 3 0 0 0-6 0v1h6z"/></symbol><symbol viewBox="0 0 38 38" id="image-comment-dark" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#1F78D1"/><path fill="#FFF" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 38 38" id="image-comment-light" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 16 16" id="import" xmlns="http://www.w3.org/2000/svg"><path d="M9 8h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0L5.6 8.8A.5.5 0 0 1 6 8h1V1a1 1 0 1 1 2 0v7zM0 8a1 1 0 1 1 2 0 6 6 0 1 0 12 0 1 1 0 0 1 2 0A8 8 0 1 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-external" xmlns="http://www.w3.org/2000/svg"><path d="M11 4a5.99 5.99 0 0 0-2 .341V3a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2.528a6.003 6.003 0 0 0 2.705 1.736A2.99 2.99 0 0 1 8 16H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3v1zM8.212 8.97l-.568-.876A.25.25 0 0 1 7.66 7.8l.404-.5a.25.25 0 0 1 .284-.076l.938.36c.256-.182.543-.325.85-.42l.323-.988a.25.25 0 0 1 .237-.173h.643a.25.25 0 0 1 .238.173l.321.989c.308.094.595.237.852.418l.937-.359a.25.25 0 0 1 .284.076l.404.5a.25.25 0 0 1 .016.293l-.568.875c.113.297.18.616.192.95l.9.54a.25.25 0 0 1 .114.27l-.145.627a.25.25 0 0 1-.221.192l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.282a.25.25 0 0 1-.29-.051l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.905a.25.25 0 0 1-.29.05l-.577-.281a.25.25 0 0 1-.138-.26L9 12.254a3.015 3.015 0 0 1-.512-.607l-1.114-.098a.25.25 0 0 1-.222-.192l-.145-.627a.25.25 0 0 1 .115-.27l.899-.54c.012-.334.08-.653.192-.95zm2.806 2.034a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="italic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.5 12l2-8H6a1 1 0 1 1 0-2h6a1 1 0 0 1 0 2h-1.5l-2 8H10a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1.5z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="menu" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.143 2h13.714C15.488 2 16 2.448 16 3s-.512 1-1.143 1H1.143C.512 4 0 3.552 0 3s.512-1 1.143-1zm0 5h13.714C15.488 7 16 7.448 16 8s-.512 1-1.143 1H1.143C.512 9 0 8.552 0 8s.512-1 1.143-1zm0 5h13.714c.631 0 1.143.448 1.143 1s-.512 1-1.143 1H1.143C.512 14 0 13.552 0 13s.512-1 1.143-1z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pencil-square" xmlns="http://www.w3.org/2000/svg"><path d="M12 9a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V9zm.778-7.179l1.414 1.415-6.476 6.476a1 1 0 0 1-.498.27l-1.51.325.323-1.512a1 1 0 0 1 .27-.497l6.477-6.477zM15.607.407a1 1 0 0 1 0 1.414l-.708.707-1.414-1.414.707-.707a1 1 0 0 1 1.415 0z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2H9V2a1 1 0 1 0-2 0v5z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="podcast" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862a1 1 0 0 1-.785 1.177A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-1-1 1 1 0 0 1 .02-.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 7.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4.464 2.464A1 1 0 0 1 5.88 3.88a3 3 0 0 0 0 4.242 1 1 0 0 1-1.415 1.415 5 5 0 0 1 0-7.072zm7.072 7.072A1 1 0 0 1 10.12 8.12a3 3 0 0 0 0-4.242 1 1 0 0 1 1.415-1.415 5 5 0 0 1 0 7.072zM2.343.343a1 1 0 1 1 1.414 1.414 6 6 0 0 0 0 8.486 1 1 0 1 1-1.414 1.414 8 8 0 0 1 0-11.314zm11.314 11.314a1 1 0 1 1-1.414-1.414 6 6 0 0 0 0-8.486A1 1 0 0 1 13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.625 4.423A4.897 4.897 0 0 1 8.079 3c2.73 0 4.944 2.239 4.944 5s-2.214 5-4.944 5c-1.41 0-2.723-.6-3.655-1.633a.98.98 0 0 0-1.397-.066 1.008 1.008 0 0 0-.064 1.413A6.87 6.87 0 0 0 8.079 15C11.9 15 15 11.866 15 8s-3.099-7-6.921-7A6.866 6.866 0 0 0 3.08 3.158L1.833 2.137a.49.49 0 0 0-.695.074.504.504 0 0 0-.11.311L1 7.26a.497.497 0 0 0 .6.492l4.576-1.013a.5.5 0 0 0 .206-.877L4.625 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.375 4.423A4.897 4.897 0 0 0 7.921 3c-2.73 0-4.944 2.239-4.944 5s2.214 5 4.944 5c1.41 0 2.723-.6 3.655-1.633a.98.98 0 0 1 1.397-.066c.403.373.432 1.005.064 1.413A6.87 6.87 0 0 1 7.921 15C4.1 15 1 11.866 1 8s3.099-7 6.921-7c1.915 0 3.706.792 4.999 2.158l1.247-1.021a.49.49 0 0 1 .695.074c.07.088.11.198.11.311L15 7.26a.497.497 0 0 1-.6.492L9.824 6.739a.5.5 0 0 1-.206-.877l1.757-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.114 6.958a4 4 0 0 0 5.283 4.775 1 1 0 1 1 .712 1.87A6 6 0 0 1 2.182 6.44l-.741-.2a.5.5 0 0 1-.12-.915l2.195-1.268a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.712-1.87 6 6 0 0 1 7.927 7.162l.742.2a.5.5 0 0 1 .12.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 12 16" id="scroll_down" xmlns="http://www.w3.org/2000/svg"><path class="fcfirst-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043a.51.51 0 0 0 .321-.105c.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/><path class="fcsecond-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/><path class="fcthird-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91a.458.458 0 0 1-.136.09h-.37a.626.626 0 0 1-.136-.09"/></symbol><symbol viewBox="0 0 12 16" id="scroll_up" xmlns="http://www.w3.org/2000/svg"><path d="M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043a.51.51 0 0 1 .321.105c.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09a.458.458 0 0 0-.136-.09h-.37a.626.626 0 0 0-.136.09"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8c1.657 0 3 1.373 3 3.067v7.346c0 1.065-.54 2.053-1.426 2.611l-4 2.52a2.944 2.944 0 0 1-3.148 0l-4-2.52A3.083 3.083 0 0 1 1 10.414V3.066C1 1.373 2.343 0 4 0zm0 2.045c-.552 0-1 .457-1 1.022v7.346c0 .355.18.685.475.87l4 2.52a.981.981 0 0 0 1.05 0l4-2.52c.295-.185.475-.515.475-.87V3.067c0-.565-.448-1.022-1-1.022H4zm0 1.533c0-.282.224-.511.5-.511h4V12.1a.52.52 0 0 1-.069.258.494.494 0 0 1-.684.183l-3.5-2.098a.513.513 0 0 1-.247-.44V3.577z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 14 14" id="spinner" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="7" cy="7" r="6" stroke="#000" stroke-opacity=".1" stroke-width="2"/><path fill="#000" fill-opacity=".1" fill-rule="nonzero" d="M7 0a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5V0z"/></g></symbol><symbol viewBox="0 0 16 16" id="staged" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.828 3.536l1.415-1.415a1 1 0 1 1 1.414 1.415l-2.121 2.12a.997.997 0 0 1-1.415 0L9.707 4.244a1 1 0 0 1 1.414-1.415l.707.708zM2 3h5a1 1 0 1 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 14 14" id="status_canceled" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M5.2 3.8l4.9 4.9c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L3.8 5.2c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0"/></g></symbol><symbol viewBox="0 0 22 22" id="status_canceled_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M8.171 5.971l7.7 7.7a.76.76 0 0 1 0 1.1l-1.1 1.1a.76.76 0 0 1-1.1 0l-7.7-7.7a.76.76 0 0 1 0-1.1l1.1-1.1a.76.76 0 0 1 1.1 0"/></symbol><symbol viewBox="0 0 16 16" id="status_closed" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83a1 1 0 0 1 1.414 1.416l-3.535 3.535a1 1 0 0 1-1.415.001l-2.12-2.12a1 1 0 1 1 1.413-1.415zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 14 14" id="status_created" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><circle cx="7" cy="7" r="3.25"/></g></symbol><symbol viewBox="0 0 22 22" id="status_created_borderless" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="5.107"/></symbol><symbol viewBox="0 0 14 14" id="status_failed" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 5.969L5.599 4.568a.29.29 0 0 0-.413.004l-.614.614a.294.294 0 0 0-.004.413L5.968 7l-1.4 1.401a.29.29 0 0 0 .004.413l.614.614c.113.114.3.117.413.004L7 8.032l1.401 1.4a.29.29 0 0 0 .413-.004l.614-.614a.294.294 0 0 0 .004-.413L8.032 7l1.4-1.401a.29.29 0 0 0-.004-.413l-.614-.614a.294.294 0 0 0-.413-.004L7 5.968z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_failed_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 9.38L8.798 7.178a.455.455 0 0 0-.65.006l-.964.965a.462.462 0 0 0-.006.65L9.38 11l-2.202 2.202a.455.455 0 0 0 .006.65l.965.964a.462.462 0 0 0 .65.006L11 12.62l2.202 2.202a.455.455 0 0 0 .65-.006l.964-.965a.462.462 0 0 0 .006-.65L12.62 11l2.202-2.202a.455.455 0 0 0-.006-.65l-.965-.964a.462.462 0 0 0-.65-.006L11 9.38z"/></symbol><symbol viewBox="0 0 14 14" id="status_manual" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_manual_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 11.99v-1.98l-1.238-.206c-.068-.273-.206-.546-.412-.956l.756-1.025-1.444-1.435-1.03.752a3.686 3.686 0 0 0-.963-.41L12.03 5.5h-1.994l-.206 1.23c-.343.068-.618.205-.962.41l-1.031-.752-1.444 1.435.687 1.025c-.206.341-.275.615-.412.956L5.5 9.941v1.981l1.237.205c.07.342.207.615.413.957l-.688 1.025 1.444 1.434 1.032-.683c.274.137.618.274.962.41l.206 1.23h2.063l.206-1.23c.344-.068.619-.205.963-.41l1.03.752 1.444-1.435-.756-1.025c.207-.341.344-.683.413-.956l1.031-.205zM11 13.017c-1.169 0-2.063-.889-2.063-2.05 0-1.162.894-2.05 2.063-2.05s2.063.888 2.063 2.05c0 1.161-.894 2.05-2.063 2.05z"/></symbol><symbol viewBox="0 0 22 22" id="status_notfound_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0v-1.43a5.9 5.9 0 0 0 .827-.492z"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></symbol><symbol viewBox="0 0 14 14" id="status_open" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M7 9.219a2.218 2.218 0 1 0 0-4.436A2.218 2.218 0 0 0 7 9.22zm0 1.12a3.338 3.338 0 1 1 0-6.676 3.338 3.338 0 0 1 0 6.676z"/></symbol><symbol viewBox="0 0 14 14" id="status_pending" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M4.7 5.3c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H5c-.2 0-.3-.1-.3-.3V5.3m3 0c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H8c-.2 0-.3-.1-.3-.3V5.3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_pending_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M7.386 8.329c0-.315.157-.472.471-.472h1.414c.315 0 .472.157.472.472v5.342c0 .315-.157.472-.472.472H7.857c-.314 0-.471-.157-.471-.472V8.33m4.714 0c0-.315.157-.472.471-.472h1.415c.314 0 .471.157.471.472v5.342c0 .315-.157.472-.471.472H12.57c-.314 0-.471-.157-.471-.472V8.33"/></symbol><symbol viewBox="0 0 14 14" id="status_running" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 3c2.2 0 4 1.8 4 4s-1.8 4-4 4c-1.3 0-2.5-.7-3.3-1.7L7 7V3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_running_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.714c3.457 0 6.286 2.829 6.286 6.286 0 3.457-2.829 6.286-6.286 6.286-2.043 0-3.929-1.1-5.186-2.672L11 11V4.714"/></symbol><symbol viewBox="0 0 14 14" id="status_skipped" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></symbol><symbol viewBox="0 0 22 22" id="status_skipped_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></symbol><symbol viewBox="0 0 14 14" id="status_success" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_success_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.866 12.095l-1.95-1.95a.462.462 0 0 0-.647.01l-.964.964a.46.46 0 0 0-.01.646l3.013 3.014a.787.787 0 0 0 1.106.008l.425-.425 4.854-4.853a.462.462 0 0 0 .002-.659l-.964-.964a.468.468 0 0 0-.658.002l-4.207 4.207z"/></symbol><symbol viewBox="0 0 14 14" id="status_success_solid" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7zm6.278.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 14 14" id="status_warning" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6 3.5c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v4c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-4m0 6c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-1"/></g></symbol><symbol viewBox="0 0 22 22" id="status_warning_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.429 5.5c0-.471.314-.786.785-.786h1.572c.471 0 .785.315.785.786v6.286c0 .471-.314.785-.785.785h-1.572c-.471 0-.785-.314-.785-.785V5.5m0 9.429c0-.472.314-.786.785-.786h1.572c.471 0 .785.314.785.786V16.5c0 .471-.314.786-.785.786h-1.572c-.471 0-.785-.315-.785-.786v-1.571"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="terminal" xmlns="http://www.w3.org/2000/svg"><path d="M7 8a.997.997 0 0 1-.293.707l-1.414 1.414a1 1 0 1 1-1.414-1.414L4.586 8l-.707-.707a1 1 0 1 1 1.414-1.414l1.414 1.414A.997.997 0 0 1 7 8zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm5 7h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="thumbtack" xmlns="http://www.w3.org/2000/svg"><path d="M7.125 9h-2.19a.5.5 0 0 1-.417-.777L6 6V2L5.362.724A.5.5 0 0 1 5.809 0h4.382a.5.5 0 0 1 .447.724L10 2v4l1.482 2.223a.5.5 0 0 1-.416.777H8.875L8 16l-.875-7z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="unstaged" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.572 10.506c.867 1.42.375 3.247-1.098 4.082a3.184 3.184 0 0 1-1.57.412h-9.81C1.387 15 0 13.665 0 12.018a2.9 2.9 0 0 1 .427-1.512L5.332 2.47C6.2 1.05 8.096.577 9.57 1.412c.453.257.831.622 1.098 1.059l4.905 8.035zM8.89 3.479a1.014 1.014 0 0 0-.366-.353 1.053 1.053 0 0 0-1.412.353l-4.905 8.035a.967.967 0 0 0-.143.504c0 .549.462.994 1.032.994h9.81c.184 0 .364-.048.523-.137a.974.974 0 0 0 .366-1.361L8.889 3.479zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg> \ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.536 7.95L1.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 1 1-1.414-1.415L5.536 7.95zm7 0L8.293 3.707a1 1 0 0 1 1.414-1.414l4.95 4.95a.997.997 0 0 1 0 1.414l-4.95 4.95a1 1 0 0 1-1.414-1.415l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-down" xmlns="http://www.w3.org/2000/svg"><path d="M10.472 7.282a.862.862 0 0 1 1.26-.006c.357.364.357.958 0 1.285L8.627 11.73A.886.886 0 0 1 8 12a.849.849 0 0 1-.627-.27L4.275 8.561a.904.904 0 0 1-.013-1.285.861.861 0 0 1 1.26-.007l2.486 2.527z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4 12.5v-9A1.5 1.5 0 0 1 5.5 2h2.104c2.182 0 3.879.681 3.879 2.982 0 1.067-.517 2.227-1.374 2.595v.073C11.176 7.963 12 8.865 12 10.466 12 12.914 10.19 14 7.911 14H5.5A1.5 1.5 0 0 1 4 12.5zm2.376-5.696H7.49c1.164 0 1.665-.552 1.665-1.417 0-.94-.534-1.289-1.649-1.289h-1.13v2.706zm0 5.098h1.341c1.293 0 1.956-.515 1.956-1.62 0-1.049-.647-1.472-1.956-1.472H6.376v3.092z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="bookmark" xmlns="http://www.w3.org/2000/svg"><path d="M6.746 10.505a2 2 0 0 1 2.508 0L11 11.911V3H5v8.91l1.746-1.405zM5 1h6a2 2 0 0 1 2 2v10.999a1 1 0 0 1-1.627.779L8 12.064l-3.373 2.714A1 1 0 0 1 3 13.998V3a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="bullhorn" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.143 10H7V4H3a3 3 0 1 0 0 6h.143l.734 5.141a1 1 0 0 0 .99.859h1.556a.5.5 0 0 0 .495-.57L6.143 10zM8 4c1.034.02 2.039-.274 3.014-.883.727-.455 1.836-1.334 3.328-2.637A1 1 0 0 1 16 1.233v10.764a1 1 0 0 1-1.595.803c-1.658-1.227-2.788-1.992-3.392-2.294-.781-.39-1.785-.559-3.013-.506V4z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chart" xmlns="http://www.w3.org/2000/svg"><path d="M15 14a1 1 0 0 1 0 2H2a2 2 0 0 1-2-2V1a1 1 0 1 1 2 0v13h13zM3.142 8.735l2.502-2.561a.5.5 0 0 1 .714-.003L8 7.833l3.592-4.553a.5.5 0 0 1 .796.015l2.516 3.454a.5.5 0 0 1 .096.295V12.5a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5V9.085a.5.5 0 0 1 .142-.35z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 9 13" id="collapse"><path d="M.084.25C.01.18-.015.12.008.071.031.024.093 0 .194 0h8.521c.1 0 .162.024.185.072.023.048-.002.107-.075.177l-4.11 3.935a.372.372 0 0 1-.11.072h-.301a.508.508 0 0 1-.11-.072L.084.249zM.377 6.88a.364.364 0 0 1-.26-.105.334.334 0 0 1-.11-.25v-.709c0-.096.036-.179.11-.249a.364.364 0 0 1 .26-.105h8.15c.101 0 .188.035.261.105.074.07.11.153.11.25v.709c0 .096-.036.179-.11.249a.364.364 0 0 1-.26.105H.377zM.084 12.132c-.074.07-.099.129-.076.177.023.048.085.072.186.072h8.521c.1 0 .162-.024.185-.072.023-.048-.002-.107-.075-.177l-4.11-3.935a.372.372 0 0 0-.11-.072h-.301a.508.508 0 0 0-.11.072l-4.11 3.935z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="cut" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="2" y="7" fill-rule="evenodd" rx="1"/></symbol><symbol viewBox="0 0 16 16" id="dashboard" xmlns="http://www.w3.org/2000/svg"><path d="M7.709 10.021l.696-2.6a.5.5 0 0 1 .966.26l-.657 2.45A2 2 0 0 1 10 12H6a2 2 0 0 1 1.709-1.979zM0 8.9a8 8 0 0 1 15.998 0H16v3.6a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5V8.9zM14 9A6 6 0 1 0 2 9v3.5a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9zM3.5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm9 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-7-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm5 0a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 105 26" id="double-headed-arrow" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.018 11.089L15.138.614c1.23-.911 3.086-.795 4.147.26.461.46.715 1.045.715 1.651v20.95C20 24.869 18.684 26 17.06 26a3.238 3.238 0 0 1-1.921-.614L1.019 14.911C-.212 14-.347 12.405.714 11.35c.094-.094.195-.18.303-.261zm102.964 0c.108.08.21.167.303.26 1.061 1.056.925 2.65-.303 3.562l-14.12 10.475A3.238 3.238 0 0 1 87.94 26C86.316 26 85 24.87 85 23.475V2.525c0-.606.254-1.192.715-1.65 1.061-1.056 2.917-1.172 4.146-.26l14.12 10.474zM35 17a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm18 0a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 1600 1600" id="ellipsis_v" xmlns="http://www.w3.org/2000/svg"><path d="M1088 1248v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V224q0-40 28-68t68-28h192q40 0 68 28t28 68z"/></symbol><symbol viewBox="0 0 18 18" id="emoji_slightly_smiling_face" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369.721.721 0 0 1 .568.047.715.715 0 0 1 .37.445 2.91 2.91 0 0 0 1.084 1.518A2.93 2.93 0 0 0 9 12.75a2.93 2.93 0 0 0 1.775-.58 2.913 2.913 0 0 0 1.084-1.518.711.711 0 0 1 .375-.445.737.737 0 0 1 .575-.047c.195.063.34.186.433.37.094.183.11.372.047.568zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smile" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568zM14 6.37c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm-6.5 0c0 .398-.04.755-.513.755-.473 0-.498-.272-1.237-.272-.74 0-.74.215-1.165.215-.425 0-.585-.3-.585-.698 0-.397.17-.736.513-1.017.341-.281.754-.422 1.237-.422.483 0 .896.14 1.237.422.342.28.513.62.513 1.017zm9 2.63a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6A7.29 7.29 0 0 0 9 16.5a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39A7.29 7.29 0 0 0 16.5 9zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 18 18" id="emoji_smiley" xmlns="http://www.w3.org/2000/svg"><path d="M13.29 11.098a4.328 4.328 0 0 1-1.618 2.285c-.79.578-1.68.867-2.672.867-.992 0-1.883-.29-2.672-.867a4.328 4.328 0 0 1-1.617-2.285.721.721 0 0 1 .047-.569.715.715 0 0 1 .445-.369c.195-.062 7.41-.062 7.606 0 .195.063.34.186.433.37.094.183.11.372.047.568h.001zM7.5 6c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 6 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 4.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 6 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm6 0c0 .414-.146.768-.44 1.06A1.44 1.44 0 0 1 12 7.5a1.44 1.44 0 0 1-1.06-.44A1.445 1.445 0 0 1 10.5 6c0-.414.146-.768.44-1.06A1.44 1.44 0 0 1 12 4.5c.414 0 .768.146 1.06.44.294.292.44.646.44 1.06zm3 3a7.29 7.29 0 0 0-.598-2.912 7.574 7.574 0 0 0-1.6-2.39 7.574 7.574 0 0 0-2.39-1.6A7.29 7.29 0 0 0 9 1.5a7.29 7.29 0 0 0-2.912.598 7.574 7.574 0 0 0-2.39 1.6 7.574 7.574 0 0 0-1.6 2.39A7.29 7.29 0 0 0 1.5 9c0 1.016.2 1.986.598 2.912a7.574 7.574 0 0 0 1.6 2.39 7.574 7.574 0 0 0 2.39 1.6c.92.397 1.91.6 2.912.598a7.29 7.29 0 0 0 2.912-.598 7.574 7.574 0 0 0 2.39-1.6 7.574 7.574 0 0 0 1.6-2.39c.397-.92.6-1.91.598-2.912zM18 9a8.804 8.804 0 0 1-1.207 4.518 8.96 8.96 0 0 1-3.275 3.275A8.804 8.804 0 0 1 9 18a8.804 8.804 0 0 1-4.518-1.207 8.96 8.96 0 0 1-3.275-3.275A8.804 8.804 0 0 1 0 9c0-1.633.402-3.139 1.207-4.518a8.96 8.96 0 0 1 3.275-3.275A8.804 8.804 0 0 1 9 0c1.633 0 3.139.402 4.518 1.207a8.96 8.96 0 0 1 3.275 3.275A8.804 8.804 0 0 1 18 9z"/></symbol><symbol viewBox="0 0 16 16" id="epic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.985 8.044l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637A2 2 0 0 0 1.618 9h11.661a2 2 0 0 0 1.706-.956zm0 3l-.757 2.272a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l.318-.637a2 2 0 0 0 .576.084h11.661a2 2 0 0 0 1.706-.956zM3.618 2h10.995a1 1 0 0 1 .948 1.316l-1.333 4a1 1 0 0 1-.949.684H1.618a1 1 0 0 1-.894-1.447l2-4A1 1 0 0 1 3.618 2zm-.382 4h9.322l.667-2H4.236l-1 2z"/></symbol><symbol viewBox="0 0 16 16" id="external-link" xmlns="http://www.w3.org/2000/svg"><path d="M13.121 4.177l-4.95 4.95a1 1 0 1 1-1.414-1.414l4.95-4.95-1.386-1.386a.5.5 0 0 1 .299-.85l4.709-.524a.5.5 0 0 1 .552.552l-.523 4.71a.5.5 0 0 1-.851.297l-1.386-1.385zM12 8.884a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3v-8a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-4z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-addition" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-o" xmlns="http://www.w3.org/2000/svg"><path d="M13 5l-4.365-.005a2 2 0 0 1-1.882-1.33A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zm0-2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="folder-open" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.59 5.464a2.998 2.998 0 0 1 1.096 3.845l-1.666 3.436A4 4 0 0 1 10.46 15H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.558a2 2 0 0 1 1.898 1.368l.21.632h4.973a2 2 0 0 1 2 2 2 2 0 0 1-.027.329l-.023.135zM5.285 7a1 1 0 0 0-.9.564l-1.939 4a1 1 0 0 0 .9 1.436h7.074a2 2 0 0 0 1.8-1.128l1.665-3.436a1 1 0 0 0-.9-1.436h-7.7z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M9 13h3v-3H4v3h3v-1a1 1 0 0 1 2 0v1zm5-3v3.659c0 .729-.657 1.341-1.5 1.341h-9c-.843 0-1.5-.612-1.5-1.341V10h-.88C.502 10 0 9.486 0 8.853c0-.307.12-.601.333-.816l6.405-6.463a1.56 1.56 0 0 1 2.374-.052L15.66 8.03c.444.441.455 1.167.024 1.622a1.108 1.108 0 0 1-.804.348H14zM7.95 3.273l-4.595 4.64h9.264l-4.67-4.64z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="hourglass" xmlns="http://www.w3.org/2000/svg"><path d="M10.331 4.889A2.988 2.988 0 0 0 11 3V2H5v1c0 .362.064.709.182 1.03l5.15.859zM3 14v-1c0-1.78.93-3.342 2.33-4.228.447-.327.67-.582.67-.764 0-.19-.242-.46-.725-.815A4.996 4.996 0 0 1 3 3V2H2a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2h-1v1a4.997 4.997 0 0 1-2.39 4.266c-.407.3-.61.545-.61.734 0 .19.203.434.61.734A4.997 4.997 0 0 1 13 13v1h1a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2h1zm8 0v-1a3 3 0 0 0-6 0v1h6z"/></symbol><symbol viewBox="0 0 38 38" id="image-comment-dark" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#1F78D1"/><path fill="#FFF" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 38 38" id="image-comment-light" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></symbol><symbol viewBox="0 0 16 16" id="import" xmlns="http://www.w3.org/2000/svg"><path d="M9 8h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0L5.6 8.8A.5.5 0 0 1 6 8h1V1a1 1 0 1 1 2 0v7zM0 8a1 1 0 1 1 2 0 6 6 0 1 0 12 0 1 1 0 0 1 2 0A8 8 0 1 1 0 8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-external" xmlns="http://www.w3.org/2000/svg"><path d="M11 4a5.99 5.99 0 0 0-2 .341V3a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2.528a6.003 6.003 0 0 0 2.705 1.736A2.99 2.99 0 0 1 8 16H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3v1zM8.212 8.97l-.568-.876A.25.25 0 0 1 7.66 7.8l.404-.5a.25.25 0 0 1 .284-.076l.938.36c.256-.182.543-.325.85-.42l.323-.988a.25.25 0 0 1 .237-.173h.643a.25.25 0 0 1 .238.173l.321.989c.308.094.595.237.852.418l.937-.359a.25.25 0 0 1 .284.076l.404.5a.25.25 0 0 1 .016.293l-.568.875c.113.297.18.616.192.95l.9.54a.25.25 0 0 1 .114.27l-.145.627a.25.25 0 0 1-.221.192l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.282a.25.25 0 0 1-.29-.051l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.905a.25.25 0 0 1-.29.05l-.577-.281a.25.25 0 0 1-.138-.26L9 12.254a3.015 3.015 0 0 1-.512-.607l-1.114-.098a.25.25 0 0 1-.222-.192l-.145-.627a.25.25 0 0 1 .115-.27l.899-.54c.012-.334.08-.653.192-.95zm2.806 2.034a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="italic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.5 12l2-8H6a1 1 0 1 1 0-2h6a1 1 0 0 1 0 2h-1.5l-2 8H10a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1.5z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="menu" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1.143 2h13.714C15.488 2 16 2.448 16 3s-.512 1-1.143 1H1.143C.512 4 0 3.552 0 3s.512-1 1.143-1zm0 5h13.714C15.488 7 16 7.448 16 8s-.512 1-1.143 1H1.143C.512 9 0 8.552 0 8s.512-1 1.143-1zm0 5h13.714c.631 0 1.143.448 1.143 1s-.512 1-1.143 1H1.143C.512 14 0 13.552 0 13s.512-1 1.143-1z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pencil-square" xmlns="http://www.w3.org/2000/svg"><path d="M12 9a1 1 0 0 1 2 0v4a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h4a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V9zm.778-7.179l1.414 1.415-6.476 6.476a1 1 0 0 1-.498.27l-1.51.325.323-1.512a1 1 0 0 1 .27-.497l6.477-6.477zM15.607.407a1 1 0 0 1 0 1.414l-.708.707-1.414-1.414.707-.707a1 1 0 0 1 1.415 0z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7H2a1 1 0 1 0 0 2h5v5a1 1 0 0 0 2 0V9h5a1 1 0 0 0 0-2H9V2a1 1 0 1 0-2 0v5z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="podcast" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862a1 1 0 0 1-.785 1.177A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-1-1 1 1 0 0 1 .02-.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 7.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4.464 2.464A1 1 0 0 1 5.88 3.88a3 3 0 0 0 0 4.242 1 1 0 0 1-1.415 1.415 5 5 0 0 1 0-7.072zm7.072 7.072A1 1 0 0 1 10.12 8.12a3 3 0 0 0 0-4.242 1 1 0 0 1 1.415-1.415 5 5 0 0 1 0 7.072zM2.343.343a1 1 0 1 1 1.414 1.414 6 6 0 0 0 0 8.486 1 1 0 1 1-1.414 1.414 8 8 0 0 1 0-11.314zm11.314 11.314a1 1 0 1 1-1.414-1.414 6 6 0 0 0 0-8.486A1 1 0 0 1 13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.625 4.423A4.897 4.897 0 0 1 8.079 3c2.73 0 4.944 2.239 4.944 5s-2.214 5-4.944 5c-1.41 0-2.723-.6-3.655-1.633a.98.98 0 0 0-1.397-.066 1.008 1.008 0 0 0-.064 1.413A6.87 6.87 0 0 0 8.079 15C11.9 15 15 11.866 15 8s-3.099-7-6.921-7A6.866 6.866 0 0 0 3.08 3.158L1.833 2.137a.49.49 0 0 0-.695.074.504.504 0 0 0-.11.311L1 7.26a.497.497 0 0 0 .6.492l4.576-1.013a.5.5 0 0 0 .206-.877L4.625 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.375 4.423A4.897 4.897 0 0 0 7.921 3c-2.73 0-4.944 2.239-4.944 5s2.214 5 4.944 5c1.41 0 2.723-.6 3.655-1.633a.98.98 0 0 1 1.397-.066c.403.373.432 1.005.064 1.413A6.87 6.87 0 0 1 7.921 15C4.1 15 1 11.866 1 8s3.099-7 6.921-7c1.915 0 3.706.792 4.999 2.158l1.247-1.021a.49.49 0 0 1 .695.074c.07.088.11.198.11.311L15 7.26a.497.497 0 0 1-.6.492L9.824 6.739a.5.5 0 0 1-.206-.877l1.757-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.114 6.958a4 4 0 0 0 5.283 4.775 1 1 0 1 1 .712 1.87A6 6 0 0 1 2.182 6.44l-.741-.2a.5.5 0 0 1-.12-.915l2.195-1.268a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.712-1.87 6 6 0 0 1 7.927 7.162l.742.2a.5.5 0 0 1 .12.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 12 16" id="scroll_down" xmlns="http://www.w3.org/2000/svg"><path class="fbfirst-triangle" d="M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043a.51.51 0 0 0 .321-.105c.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"/><path class="fbsecond-triangle" d="M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"/><path class="fbthird-triangle" d="M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91a.458.458 0 0 1-.136.09h-.37a.626.626 0 0 1-.136-.09"/></symbol><symbol viewBox="0 0 12 16" id="scroll_up" xmlns="http://www.w3.org/2000/svg"><path d="M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043a.51.51 0 0 1 .321.105c.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09a.458.458 0 0 0-.136-.09h-.37a.626.626 0 0 0-.136.09"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8c1.657 0 3 1.373 3 3.067v7.346c0 1.065-.54 2.053-1.426 2.611l-4 2.52a2.944 2.944 0 0 1-3.148 0l-4-2.52A3.083 3.083 0 0 1 1 10.414V3.066C1 1.373 2.343 0 4 0zm0 2.045c-.552 0-1 .457-1 1.022v7.346c0 .355.18.685.475.87l4 2.52a.981.981 0 0 0 1.05 0l4-2.52c.295-.185.475-.515.475-.87V3.067c0-.565-.448-1.022-1-1.022H4zm0 1.533c0-.282.224-.511.5-.511h4V12.1a.52.52 0 0 1-.069.258.494.494 0 0 1-.684.183l-3.5-2.098a.513.513 0 0 1-.247-.44V3.577z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 14 14" id="spinner" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle cx="7" cy="7" r="6" stroke="#000" stroke-opacity=".1" stroke-width="2"/><path fill="#000" fill-opacity=".1" fill-rule="nonzero" d="M7 0a7 7 0 0 1 7 7h-2a5 5 0 0 0-5-5V0z"/></g></symbol><symbol viewBox="0 0 16 16" id="staged" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3h4a1 1 0 1 1 0 2H2a1 1 0 1 1 0-2zm9 6a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM2 7h4a1 1 0 1 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 14 14" id="status_canceled" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M5.2 3.8l4.9 4.9c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L3.8 5.2c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0"/></g></symbol><symbol viewBox="0 0 22 22" id="status_canceled_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M8.171 5.971l7.7 7.7a.76.76 0 0 1 0 1.1l-1.1 1.1a.76.76 0 0 1-1.1 0l-7.7-7.7a.76.76 0 0 1 0-1.1l1.1-1.1a.76.76 0 0 1 1.1 0"/></symbol><symbol viewBox="0 0 16 16" id="status_closed" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.83a1 1 0 0 1 1.414 1.416l-3.535 3.535a1 1 0 0 1-1.415.001l-2.12-2.12a1 1 0 1 1 1.413-1.415zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 14 14" id="status_created" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><circle cx="7" cy="7" r="3.25"/></g></symbol><symbol viewBox="0 0 22 22" id="status_created_borderless" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="5.107"/></symbol><symbol viewBox="0 0 14 14" id="status_failed" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 5.969L5.599 4.568a.29.29 0 0 0-.413.004l-.614.614a.294.294 0 0 0-.004.413L5.968 7l-1.4 1.401a.29.29 0 0 0 .004.413l.614.614c.113.114.3.117.413.004L7 8.032l1.401 1.4a.29.29 0 0 0 .413-.004l.614-.614a.294.294 0 0 0 .004-.413L8.032 7l1.4-1.401a.29.29 0 0 0-.004-.413l-.614-.614a.294.294 0 0 0-.413-.004L7 5.968z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_failed_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 9.38L8.798 7.178a.455.455 0 0 0-.65.006l-.964.965a.462.462 0 0 0-.006.65L9.38 11l-2.202 2.202a.455.455 0 0 0 .006.65l.965.964a.462.462 0 0 0 .65.006L11 12.62l2.202 2.202a.455.455 0 0 0 .65-.006l.964-.965a.462.462 0 0 0 .006-.65L12.62 11l2.202-2.202a.455.455 0 0 0-.006-.65l-.965-.964a.462.462 0 0 0-.65-.006L11 9.38z"/></symbol><symbol viewBox="0 0 14 14" id="status_manual" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_manual_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 11.99v-1.98l-1.238-.206c-.068-.273-.206-.546-.412-.956l.756-1.025-1.444-1.435-1.03.752a3.686 3.686 0 0 0-.963-.41L12.03 5.5h-1.994l-.206 1.23c-.343.068-.618.205-.962.41l-1.031-.752-1.444 1.435.687 1.025c-.206.341-.275.615-.412.956L5.5 9.941v1.981l1.237.205c.07.342.207.615.413.957l-.688 1.025 1.444 1.434 1.032-.683c.274.137.618.274.962.41l.206 1.23h2.063l.206-1.23c.344-.068.619-.205.963-.41l1.03.752 1.444-1.435-.756-1.025c.207-.341.344-.683.413-.956l1.031-.205zM11 13.017c-1.169 0-2.063-.889-2.063-2.05 0-1.162.894-2.05 2.063-2.05s2.063.888 2.063 2.05c0 1.161-.894 2.05-2.063 2.05z"/></symbol><symbol viewBox="0 0 14 14" id="status_notfound" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M8.16 7.184c.519-.37.904-.857 1.07-1.477.384-1.427-.619-2.897-2.246-2.897-.732 0-1.327.26-1.766.692a2.163 2.163 0 0 0-.509.743.75.75 0 0 0 1.4.54.78.78 0 0 1 .16-.213c.168-.165.39-.262.715-.262.597 0 .936.496.798 1.007-.067.249-.235.462-.492.644-.231.165-.47.264-.601.3a.75.75 0 0 0-.556.724v1.421a.75.75 0 0 0 1.5 0v-.909a3.74 3.74 0 0 0 .526-.313z"/><ellipse cx="6.889" cy="10.634" rx="1" ry="1"/></symbol><symbol viewBox="0 0 22 22" id="status_notfound_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0v-1.43a5.9 5.9 0 0 0 .827-.492z"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></symbol><symbol viewBox="0 0 14 14" id="status_open" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M7 9.219a2.218 2.218 0 1 0 0-4.436A2.218 2.218 0 0 0 7 9.22zm0 1.12a3.338 3.338 0 1 1 0-6.676 3.338 3.338 0 0 1 0 6.676z"/></symbol><symbol viewBox="0 0 14 14" id="status_pending" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M4.7 5.3c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H5c-.2 0-.3-.1-.3-.3V5.3m3 0c0-.2.1-.3.3-.3h.9c.2 0 .3.1.3.3v3.4c0 .2-.1.3-.3.3H8c-.2 0-.3-.1-.3-.3V5.3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_pending_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M7.386 8.329c0-.315.157-.472.471-.472h1.414c.315 0 .472.157.472.472v5.342c0 .315-.157.472-.472.472H7.857c-.314 0-.471-.157-.471-.472V8.33m4.714 0c0-.315.157-.472.471-.472h1.415c.314 0 .471.157.471.472v5.342c0 .315-.157.472-.471.472H12.57c-.314 0-.471-.157-.471-.472V8.33"/></symbol><symbol viewBox="0 0 14 14" id="status_running" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7 3c2.2 0 4 1.8 4 4s-1.8 4-4 4c-1.3 0-2.5-.7-3.3-1.7L7 7V3"/></g></symbol><symbol viewBox="0 0 22 22" id="status_running_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.714c3.457 0 6.286 2.829 6.286 6.286 0 3.457-2.829 6.286-6.286 6.286-2.043 0-3.929-1.1-5.186-2.672L11 11V4.714"/></symbol><symbol viewBox="0 0 14 14" id="status_skipped" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></symbol><symbol viewBox="0 0 22 22" id="status_skipped_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></symbol><symbol viewBox="0 0 14 14" id="status_success" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></symbol><symbol viewBox="0 0 22 22" id="status_success_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.866 12.095l-1.95-1.95a.462.462 0 0 0-.647.01l-.964.964a.46.46 0 0 0-.01.646l3.013 3.014a.787.787 0 0 0 1.106.008l.425-.425 4.854-4.853a.462.462 0 0 0 .002-.659l-.964-.964a.468.468 0 0 0-.658.002l-4.207 4.207z"/></symbol><symbol viewBox="0 0 14 14" id="status_success_solid" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7zm6.278.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 14 14" id="status_warning" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6 3.5c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v4c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-4m0 6c0-.3.2-.5.5-.5h1c.3 0 .5.2.5.5v1c0 .3-.2.5-.5.5h-1c-.3 0-.5-.2-.5-.5v-1"/></g></symbol><symbol viewBox="0 0 22 22" id="status_warning_borderless" xmlns="http://www.w3.org/2000/svg"><path d="M9.429 5.5c0-.471.314-.786.785-.786h1.572c.471 0 .785.315.785.786v6.286c0 .471-.314.785-.785.785h-1.572c-.471 0-.785-.314-.785-.785V5.5m0 9.429c0-.472.314-.786.785-.786h1.572c.471 0 .785.314.785.786V16.5c0 .471-.314.786-.785.786h-1.572c-.471 0-.785-.315-.785-.786v-1.571"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="terminal" xmlns="http://www.w3.org/2000/svg"><path d="M7 8a.997.997 0 0 1-.293.707l-1.414 1.414a1 1 0 1 1-1.414-1.414L4.586 8l-.707-.707a1 1 0 1 1 1.414-1.414l1.414 1.414A.997.997 0 0 1 7 8zM4 0h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm5 7h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thumb-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="thumbtack" xmlns="http://www.w3.org/2000/svg"><path d="M7.125 9h-2.19a.5.5 0 0 1-.417-.777L6 6V2L5.362.724A.5.5 0 0 1 5.809 0h4.382a.5.5 0 0 1 .447.724L10 2v4l1.482 2.223a.5.5 0 0 1-.416.777H8.875L8 16l-.875-7z" fill-rule="evenodd"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="unstaged" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm0 4h12a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.572 10.506c.867 1.42.375 3.247-1.098 4.082a3.184 3.184 0 0 1-1.57.412h-9.81C1.387 15 0 13.665 0 12.018a2.9 2.9 0 0 1 .427-1.512L5.332 2.47C6.2 1.05 8.096.577 9.57 1.412c.453.257.831.622 1.098 1.059l4.905 8.035zM8.89 3.479a1.014 1.014 0 0 0-.366-.353 1.053 1.053 0 0 0-1.412.353l-4.905 8.035a.967.967 0 0 0-.143.504c0 .549.462.994 1.032.994h9.81c.184 0 .364-.048.523-.137a.974.974 0 0 0 .366-1.361L8.889 3.479zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg> \ No newline at end of file
diff --git a/app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg b/app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg
new file mode 100644
index 00000000000..06d73941c33
--- /dev/null
+++ b/app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 80 80"><g fill="none" fill-rule="evenodd"><path fill="#EEE" d="M44.242 59.348c-3.7 1.576-7.3 1.994-10.902.84a7.002 7.002 0 0 1-9.085-.699l-4.243-4.243a7 7 0 0 1-.238-9.649c-.701-3.024-.419-6.083.646-9.206l-6.287-2.426a5.6 5.6 0 0 1-2.274-8.824l8.233-9.811a5.6 5.6 0 0 1 6.306-1.625l8.045 3.105c.772-.797 1.564-1.6 2.374-2.41C44.841 6.376 55.265 2.135 68.09 1.677a10 10 0 0 1 1.119.023c5.507.42 9.63 5.226 9.209 10.733-.935 12.225-5.373 22.309-13.315 30.25a410.76 410.76 0 0 1-1.661 1.653l3.247 8.412a5.6 5.6 0 0 1-1.625 6.306l-9.81 8.233a5.6 5.6 0 0 1-8.825-2.274l-2.186-5.665zm-22.92-26.923l10.406-12.402-6.822-2.633a1.6 1.6 0 0 0-1.801.464l-8.233 9.811a1.6 1.6 0 0 0 .65 2.521l5.8 2.239zm26.646 25.4l2.239 5.8a1.6 1.6 0 0 0 2.521.649l9.81-8.232a1.6 1.6 0 0 0 .465-1.802l-2.633-6.822-12.402 10.406zm-19.69-5.627c8.751 8.752 16.065 5.587 33.995-12.343 7.25-7.25 11.292-16.433 12.155-27.727a6 6 0 0 0-6.196-6.454c-11.846.423-21.303 4.271-28.586 11.554-17.03 17.03-20.414 25.924-11.368 34.97z"/><path fill="#FDC4A8" fill-rule="nonzero" d="M52.54 28.376a4 4 0 1 0 5.656-5.657 4 4 0 0 0-5.657 5.657zm-2.83 2.829A8 8 0 1 1 61.025 19.89a8 8 0 0 1-11.313 11.314z"/><path fill="#FEE1D3" d="M15.063 54.54a2 2 0 0 1 0 2.828L3.749 68.68A2 2 0 1 1 .92 65.853l11.314-11.314a2 2 0 0 1 2.829 0zm9.899 9.899a2 2 0 0 1 0 2.828l-8.485 8.485a2 2 0 1 1-2.829-2.828l8.486-8.485a2 2 0 0 1 2.828 0z"/><path fill="#FDC4A8" d="M20.012 59.489a2 2 0 0 1 0 2.828L4.456 77.874a2 2 0 0 1-2.829-2.829L17.184 59.49a2 2 0 0 1 2.828 0z"/></g></svg> \ No newline at end of file
diff --git a/app/assets/images/illustrations/multi-editor_no_changes_empty.svg b/app/assets/images/illustrations/multi-editor_no_changes_empty.svg
new file mode 100644
index 00000000000..ebeea1f3dd9
--- /dev/null
+++ b/app/assets/images/illustrations/multi-editor_no_changes_empty.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 80 80"><g fill="none" fill-rule="evenodd" transform="translate(7 3)"><path fill="#EEE" fill-rule="nonzero" d="M54 18a2 2 0 1 1 0-4h4c.843 0 1.675.105 2.48.31a2 2 0 1 1-.99 3.876A6.015 6.015 0 0 0 58 18h-4zm9.735 4.228a2 2 0 0 1 3.822-1.18A10 10 0 0 1 68 24v3.513a2 2 0 1 1-4 0V24c0-.61-.09-1.204-.265-1.772zM64 35.513a2 2 0 1 1 4 0v6a2 2 0 1 1-4 0v-6zm0 14a2 2 0 1 1 4 0v6a2 2 0 1 1-4 0v-6zm0 14a2 2 0 1 1 4 0V66a9.97 9.97 0 0 1-.963 4.286 2 2 0 1 1-3.613-1.716A5.969 5.969 0 0 0 64 66v-2.487zm-5.255 8.441a2 2 0 1 1 .49 3.97c-.401.05-.806.075-1.218.076h-5.042a2 2 0 1 1 0-4h5.038c.246 0 .49-.016.732-.046zM44.975 72a2 2 0 1 1 0 4h-6a2 2 0 1 1 0-4h6zm-14 0a2 2 0 1 1 0 4H26c-.429 0-.855-.027-1.276-.08a2 2 0 0 1 .506-3.969c.254.033.51.049.77.049h4.975zm-10.438-3.514a2 2 0 1 1-3.64 1.66A9.97 9.97 0 0 1 16 66v-2.538a2 2 0 1 1 4 0V66c0 .871.185 1.713.537 2.486zM8 2a6 6 0 0 0-6 6v42a6 6 0 0 0 6 6h32a6 6 0 0 0 6-6V8a6 6 0 0 0-6-6H8zm0-4h32c5.523 0 10 4.477 10 10v42c0 5.523-4.477 10-10 10H8C2.477 60-2 55.523-2 50V8C-2 2.477 2.477-2 8-2z"/><rect width="10" height="4" x="8" y="16" fill="#EFEDF8" rx="2"/><rect width="10" height="4" x="21" y="16" fill="#6B4FBB" rx="2"/><rect width="10" height="4" x="8" y="32" fill="#E1DBF1" rx="2"/><rect width="6" height="4" x="34" y="16" fill="#EFEDF8" rx="2"/><rect width="6" height="4" x="8" y="24" fill="#6B4FBB" rx="2"/><rect width="6" height="4" x="17" y="24" fill="#EFEDF8" rx="2"/><rect width="6" height="4" x="21" y="32" fill="#6B4FBB" rx="2"/><rect width="6" height="4" x="8" y="40" fill="#6B4FBB" rx="2"/><rect width="6" height="4" x="17" y="40" fill="#EFEDF8" rx="2"/><rect width="6" height="4" x="26" y="40" fill="#C3B8E3" rx="2"/><rect width="10" height="4" x="26" y="24" fill="#C3B8E3" rx="2"/></g></svg> \ No newline at end of file
diff --git a/app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg b/app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg
new file mode 100644
index 00000000000..08321ef526b
--- /dev/null
+++ b/app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 80 80"><g fill="none" fill-rule="evenodd" transform="translate(0 3)"><path fill="#EEE" fill-rule="nonzero" d="M40.843 5.864a2 2 0 1 1 .348-3.985l5.977.523a2 2 0 1 1-.348 3.985l-5.977-.523zm13.946 1.22a2 2 0 1 1 .349-3.985l5.977.523a2 2 0 1 1-.348 3.985l-5.978-.523zm13.947 1.22a2 2 0 1 1 .349-3.984 11.952 11.952 0 0 1 6.655 2.75 2 2 0 1 1-2.569 3.066 7.953 7.953 0 0 0-4.435-1.832zm7.28 7.357a2 2 0 1 1 3.99-.301c.048.639.045 1.283-.01 1.934l-.385 4.4a2 2 0 1 1-3.985-.349l.384-4.395c.037-.433.039-.863.007-1.29zm-1.088 13.654a2 2 0 0 1 3.985.348l-.523 5.978a2 2 0 1 1-3.984-.349l.522-5.977zm-1.22 13.947a2 2 0 1 1 3.985.348l-.523 5.977a2 2 0 1 1-3.985-.348l.523-5.977zM72.305 56.7a2 2 0 0 1 3.79 1.282 11.995 11.995 0 0 1-4.253 5.81 2 2 0 0 1-2.373-3.22 7.996 7.996 0 0 0 2.836-3.872zm-9.054 5.33a2 2 0 1 1-.349 3.985l-5.977-.522a2 2 0 1 1 .349-3.985l5.977.523zM32.793 10.675a2 2 0 1 1-3.675-1.579 12.02 12.02 0 0 1 4.696-5.456 2 2 0 0 1 2.112 3.397 8.02 8.02 0 0 0-3.133 3.638z"/><rect width="48" height="58" x="2" y="14" fill="#FAFAFA" rx="10"/><path fill="#EEE" fill-rule="nonzero" d="M12 16a8 8 0 0 0-8 8v38a8 8 0 0 0 8 8h28a8 8 0 0 0 8-8V24a8 8 0 0 0-8-8H12zm0-4h28c6.627 0 12 5.373 12 12v38c0 6.627-5.373 12-12 12H12C5.373 74 0 68.627 0 62V24c0-6.627 5.373-12 12-12z"/><rect width="24" height="4" x="11" y="30" fill="#E5E5E5" rx="2"/><rect width="30" height="4" x="11" y="41" fill="#E5E5E5" rx="2"/><rect width="20" height="4" x="11" y="52" fill="#E5E5E5" rx="2"/></g></svg> \ No newline at end of file
diff --git a/app/assets/images/illustrations/pending_job_empty.svg b/app/assets/images/illustrations/pending_job_empty.svg
new file mode 100644
index 00000000000..8de695afa18
--- /dev/null
+++ b/app/assets/images/illustrations/pending_job_empty.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="430" height="200" viewBox="0 0 430 200"><g fill="none" fill-rule="evenodd"><g transform="translate(138 65)"><path fill="#E5E5E5" fill-rule="nonzero" d="M35 70a2 2 0 1 1 0-4c2.542 0 5.042-.305 7.463-.904a2 2 0 1 1 .96 3.884A35.075 35.075 0 0 1 35 70zm18.21-5.105a2 2 0 1 1-2.083-3.414 31.143 31.143 0 0 0 5.896-4.664 2 2 0 1 1 2.842 2.815 35.143 35.143 0 0 1-6.654 5.263zM66.106 51.06a2 2 0 0 1-3.552-1.838 30.77 30.77 0 0 0 2.612-7.042 2 2 0 1 1 3.892.922 34.77 34.77 0 0 1-2.952 7.958zm3.816-18.433a2 2 0 1 1-3.991.268 30.873 30.873 0 0 0-1.407-7.38 2 2 0 0 1 3.808-1.223 34.873 34.873 0 0 1 1.59 8.335zm-6.346-17.842a2 2 0 0 1-3.264 2.312 31.188 31.188 0 0 0-5.054-5.564 2 2 0 0 1 2.615-3.027 35.188 35.188 0 0 1 5.703 6.279zM48.895 2.867a2 2 0 0 1-1.59 3.67 30.758 30.758 0 0 0-7.206-2.12 2 2 0 1 1 .653-3.946 34.758 34.758 0 0 1 8.143 2.396zM30.263.318a2 2 0 0 1 .537 3.964c-2.505.339-4.94.98-7.266 1.907a2 2 0 1 1-1.48-3.716A34.774 34.774 0 0 1 30.263.318zM12.907 7.853a2 2 0 0 1 2.527 3.1 31.196 31.196 0 0 0-5.213 5.416 2 2 0 0 1-3.196-2.406 35.196 35.196 0 0 1 5.882-6.11zM1.99 23.343a2 2 0 0 1 3.772 1.331 30.82 30.82 0 0 0-1.619 7.337 2 2 0 1 1-3.982-.38 34.82 34.82 0 0 1 1.829-8.289zM.719 42.086a2 2 0 1 1 3.917-.806 30.757 30.757 0 0 0 2.4 7.118 2 2 0 1 1-3.605 1.73 34.757 34.757 0 0 1-2.713-8.042zM9.393 58.86a2 2 0 0 1 2.926-2.728 31.167 31.167 0 0 0 5.751 4.841 2 2 0 1 1-2.187 3.349 35.167 35.167 0 0 1-6.49-5.462zm16.245 9.873a2 2 0 1 1 1.067-3.855 30.979 30.979 0 0 0 7.434 1.11 2 2 0 1 1-.11 3.998 34.979 34.979 0 0 1-8.391-1.253z"/><circle cx="35" cy="35" r="16" stroke="#E1DBF1" stroke-width="4"/><path fill="#6B4FBB" d="M37 33h5a2 2 0 1 1 0 4h-7a2 2 0 0 1-2-2v-8a2 2 0 1 1 4 0v6z"/></g><g transform="translate(247 30)"><rect width="116" height="135" y="5" fill="#F9F9F9" rx="10"/><rect width="116" height="134" x="5" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="10"/><g transform="translate(23 23)"><rect width="16" height="4" fill="#E1DBF1" rx="2"/><rect width="16" height="4" x="32" y="12" fill="#E1DBF1" rx="2"/><rect width="16" height="4" x="44" fill="#EEE" rx="2"/><rect width="16" height="4" x="12" y="24" fill="#E1DBF1" rx="2"/><rect width="16" height="4" x="64" y="36" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="20" fill="#FEE1D3" rx="2"/><rect width="8" height="4" x="32" y="36" fill="#FC6D26" rx="2"/><rect width="8" height="4" x="52" y="12" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="64" fill="#FEF0E8" rx="2"/><rect width="12" height="4" x="16" y="48" fill="#E1DBF1" rx="2"/><rect width="8" height="4" x="44" y="36" fill="#FC6D26" rx="2"/><rect width="4" height="4" x="56" y="36" fill="#E1DBF1" rx="2"/><rect width="4" height="4" x="64" y="60" fill="#E1DBF1" rx="2"/><rect width="4" height="4" x="72" y="60" fill="#FC6D26" rx="2"/><rect width="8" height="4" x="32" fill="#FC6D26" rx="2"/><rect width="28" height="4" y="36" fill="#EEE" rx="2"/><rect width="28" height="4" x="44" y="48" fill="#EEE" rx="2"/><rect width="28" height="4" x="32" y="60" fill="#EFEDF8" rx="2"/><rect width="28" height="4" y="12" fill="#6B4FBB" rx="2"/><rect width="28" height="4" x="32" y="24" fill="#C3B8E3" rx="2"/><rect width="8" height="4" y="24" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="32" y="48" fill="#6B4FBB" rx="2"/><rect width="12" height="4" y="48" fill="#FC6D26" rx="2"/><rect width="12" height="4" y="60" fill="#FEF0E8" rx="2"/><rect width="12" height="4" x="16" y="60" fill="#FEF0E8" rx="2"/></g><g transform="translate(23 95)"><rect width="16" height="4" fill="#EFEDF8" rx="2"/><rect width="16" height="4" x="18" y="12" fill="#FC6D26" rx="2"/><rect width="16" height="4" x="44" fill="#6B4FBB" rx="2"/><rect width="8" height="4" x="20" fill="#FEE1D3" rx="2"/><rect width="8" height="4" x="38" y="12" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="64" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="32" fill="#FC6D26" rx="2"/><rect width="14" height="4" y="12" fill="#EEE" rx="2"/></g></g><path fill="#FC6D26" fill-rule="nonzero" d="M81 119c-10.493 0-19-8.507-19-19s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19zm0-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 15zm-5-20a2 2 0 0 1 2 2v6a2 2 0 1 1-4 0v-6a2 2 0 0 1 2-2zm10 0a2 2 0 0 1 2 2v6a2 2 0 1 1-4 0v-6a2 2 0 0 1 2-2z"/><path fill="#E5E5E5" fill-rule="nonzero" d="M108 102c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2zm14 0c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2zm93 0c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2zm14 0c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2z"/></g></svg> \ No newline at end of file
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 21d8c790e90..7cb81bf4d5b 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -178,7 +178,7 @@ const Api = {
issueTemplate(namespacePath, projectPath, key, type, callback) {
const url = Api.buildUrl(Api.issuableTemplatePath)
- .replace(':key', key)
+ .replace(':key', encodeURIComponent(key))
.replace(':type', type)
.replace(':project_path', projectPath)
.replace(':namespace_path', namespacePath);
@@ -218,6 +218,7 @@ const Api = {
(jqXHR, textStatus, errorThrown) => {
const error = new Error(`${options.url}: ${errorThrown}`);
error.textStatus = textStatus;
+ if (jqXHR && jqXHR.responseJSON) error.responseJSON = jqXHR.responseJSON;
reject(error);
},
);
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 622764107ad..d9341837149 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,8 +1,10 @@
/* eslint-disable class-methods-use-this */
import _ from 'underscore';
import Cookies from 'js-cookie';
+import { s__ } from './locale';
import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils';
-import Flash from './flash';
+import flash from './flash';
+import axios from './lib/utils/axios_utils';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
@@ -441,13 +443,15 @@ class AwardsHandler {
if (this.isUserAuthored($emojiButton)) {
this.userAuthored($emojiButton);
} else {
- $.post(awardUrl, {
+ axios.post(awardUrl, {
name: emoji,
- }, (data) => {
+ })
+ .then(({ data }) => {
if (data.ok) {
callback();
}
- }).fail(() => new Flash('Something went wrong on our end.'));
+ })
+ .catch(() => flash(s__('Something went wrong on our end.')));
}
}
diff --git a/app/assets/javascripts/behaviors/copy_as_gfm.js b/app/assets/javascripts/behaviors/copy_as_gfm.js
index c6eca72c51b..ffe90595b5d 100644
--- a/app/assets/javascripts/behaviors/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/copy_as_gfm.js
@@ -299,6 +299,13 @@ const gfmRules = {
export class CopyAsGFM {
constructor() {
+ // iOS currently does not support clipboardData.setData(). This bug should
+ // be fixed in iOS 12, but for now we'll disable this for all iOS browsers
+ // ref: https://trac.webkit.org/changeset/222228/webkit
+ const userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
+ const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
+ if (isIOS) return;
+
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
diff --git a/app/assets/javascripts/behaviors/secret_values.js b/app/assets/javascripts/behaviors/secret_values.js
index 1cf0b960eb0..7f70fce913a 100644
--- a/app/assets/javascripts/behaviors/secret_values.js
+++ b/app/assets/javascripts/behaviors/secret_values.js
@@ -2,18 +2,19 @@ import { n__ } from '../locale';
import { convertPermissionToBoolean } from '../lib/utils/common_utils';
export default class SecretValues {
- constructor(container) {
+ constructor({
+ container,
+ valueSelector = '.js-secret-value',
+ placeholderSelector = '.js-secret-value-placeholder',
+ }) {
this.container = container;
+ this.valueSelector = valueSelector;
+ this.placeholderSelector = placeholderSelector;
}
init() {
- this.values = this.container.querySelectorAll('.js-secret-value');
- this.placeholders = this.container.querySelectorAll('.js-secret-value-placeholder');
this.revealButton = this.container.querySelector('.js-secret-value-reveal-button');
- this.revealText = n__('Reveal value', 'Reveal values', this.values.length);
- this.hideText = n__('Hide value', 'Hide values', this.values.length);
-
const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus);
this.updateDom(isRevealed);
@@ -28,15 +29,17 @@ export default class SecretValues {
}
updateDom(isRevealed) {
- this.values.forEach((value) => {
+ const values = this.container.querySelectorAll(this.valueSelector);
+ values.forEach((value) => {
value.classList.toggle('hide', !isRevealed);
});
- this.placeholders.forEach((placeholder) => {
+ const placeholders = this.container.querySelectorAll(this.placeholderSelector);
+ placeholders.forEach((placeholder) => {
placeholder.classList.toggle('hide', isRevealed);
});
- this.revealButton.textContent = isRevealed ? this.hideText : this.revealText;
+ this.revealButton.textContent = isRevealed ? n__('Hide value', 'Hide values', values.length) : n__('Reveal value', 'Reveal values', values.length);
this.revealButton.dataset.secretRevealStatus = isRevealed;
}
}
diff --git a/app/assets/javascripts/boards/utils/query_data.js b/app/assets/javascripts/boards/utils/query_data.js
index 2cd3c146f11..65315979df7 100644
--- a/app/assets/javascripts/boards/utils/query_data.js
+++ b/app/assets/javascripts/boards/utils/query_data.js
@@ -5,7 +5,7 @@ export default (path, extraData) => path.split('&').reduce((dataParam, filterPar
const paramSplit = filterParam.split('=');
const paramKeyNormalized = paramSplit[0].replace('[]', '');
const isArray = paramSplit[0].indexOf('[]');
- const value = decodeURIComponent(paramSplit[1]).replace(/\+/g, ' ');
+ const value = decodeURIComponent(paramSplit[1].replace(/\+/g, ' '));
if (isArray !== -1) {
if (!data[paramKeyNormalized]) {
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index 637d0dbde23..4dddb6eb0d6 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -14,6 +14,7 @@ import {
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
import applications from './components/applications.vue';
+import setupToggleButtons from '../toggle_buttons';
/**
* Cluster page has 2 separate parts:
@@ -48,12 +49,9 @@ export default class Clusters {
installPrometheusEndpoint: installPrometheusPath,
});
- this.toggle = this.toggle.bind(this);
this.installApplication = this.installApplication.bind(this);
this.showToken = this.showToken.bind(this);
- this.toggleButton = document.querySelector('.js-toggle-cluster');
- this.toggleInput = document.querySelector('.js-toggle-input');
this.errorContainer = document.querySelector('.js-cluster-error');
this.successContainer = document.querySelector('.js-cluster-success');
this.creatingContainer = document.querySelector('.js-cluster-creating');
@@ -63,6 +61,7 @@ export default class Clusters {
this.tokenField = document.querySelector('.js-cluster-token');
initSettingsPanels();
+ setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
this.initApplications();
if (this.store.state.status !== 'created') {
@@ -101,13 +100,11 @@ export default class Clusters {
}
addListeners() {
- this.toggleButton.addEventListener('click', this.toggle);
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication);
}
removeListeners() {
- this.toggleButton.removeEventListener('click', this.toggle);
if (this.showTokenButton) this.showTokenButton.removeEventListener('click', this.showToken);
eventHub.$off('installApplication', this.installApplication);
}
@@ -151,11 +148,6 @@ export default class Clusters {
this.updateContainer(prevStatus, this.store.state.status, this.store.state.statusReason);
}
- toggle() {
- this.toggleButton.classList.toggle('is-checked');
- this.toggleInput.setAttribute('value', this.toggleButton.classList.contains('is-checked').toString());
- }
-
showToken() {
const type = this.tokenField.getAttribute('type');
diff --git a/app/assets/javascripts/clusters/clusters_index.js b/app/assets/javascripts/clusters/clusters_index.js
index 6844d1dbd83..2e3ad244375 100644
--- a/app/assets/javascripts/clusters/clusters_index.js
+++ b/app/assets/javascripts/clusters/clusters_index.js
@@ -1,58 +1,20 @@
import Flash from '../flash';
import { s__ } from '../locale';
+import setupToggleButtons from '../toggle_buttons';
import ClustersService from './services/clusters_service';
-/**
- * Toggles loading and disabled classes.
- * @param {HTMLElement} button
- */
-const toggleLoadingButton = (button) => {
- if (button.getAttribute('disabled')) {
- button.removeAttribute('disabled');
- } else {
- button.setAttribute('disabled', true);
- }
-
- button.classList.toggle('is-loading');
-};
-/**
- * Toggles checked class for the given button
- * @param {HTMLElement} button
- */
-const toggleValue = (button) => {
- button.classList.toggle('is-checked');
+export default () => {
+ const clusterList = document.querySelector('.js-clusters-list');
+ // The empty state won't have a clusterList
+ if (clusterList) {
+ setupToggleButtons(
+ document.querySelector('.js-clusters-list'),
+ (value, toggle) =>
+ ClustersService.updateCluster(toggle.dataset.endpoint, { cluster: { enabled: value } })
+ .catch((err) => {
+ Flash(s__('ClusterIntegration|Something went wrong on our end.'));
+ throw err;
+ }),
+ );
+ }
};
-
-/**
- * Handles toggle buttons in the cluster's table.
- *
- * When the user clicks the toggle button for each cluster, it:
- * - toggles the button
- * - shows a loading and disables button
- * - Makes a put request to the given endpoint
- * Once we receive the response, either:
- * 1) Show updated status in case of successfull response
- * 2) Show initial status in case of failed response
- */
-export default function setClusterTableToggles() {
- document.querySelectorAll('.js-toggle-cluster-list')
- .forEach(button => button.addEventListener('click', (e) => {
- const toggleButton = e.currentTarget;
- const endpoint = toggleButton.getAttribute('data-endpoint');
-
- toggleValue(toggleButton);
- toggleLoadingButton(toggleButton);
-
- const value = toggleButton.classList.contains('is-checked');
-
- ClustersService.updateCluster(endpoint, { cluster: { enabled: value } })
- .then(() => {
- toggleLoadingButton(toggleButton);
- })
- .catch(() => {
- toggleLoadingButton(toggleButton);
- toggleValue(toggleButton);
- Flash(s__('ClusterIntegration|Something went wrong on our end.'));
- });
- }));
-}
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index 144caf1d278..e2a008e8904 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */
import { localTimeAgo } from './lib/utils/datetime_utility';
+import axios from './lib/utils/axios_utils';
export default class Compare {
constructor(opts) {
@@ -41,17 +42,14 @@ export default class Compare {
}
getTargetProject() {
- return $.ajax({
- url: this.opts.targetProjectUrl,
- data: {
- target_project_id: $("input[name='merge_request[target_project_id]']").val()
- },
- beforeSend: function() {
- return $('.mr_target_commit').empty();
+ $('.mr_target_commit').empty();
+
+ return axios.get(this.opts.targetProjectUrl, {
+ params: {
+ target_project_id: $("input[name='merge_request[target_project_id]']").val(),
},
- success: function(html) {
- return $('.js-target-branch-dropdown .dropdown-content').html(html);
- }
+ }).then(({ data }) => {
+ $('.js-target-branch-dropdown .dropdown-content').html(data);
});
}
@@ -68,22 +66,19 @@ export default class Compare {
});
}
- static sendAjax(url, loading, target, data) {
- var $target;
- $target = $(target);
- return $.ajax({
- url: url,
- data: data,
- beforeSend: function() {
- loading.show();
- return $target.empty();
- },
- success: function(html) {
- loading.hide();
- $target.html(html);
- var className = '.' + $target[0].className.replace(' ', '.');
- localTimeAgo($('.js-timeago', className));
- }
+ static sendAjax(url, loading, target, params) {
+ const $target = $(target);
+
+ loading.show();
+ $target.empty();
+
+ return axios.get(url, {
+ params,
+ }).then(({ data }) => {
+ loading.hide();
+ $target.html(data);
+ const className = '.' + $target[0].className.replace(' ', '.');
+ localTimeAgo($('.js-timeago', className));
});
}
}
diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js
index e633ef8a29e..59899e97be1 100644
--- a/app/assets/javascripts/compare_autocomplete.js
+++ b/app/assets/javascripts/compare_autocomplete.js
@@ -1,4 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */
+import { __ } from './locale';
+import axios from './lib/utils/axios_utils';
+import flash from './flash';
export default function initCompareAutocomplete() {
$('.js-compare-dropdown').each(function() {
@@ -10,15 +13,14 @@ export default function initCompareAutocomplete() {
const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({
data: function(term, callback) {
- return $.ajax({
- url: $dropdown.data('refs-url'),
- data: {
+ axios.get($dropdown.data('refsUrl'), {
+ params: {
ref: $dropdown.data('ref'),
search: term,
- }
- }).done(function(refs) {
- return callback(refs);
- });
+ },
+ }).then(({ data }) => {
+ callback(data);
+ }).catch(() => flash(__('Error fetching refs')));
},
selectable: true,
filterable: true,
diff --git a/app/assets/javascripts/create_item_dropdown.js b/app/assets/javascripts/create_item_dropdown.js
index c3eceb285f5..488db023ee7 100644
--- a/app/assets/javascripts/create_item_dropdown.js
+++ b/app/assets/javascripts/create_item_dropdown.js
@@ -65,7 +65,17 @@ export default class CreateItemDropdown {
getData(term, callback) {
this.getDataOption(term, (data = []) => {
- callback(data.concat(this.selectedItem || []));
+ // Ensure the selected item isn't already in the data to avoid duplicates
+ const alreadyHasSelectedItem = this.selectedItem && data.some(item =>
+ item.id === this.selectedItem.id,
+ );
+
+ let uniqueData = data;
+ if (!alreadyHasSelectedItem) {
+ uniqueData = data.concat(this.selectedItem || []);
+ }
+
+ callback(uniqueData);
});
}
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index bc23a72762f..482d83621e2 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -1,5 +1,6 @@
/* eslint-disable no-new */
import _ from 'underscore';
+import axios from './lib/utils/axios_utils';
import Flash from './flash';
import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
@@ -74,60 +75,52 @@ export default class CreateMergeRequestDropdown {
}
checkAbilityToCreateBranch() {
- return $.ajax({
- type: 'GET',
- dataType: 'json',
- url: this.canCreatePath,
- beforeSend: () => this.setUnavailableButtonState(),
- })
- .done((data) => {
- this.setUnavailableButtonState(false);
-
- if (data.can_create_branch) {
- this.available();
- this.enable();
-
- if (!this.droplabInitialized) {
- this.droplabInitialized = true;
- this.initDroplab();
- this.bindEvents();
+ this.setUnavailableButtonState();
+
+ axios.get(this.canCreatePath)
+ .then(({ data }) => {
+ this.setUnavailableButtonState(false);
+
+ if (data.can_create_branch) {
+ this.available();
+ this.enable();
+
+ if (!this.droplabInitialized) {
+ this.droplabInitialized = true;
+ this.initDroplab();
+ this.bindEvents();
+ }
+ } else if (data.has_related_branch) {
+ this.hide();
}
- } else if (data.has_related_branch) {
- this.hide();
- }
- }).fail(() => {
- this.unavailable();
- this.disable();
- new Flash('Failed to check if a new branch can be created.');
- });
+ })
+ .catch(() => {
+ this.unavailable();
+ this.disable();
+ Flash('Failed to check if a new branch can be created.');
+ });
}
createBranch() {
- return $.ajax({
- method: 'POST',
- dataType: 'json',
- url: this.createBranchPath,
- beforeSend: () => (this.isCreatingBranch = true),
- })
- .done((data) => {
- this.branchCreated = true;
- window.location.href = data.url;
- })
- .fail(() => new Flash('Failed to create a branch for this issue. Please try again.'));
+ this.isCreatingBranch = true;
+
+ return axios.post(this.createBranchPath)
+ .then(({ data }) => {
+ this.branchCreated = true;
+ window.location.href = data.url;
+ })
+ .catch(() => Flash('Failed to create a branch for this issue. Please try again.'));
}
createMergeRequest() {
- return $.ajax({
- method: 'POST',
- dataType: 'json',
- url: this.createMrPath,
- beforeSend: () => (this.isCreatingMergeRequest = true),
- })
- .done((data) => {
- this.mergeRequestCreated = true;
- window.location.href = data.url;
- })
- .fail(() => new Flash('Failed to create Merge Request. Please try again.'));
+ this.isCreatingMergeRequest = true;
+
+ return axios.post(this.createMrPath)
+ .then(({ data }) => {
+ this.mergeRequestCreated = true;
+ window.location.href = data.url;
+ })
+ .catch(() => Flash('Failed to create Merge Request. Please try again.'));
}
disable() {
@@ -200,39 +193,33 @@ export default class CreateMergeRequestDropdown {
getRef(ref, target = 'all') {
if (!ref) return false;
- return $.ajax({
- method: 'GET',
- dataType: 'json',
- url: this.refsPath + ref,
- beforeSend: () => {
- this.isGettingRef = true;
- },
- })
- .always(() => {
- this.isGettingRef = false;
- })
- .done((data) => {
- const branches = data[Object.keys(data)[0]];
- const tags = data[Object.keys(data)[1]];
- let result;
+ return axios.get(this.refsPath + ref)
+ .then(({ data }) => {
+ const branches = data[Object.keys(data)[0]];
+ const tags = data[Object.keys(data)[1]];
+ let result;
+
+ if (target === 'branch') {
+ result = CreateMergeRequestDropdown.findByValue(branches, ref);
+ } else {
+ result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
+ CreateMergeRequestDropdown.findByValue(tags, ref, true);
+ this.suggestedRef = result;
+ }
- if (target === 'branch') {
- result = CreateMergeRequestDropdown.findByValue(branches, ref);
- } else {
- result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
- CreateMergeRequestDropdown.findByValue(tags, ref, true);
- this.suggestedRef = result;
- }
+ this.isGettingRef = false;
- return this.updateInputState(target, ref, result);
- })
- .fail(() => {
- this.unavailable();
- this.disable();
- new Flash('Failed to get ref.');
+ return this.updateInputState(target, ref, result);
+ })
+ .catch(() => {
+ this.unavailable();
+ this.disable();
+ new Flash('Failed to get ref.');
- return false;
- });
+ this.isGettingRef = false;
+
+ return false;
+ });
}
getTargetData(target) {
@@ -332,12 +319,12 @@ export default class CreateMergeRequestDropdown {
xhr = this.createBranch();
}
- xhr.fail(() => {
+ xhr.catch(() => {
this.isCreatingMergeRequest = false;
this.isCreatingBranch = false;
- });
- xhr.always(() => this.enable());
+ this.enable();
+ });
this.disable();
}
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 7b68b19de75..5a782237b7d 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -87,6 +87,7 @@
<div v-else-if="hasKeys">
<keys-panel
title="Enabled deploy keys for this project"
+ class="qa-project-deploy-keys"
:keys="keys.enabled_keys"
:store="store"
:endpoint="endpoint"
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index a9e819b8a3c..c6091efd62f 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -1,11 +1,15 @@
<script>
import actionBtn from './action_btn.vue';
import { getTimeago } from '../../lib/utils/datetime_utility';
+ import tooltip from '../../vue_shared/directives/tooltip';
export default {
components: {
actionBtn,
},
+ directives: {
+ tooltip,
+ },
props: {
deployKey: {
type: Object,
@@ -32,6 +36,9 @@
isEnabled(id) {
return this.store.findEnabledKey(id) !== undefined;
},
+ tooltipTitle(project) {
+ return project.can_push ? 'Write access allowed' : 'Read access only';
+ },
},
};
</script>
@@ -46,27 +53,29 @@
</i>
</div>
<div class="deploy-key-content key-list-item-info">
- <strong class="title">
+ <strong class="title qa-key-title">
{{ deployKey.title }}
</strong>
- <div class="description">
+ <div class="description qa-key-fingerprint">
{{ deployKey.fingerprint }}
</div>
- <div
- v-if="deployKey.can_push"
- class="write-access-allowed"
- >
- Write access allowed
- </div>
</div>
<div class="deploy-key-content prepend-left-default deploy-key-projects">
<a
- v-for="(project, i) in deployKey.projects"
- class="label deploy-project-label"
- :href="project.full_path"
+ v-for="(deployKeysProject, i) in deployKey.deploy_keys_projects"
:key="i"
+ class="label deploy-project-label"
+ :href="deployKeysProject.project.full_path"
+ :title="tooltipTitle(deployKeysProject)"
+ v-tooltip
>
- {{ project.full_name }}
+ {{ deployKeysProject.project.full_name }}
+ <i
+ v-if="!deployKeysProject.can_push"
+ aria-hidden="true"
+ class="fa fa-lock"
+ >
+ </i>
</a>
</div>
<div class="deploy-key-content">
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 0c784084b0d..262ed3783fb 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -1,56 +1,16 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
-import projectSelect from './project_select';
-import Milestone from './milestone';
-import IssuableForm from './issuable_form';
-import LabelsSelect from './labels_select';
-import MilestoneSelect from './milestone_select';
-import NotificationsForm from './notifications_form';
-import notificationsDropdown from './notifications_dropdown';
-import groupAvatar from './group_avatar';
-import GroupLabelSubscription from './group_label_subscription';
-import LineHighlighter from './line_highlighter';
-import Project from './project';
-import projectAvatar from './project_avatar';
import MergeRequest from './merge_request';
-import Compare from './compare';
-import ProjectNew from './project_new';
-import Labels from './labels';
-import LabelManager from './label_manager';
-import Sidebar from './right_sidebar';
-import IssuableTemplateSelectors from './templates/issuable_template_selectors';
import Flash from './flash';
-import BindInOut from './behaviors/bind_in_out';
-import SecretValues from './behaviors/secret_values';
-import Group from './group';
-import ProjectsList from './projects_list';
-import UserCallout from './user_callout';
-import ShortcutsWiki from './shortcuts_wiki';
-import BlobViewer from './blob/viewer/index';
-import UsersSelect from './users_select';
import GfmAutoComplete from './gfm_auto_complete';
-import Star from './star';
-import TreeView from './tree';
-import Wikis from './wikis';
import ZenMode from './zen_mode';
-import initSettingsPanels from './settings_panels';
-import PerformanceBar from './performance_bar';
import initNotes from './init_notes';
import initIssuableSidebar from './init_issuable_sidebar';
-import initProjectVisibilitySelector from './project_visibility';
-import NewGroupChild from './groups/new_group_child';
-import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils';
+import { convertPermissionToBoolean } from './lib/utils/common_utils';
import GlFieldErrors from './gl_field_errors';
-import GLForm from './gl_form';
import Shortcuts from './shortcuts';
-import ShortcutsNavigation from './shortcuts_navigation';
import ShortcutsIssuable from './shortcuts_issuable';
-import U2FAuthenticate from './u2f/authenticate';
-import Members from './members';
-import memberExpirationDate from './member_expiration_date';
import Diff from './diff';
-import ProjectLabelSubscription from './project_label_subscription';
import SearchAutocomplete from './search_autocomplete';
-import Activities from './activities';
(function() {
var Dispatcher;
@@ -88,8 +48,6 @@ import Activities from './activities';
});
});
- const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
-
switch (page) {
case 'sessions:new':
import('./pages/sessions/new')
@@ -103,6 +61,11 @@ import Activities from './activities';
.catch(fail);
shortcut_handler = true;
break;
+ case 'projects:environments:metrics':
+ import('./pages/projects/environments/metrics')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'projects:merge_requests:index':
import('./pages/projects/merge_requests/index')
.then(callDefault)
@@ -126,10 +89,20 @@ import Activities from './activities';
.then(callDefault)
.catch(fail);
break;
+ case 'projects:milestones:index':
+ import('./pages/projects/milestones/index')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'projects:milestones:show':
+ import('./pages/projects/milestones/show')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'groups:milestones:show':
- new Milestone();
- new Sidebar();
+ import('./pages/groups/milestones/show')
+ .then(callDefault)
+ .catch(fail);
break;
case 'dashboard:milestones:show':
import('./pages/dashboard/milestones/show')
@@ -147,15 +120,24 @@ import Activities from './activities';
.catch(fail);
break;
case 'groups:issues':
+ import('./pages/groups/issues')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'groups:merge_requests':
- if (filteredSearchEnabled) {
- const filteredSearchManager = new gl.FilteredSearchManager(page === 'groups:issues' ? 'issues' : 'merge_requests');
- filteredSearchManager.setup();
- }
- projectSelect();
+ import('./pages/groups/merge_requests')
+ .then(callDefault)
+ .catch(fail);
break;
case 'dashboard:todos:index':
- import('./pages/dashboard/todos/index').then(callDefault).catch(fail);
+ import('./pages/dashboard/todos/index')
+ .then(callDefault)
+ .catch(fail);
+ break;
+ case 'admin:jobs:index':
+ import('./pages/admin/jobs/index')
+ .then(callDefault)
+ .catch(fail);
break;
case 'dashboard:projects:index':
case 'dashboard:projects:starred':
@@ -232,28 +214,20 @@ import Activities from './activities';
shortcut_handler = true;
break;
case 'projects:merge_requests:creations:new':
- const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
- if (mrNewCompareNode) {
- new Compare({
- targetProjectUrl: mrNewCompareNode.dataset.targetProjectUrl,
- sourceBranchUrl: mrNewCompareNode.dataset.sourceBranchUrl,
- targetBranchUrl: mrNewCompareNode.dataset.targetBranchUrl,
- });
- } else {
- const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit');
- new MergeRequest({
- action: mrNewSubmitNode.dataset.mrSubmitAction,
- });
- }
+ import('./pages/projects/merge_requests/creations/new')
+ .then(callDefault)
+ .catch(fail);
case 'projects:merge_requests:creations:diffs':
+ import('./pages/projects/merge_requests/creations/diffs')
+ .then(callDefault)
+ .catch(fail);
+ shortcut_handler = true;
+ break;
case 'projects:merge_requests:edit':
- new Diff();
- shortcut_handler = new ShortcutsNavigation();
- new GLForm($('.merge-request-form'), true);
- new IssuableForm($('.merge-request-form'));
- new LabelsSelect();
- new MilestoneSelect();
- new IssuableTemplateSelectors();
+ import('./pages/projects/merge_requests/edit')
+ .then(callDefault)
+ .catch(fail);
+ shortcut_handler = true;
break;
case 'projects:tags:new':
import('./pages/projects/tags/new')
@@ -261,15 +235,21 @@ import Activities from './activities';
.catch(fail);
break;
case 'projects:snippets:show':
- initNotes();
- new ZenMode();
+ import('./pages/projects/snippets/show')
+ .then(callDefault)
+ .catch(fail);
break;
case 'projects:snippets:new':
- case 'projects:snippets:edit':
case 'projects:snippets:create':
+ import('./pages/projects/snippets/new')
+ .then(callDefault)
+ .catch(fail);
+ break;
+ case 'projects:snippets:edit':
case 'projects:snippets:update':
- new GLForm($('.snippet-form'), true);
- new ZenMode();
+ import('./pages/projects/snippets/edit')
+ .then(callDefault)
+ .catch(fail);
break;
case 'snippets:new':
import('./pages/snippets/new')
@@ -292,8 +272,9 @@ import Activities from './activities';
.catch(fail);
break;
case 'projects:releases:edit':
- new ZenMode();
- new GLForm($('.release-form'), true);
+ import('./pages/projects/releases/edit')
+ .then(callDefault)
+ .catch(fail);
break;
case 'projects:merge_requests:show':
new Diff();
@@ -306,7 +287,6 @@ import Activities from './activities';
window.mergeRequest = new MergeRequest({
action: mrShowNode.dataset.mrAction,
});
-
shortcut_handler = new ShortcutsIssuable(true);
break;
case 'dashboard:activity':
@@ -338,19 +318,10 @@ import Activities from './activities';
shortcut_handler = true;
break;
case 'projects:show':
- shortcut_handler = new ShortcutsNavigation();
- new NotificationsForm();
- new UserCallout({
- setCalloutPerProject: true,
- className: 'js-autodevops-banner',
- });
-
- if ($('#tree-slider').length) new TreeView();
- if ($('.blob-viewer').length) new BlobViewer();
- if ($('.project-show-activity').length) new Activities();
- $('#tree-slider').waitForImages(function() {
- ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
- });
+ import('./pages/projects/show')
+ .then(callDefault)
+ .catch(fail);
+ shortcut_handler = true;
break;
case 'projects:edit':
import('./pages/projects/edit')
@@ -376,34 +347,36 @@ import Activities from './activities';
.catch(fail);
break;
case 'groups:activity':
- new Activities();
+ import('./pages/groups/activity')
+ .then(callDefault)
+ .catch(fail);
break;
case 'groups:show':
- const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
- shortcut_handler = new ShortcutsNavigation();
- new NotificationsForm();
- notificationsDropdown();
- new ProjectsList();
-
- if (newGroupChildWrapper) {
- new NewGroupChild(newGroupChildWrapper);
- }
+ import('./pages/groups/show')
+ .then(callDefault)
+ .catch(fail);
+ shortcut_handler = true;
break;
case 'groups:group_members:index':
- memberExpirationDate();
- new Members();
- new UsersSelect();
+ import('./pages/groups/group_members/index')
+ .then(callDefault)
+ .catch(fail);
break;
case 'projects:project_members:index':
import('./pages/projects/project_members/')
.then(callDefault)
.catch(fail);
break;
- case 'groups:new':
case 'groups:create':
- BindInOut.initAll();
- new Group();
- groupAvatar();
+ case 'groups:new':
+ import('./pages/groups/new')
+ .then(callDefault)
+ .catch(fail);
+ break;
+ case 'groups:edit':
+ import('./pages/groups/edit')
+ .then(callDefault)
+ .catch(fail);
break;
case 'admin:groups:create':
case 'admin:groups:new':
@@ -416,9 +389,6 @@ import Activities from './activities';
.then(callDefault)
.catch(fail);
break;
- case 'groups:edit':
- groupAvatar();
- break;
case 'projects:tree:show':
import('./pages/projects/tree/show')
.then(callDefault)
@@ -444,8 +414,14 @@ import Activities from './activities';
shortcut_handler = true;
break;
case 'groups:labels:new':
+ import('./pages/groups/labels/new')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'groups:labels:edit':
- new Labels();
+ import('./pages/groups/labels/edit')
+ .then(callDefault)
+ .catch(fail);
break;
case 'projects:labels:new':
import('./pages/projects/labels/new')
@@ -457,25 +433,16 @@ import Activities from './activities';
.then(callDefault)
.catch(fail);
break;
+ case 'groups:labels:index':
+ import('./pages/groups/labels/index')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'projects:labels:index':
import('./pages/projects/labels/index')
.then(callDefault)
.catch(fail);
break;
- case 'groups:labels:index':
- if ($('.prioritized-labels').length) {
- new LabelManager();
- }
- $('.label-subscription').each((i, el) => {
- const $el = $(el);
-
- if ($el.find('.dropdown-group-label').length) {
- new GroupLabelSubscription($el);
- } else {
- new ProjectLabelSubscription($el);
- }
- });
- break;
case 'projects:network:show':
// Ensure we don't create a particular shortcut handler here. This is
// already created, where the network graph is created.
@@ -509,34 +476,35 @@ import Activities from './activities';
.catch(fail);
break;
case 'projects:settings:repository:show':
- // Initialize expandable settings panels
- initSettingsPanels();
+ import('./pages/projects/settings/repository/show')
+ .then(callDefault)
+ .catch(fail);
break;
case 'projects:settings:ci_cd:show':
- // Initialize expandable settings panels
- initSettingsPanels();
-
- const runnerToken = document.querySelector('.js-secret-runner-token');
- if (runnerToken) {
- const runnerTokenSecretValue = new SecretValues(runnerToken);
- runnerTokenSecretValue.init();
- }
+ import('./pages/projects/settings/ci_cd/show')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'groups:settings:ci_cd:show':
- const secretVariableTable = document.querySelector('.js-secret-variable-table');
- if (secretVariableTable) {
- const secretVariableTableValues = new SecretValues(secretVariableTable);
- secretVariableTableValues.init();
- }
+ import('./pages/groups/settings/ci_cd/show')
+ .then(callDefault)
+ .catch(fail);
break;
case 'ci:lints:create':
case 'ci:lints:show':
- import('./pages/ci/lints').then(m => m.default()).catch(fail);
+ import('./pages/ci/lints')
+ .then(callDefault)
+ .catch(fail);
break;
case 'users:show':
- import('./pages/users/show').then(callDefault).catch(fail);
+ import('./pages/users/show')
+ .then(callDefault)
+ .catch(fail);
break;
case 'admin:conversational_development_index:show':
- import('./pages/admin/conversational_development_index/show').then(m => m.default()).catch(fail);
+ import('./pages/admin/conversational_development_index/show')
+ .then(callDefault)
+ .catch(fail);
break;
case 'snippets:show':
import('./pages/snippets/show')
@@ -544,7 +512,9 @@ import Activities from './activities';
.catch(fail);
break;
case 'import:fogbugz:new_user_map':
- import('./pages/import/fogbugz/new_user_map').then(m => m.default()).catch(fail);
+ import('./pages/import/fogbugz/new_user_map')
+ .then(callDefault)
+ .catch(fail);
break;
case 'profiles:personal_access_tokens:index':
import('./pages/profiles/personal_access_tokens')
@@ -568,21 +538,23 @@ import Activities from './activities';
.then(callDefault)
.catch(fail);
break;
+ case 'dashboard:groups:index':
+ import('./pages/dashboard/groups/index')
+ .then(callDefault)
+ .catch(fail);
+ break;
}
switch (path[0]) {
case 'sessions':
+ import('./pages/sessions')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'omniauth_callbacks':
- if (!gon.u2f) break;
- const u2fAuthenticate = new U2FAuthenticate(
- $('#js-authenticate-u2f'),
- '#js-login-u2f-form',
- gon.u2f,
- document.querySelector('#js-login-2fa-device'),
- document.querySelector('.js-2fa-form'),
- );
- u2fAuthenticate.start();
- // needed in rspec
- gl.u2fAuthenticate = u2fAuthenticate;
+ import('./pages/omniauth_callbacks')
+ .then(callDefault)
+ .catch(fail);
+ break;
case 'admin':
import('./pages/admin')
.then(callDefault)
@@ -632,67 +604,34 @@ import Activities from './activities';
break;
}
break;
- case 'dashboard':
- case 'root':
- new UserCallout();
- break;
case 'profiles':
import('./pages/profiles/index/')
.then(callDefault)
.catch(fail);
break;
case 'projects':
- new Project();
- projectAvatar();
+ import('./pages/projects')
+ .then(callDefault)
+ .catch(fail);
+ shortcut_handler = true;
switch (path[1]) {
case 'compare':
import('./pages/projects/compare')
.then(callDefault)
.catch(fail);
break;
- case 'edit':
- shortcut_handler = new ShortcutsNavigation();
- new ProjectNew();
- import(/* webpackChunkName: 'project_permissions' */ './projects/permissions')
+ case 'create':
+ case 'new':
+ import('./pages/projects/new')
.then(callDefault)
.catch(fail);
break;
- case 'new':
- new ProjectNew();
- initProjectVisibilitySelector();
- break;
- case 'show':
- new Star();
- new ProjectNew();
- notificationsDropdown();
- break;
case 'wikis':
- new Wikis();
- shortcut_handler = new ShortcutsWiki();
- new ZenMode();
- new GLForm($('.wiki-form'), true);
- break;
- case 'snippets':
- shortcut_handler = new ShortcutsNavigation();
- if (path[2] === 'show') {
- new ZenMode();
- new LineHighlighter();
- new BlobViewer();
- }
+ import('./pages/projects/wikis')
+ .then(callDefault)
+ .catch(fail);
+ shortcut_handler = true;
break;
- case 'labels':
- case 'graphs':
- case 'compare':
- case 'pipelines':
- case 'forks':
- case 'milestones':
- case 'project_members':
- case 'deploy_keys':
- case 'builds':
- case 'hooks':
- case 'services':
- case 'protected_branches':
- shortcut_handler = new ShortcutsNavigation();
}
break;
}
@@ -702,7 +641,9 @@ import Activities from './activities';
}
if (document.querySelector('#peek')) {
- new PerformanceBar({ container: '#peek' });
+ import('./performance_bar')
+ .then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap
+ .catch(fail);
}
};
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 550dbdda922..ba89e5726fa 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -2,6 +2,7 @@ import Dropzone from 'dropzone';
import _ from 'underscore';
import './preview_markdown';
import csrf from './lib/utils/csrf';
+import axios from './lib/utils/axios_utils';
Dropzone.autoDiscover = false;
@@ -235,25 +236,21 @@ export default function dropzoneInput(form) {
uploadFile = (item, filename) => {
const formData = new FormData();
formData.append('file', item, filename);
- return $.ajax({
- url: uploadsPath,
- type: 'POST',
- data: formData,
- dataType: 'json',
- processData: false,
- contentType: false,
- headers: csrf.headers,
- beforeSend: () => {
- showSpinner();
- return closeAlertMessage();
- },
- success: (e, text, response) => {
- const md = response.responseJSON.link.markdown;
+
+ showSpinner();
+ closeAlertMessage();
+
+ axios.post(uploadsPath, formData)
+ .then(({ data }) => {
+ const md = data.link.markdown;
+
insertToTextArea(filename, md);
- },
- error: response => showError(response.responseJSON.message),
- complete: () => closeSpinner(),
- });
+ closeSpinner();
+ })
+ .catch((e) => {
+ showError(e.response.data.message);
+ closeSpinner();
+ });
};
updateAttachingMessage = (files, messageContainer) => {
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index ada985913bb..bd4c58b7cb1 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -1,6 +1,7 @@
/* global dateFormat */
import Pikaday from 'pikaday';
+import axios from './lib/utils/axios_utils';
import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
class DueDateSelect {
@@ -125,37 +126,30 @@ class DueDateSelect {
}
submitSelectedDate(isDropdown) {
- return $.ajax({
- type: 'PUT',
- url: this.issueUpdateURL,
- data: this.datePayload,
- dataType: 'json',
- beforeSend: () => {
- const selectedDateValue = this.datePayload[this.abilityName].due_date;
- const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
+ const selectedDateValue = this.datePayload[this.abilityName].due_date;
+ const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
- this.$loading.removeClass('hidden').fadeIn();
+ this.$loading.removeClass('hidden').fadeIn();
- if (isDropdown) {
- this.$dropdown.trigger('loading.gl.dropdown');
- this.$selectbox.hide();
- }
+ if (isDropdown) {
+ this.$dropdown.trigger('loading.gl.dropdown');
+ this.$selectbox.hide();
+ }
- this.$value.css('display', '');
- this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
- this.$sidebarValue.html(this.displayedDate);
+ this.$value.css('display', '');
+ this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
+ this.$sidebarValue.html(this.displayedDate);
- return selectedDateValue.length ?
- $('.js-remove-due-date-holder').removeClass('hidden') :
- $('.js-remove-due-date-holder').addClass('hidden');
- },
- }).done(() => {
- if (isDropdown) {
- this.$dropdown.trigger('loaded.gl.dropdown');
- this.$dropdown.dropdown('toggle');
- }
- return this.$loading.fadeOut();
- });
+ $('.js-remove-due-date-holder').toggleClass('hidden', selectedDateValue.length);
+
+ return axios.put(this.issueUpdateURL, this.datePayload)
+ .then(() => {
+ if (isDropdown) {
+ this.$dropdown.trigger('loaded.gl.dropdown');
+ this.$dropdown.dropdown('toggle');
+ }
+ return this.$loading.fadeOut();
+ });
}
}
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index 858acf293a1..b4eca47957e 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -99,7 +99,7 @@ export default {
>
<div
v-if="model.isLoadingFolderContent"
- :key="i">
+ :key="`loading-item-${i}`">
<loading-icon size="2" />
</div>
@@ -110,10 +110,10 @@ export default {
:model="children"
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
- :key="index"
+ :key="`env-item-${i}-${index}`"
/>
- <div :key="i">
+ <div :key="`sub-div-${i}`">
<div class="text-center prepend-top-10">
<a
:href="folderUrl(model)"
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index 9e91f72b2ea..a10f027de53 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -1,4 +1,5 @@
import _ from 'underscore';
+import axios from './lib/utils/axios_utils';
/**
* Makes search request for content when user types a value in the search input.
@@ -54,32 +55,26 @@ export default class FilterableList {
this.listFilterElement.removeEventListener('input', this.debounceFilter);
}
- filterResults(queryData) {
+ filterResults(params) {
if (this.isBusy) {
return false;
}
$(this.listHolderElement).fadeTo(250, 0.5);
- return $.ajax({
- url: this.getFilterEndpoint(),
- data: queryData,
- type: 'GET',
- dataType: 'json',
- context: this,
- complete: this.onFilterComplete,
- beforeSend: () => {
- this.isBusy = true;
- },
- success: (response, textStatus, xhr) => {
- this.onFilterSuccess(response, xhr, queryData);
- },
- });
+ this.isBusy = true;
+
+ return axios.get(this.getFilterEndpoint(), {
+ params,
+ }).then((res) => {
+ this.onFilterSuccess(res, params);
+ this.onFilterComplete();
+ }).catch(() => this.onFilterComplete());
}
- onFilterSuccess(response, xhr, queryData) {
- if (response.html) {
- this.listHolderElement.innerHTML = response.html;
+ onFilterSuccess(response, queryData) {
+ if (response.data.html) {
+ this.listHolderElement.innerHTML = response.data.html;
}
// Change url so if user reload a page - search results are saved
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index 44deab9288e..a0af2875ab5 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -10,6 +10,7 @@ const hideFlash = (flashEl, fadeTransition = true) => {
flashEl.addEventListener('transitionend', () => {
flashEl.remove();
+ if (document.body.classList.contains('flash-shown')) document.body.classList.remove('flash-shown');
}, {
once: true,
passive: true,
@@ -64,6 +65,7 @@ const createFlash = function createFlash(
parent = document,
actionConfig = null,
fadeTransition = true,
+ addBodyClass = false,
) {
const flashContainer = parent.querySelector('.flash-container');
@@ -86,6 +88,8 @@ const createFlash = function createFlash(
flashContainer.style.display = 'block';
+ if (addBodyClass) document.body.classList.add('flash-shown');
+
return flashContainer;
};
diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js
index abb04d77f8f..8b4f3b05ee7 100644
--- a/app/assets/javascripts/fly_out_nav.js
+++ b/app/assets/javascripts/fly_out_nav.js
@@ -118,14 +118,14 @@ export const showSubLevelItems = (el) => {
moveSubItemsToPosition(el, subItems);
};
-export const mouseEnterTopItems = (el) => {
+export const mouseEnterTopItems = (el, timeout = getHideSubItemsInterval()) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
if (currentOpenMenu) hideMenu(currentOpenMenu);
showSubLevelItems(el);
- }, getHideSubItemsInterval());
+ }, timeout);
};
export const mouseLeaveTopItem = (el) => {
diff --git a/app/assets/javascripts/groups/groups_filterable_list.js b/app/assets/javascripts/groups/groups_filterable_list.js
index 2db233b09da..31d56d15c23 100644
--- a/app/assets/javascripts/groups/groups_filterable_list.js
+++ b/app/assets/javascripts/groups/groups_filterable_list.js
@@ -1,6 +1,6 @@
import FilterableList from '~/filterable_list';
import eventHub from './event_hub';
-import { getParameterByName } from '../lib/utils/common_utils';
+import { normalizeHeaders, getParameterByName } from '../lib/utils/common_utils';
export default class GroupFilterableList extends FilterableList {
constructor({ form, filter, holder, filterEndpoint, pagePath, dropdownSel, filterInputField }) {
@@ -94,23 +94,14 @@ export default class GroupFilterableList extends FilterableList {
this.form.querySelector(`[name="${this.filterInputField}"]`).value = '';
}
- onFilterSuccess(data, xhr, queryData) {
+ onFilterSuccess(res, queryData) {
const currentPath = this.getPagePath(queryData);
- const paginationData = {
- 'X-Per-Page': xhr.getResponseHeader('X-Per-Page'),
- 'X-Page': xhr.getResponseHeader('X-Page'),
- 'X-Total': xhr.getResponseHeader('X-Total'),
- 'X-Total-Pages': xhr.getResponseHeader('X-Total-Pages'),
- 'X-Next-Page': xhr.getResponseHeader('X-Next-Page'),
- 'X-Prev-Page': xhr.getResponseHeader('X-Prev-Page'),
- };
-
window.history.replaceState({
page: currentPath,
}, document.title, currentPath);
- eventHub.$emit('updateGroups', data, Object.prototype.hasOwnProperty.call(queryData, this.filterInputField));
- eventHub.$emit('updatePagination', paginationData);
+ eventHub.$emit('updateGroups', res.data, Object.prototype.hasOwnProperty.call(queryData, this.filterInputField));
+ eventHub.$emit('updatePagination', normalizeHeaders(res.headers));
}
}
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index 8b850765a1b..57eaac72906 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -10,7 +10,7 @@ import groupItemComponent from './components/group_item.vue';
Vue.use(Translate);
-document.addEventListener('DOMContentLoaded', () => {
+export default () => {
const el = document.getElementById('js-groups-tree');
// Don't do anything if element doesn't exist (No groups)
@@ -71,4 +71,4 @@ document.addEventListener('DOMContentLoaded', () => {
});
},
});
-});
+};
diff --git a/app/assets/javascripts/groups/service/groups_service.js b/app/assets/javascripts/groups/service/groups_service.js
index 639410384c2..b79ba291463 100644
--- a/app/assets/javascripts/groups/service/groups_service.js
+++ b/app/assets/javascripts/groups/service/groups_service.js
@@ -1,7 +1,5 @@
import Vue from 'vue';
-import VueResource from 'vue-resource';
-
-Vue.use(VueResource);
+import '../../vue_shared/vue_resource_interceptor';
export default class GroupsService {
constructor(endpoint) {
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 5279417a72a..96b1bb78c1d 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -68,12 +68,8 @@ export default {
this.commitChanges({ payload, newMr: this.startNewMR })
.then(() => {
this.submitCommitsLoading = false;
- this.$store.dispatch('getTreeData', {
- projectId: this.currentProjectId,
- branch: this.currentBranchId,
- endpoint: `/tree/${this.currentBranchId}`,
- force: true,
- });
+ this.commitMessage = '';
+ this.startNewMR = false;
})
.catch(() => {
this.submitCommitsLoading = false;
@@ -153,6 +149,7 @@ you started editing. Would you like to create a new branch?`)"
type="submit"
:disabled="commitButtonDisabled"
class="btn btn-default btn-sm append-right-10 prepend-left-10"
+ :class="{ disabled: submitCommitsLoading }"
>
<i
v-if="submitCommitsLoading"
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 83b82ae44c9..f99228012f4 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -70,7 +70,10 @@ export default {
this.editor.createInstance(this.$refs.editor);
})
.then(() => this.setupEditor())
- .catch(() => flash('Error setting up monaco. Please try again.'));
+ .catch((err) => {
+ flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
+ throw err;
+ });
},
setupEditor() {
if (!this.activeFile) return;
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index f7f4db89bdf..110918872fb 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -35,9 +35,12 @@
return this.file.type === 'tree';
},
levelIndentation() {
- return {
- marginLeft: `${this.file.level * 16}px`,
- };
+ if (this.file.level > 0) {
+ return {
+ marginLeft: `${this.file.level * 16}px`,
+ };
+ }
+ return {};
},
shortId() {
return this.file.id.substr(0, 8);
@@ -111,7 +114,7 @@
/>
<i
class="fa"
- v-if="changedClass"
+ v-if="file.changed || file.tempFile"
:class="changedClass"
aria-hidden="true"
>
diff --git a/app/assets/javascripts/ide/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue
index ab0bef4f0ac..ca363bba0ef 100644
--- a/app/assets/javascripts/ide/components/repo_tabs.vue
+++ b/app/assets/javascripts/ide/components/repo_tabs.vue
@@ -20,7 +20,7 @@
>
<repo-tab
v-for="tab in openFiles"
- :key="tab.id"
+ :key="tab.key"
:tab="tab"
/>
</ul>
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index a9cbf8e370f..a7fb9e0588a 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -84,13 +84,13 @@ router.beforeEach((to, from, next) => {
}
})
.catch((e) => {
- flash('Error while loading the branch files. Please try again.');
+ flash('Error while loading the branch files. Please try again.', 'alert', document, null, false, true);
throw e;
});
}
})
.catch((e) => {
- flash('Error while loading the project data. Please try again.');
+ flash('Error while loading the project data. Please try again.', 'alert', document, null, false, true);
throw e;
});
}
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
index 668221c0296..51255f15658 100644
--- a/app/assets/javascripts/ide/lib/editor.js
+++ b/app/assets/javascripts/ide/lib/editor.js
@@ -55,7 +55,7 @@ export default class Editor {
attachModel(model) {
this.instance.setModel(model.getModel());
- this.dirtyDiffController.attachModel(model);
+ if (this.dirtyDiffController) this.dirtyDiffController.attachModel(model);
this.currentModel = model;
@@ -68,7 +68,7 @@ export default class Editor {
return acc;
}, {}));
- this.dirtyDiffController.reDecorate(model);
+ if (this.dirtyDiffController) this.dirtyDiffController.reDecorate(model);
}
clearEditor() {
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 335882bb6d7..96a87744df5 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -3,6 +3,7 @@ import { visitUrl } from '../../lib/utils/url_utility';
import flash from '../../flash';
import service from '../services';
import * as types from './mutation_types';
+import { stripHtml } from '../../lib/utils/text_utility';
export const redirectToUrl = (_, url) => visitUrl(url);
@@ -81,7 +82,7 @@ export const checkCommitStatus = ({ state }) =>
return false;
})
- .catch(() => flash('Error checking branch data. Please try again.'));
+ .catch(() => flash('Error checking branch data. Please try again.', 'alert', document, null, false, true));
export const commitChanges = (
{ commit, state, dispatch, getters },
@@ -92,7 +93,7 @@ export const commitChanges = (
.then((data) => {
const { branch } = payload;
if (!data.short_id) {
- flash(data.message);
+ flash(data.message, 'alert', document, null, false, true);
return;
}
@@ -105,19 +106,25 @@ export const commitChanges = (
},
};
+ let commitMsg = `Your changes have been committed. Commit ${data.short_id}`;
+ if (data.stats) {
+ commitMsg += ` with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`;
+ }
+
flash(
- `Your changes have been committed. Commit ${data.short_id} with ${
- data.stats.additions
- } additions, ${data.stats.deletions} deletions.`,
+ commitMsg,
'notice',
- );
+ document,
+ null,
+ false,
+ true);
+ window.dispatchEvent(new Event('resize'));
if (newMr) {
+ dispatch('discardAllChanges');
dispatch(
'redirectToUrl',
- `${
- selectedProject.web_url
- }/merge_requests/new?merge_request%5Bsource_branch%5D=${branch}`,
+ `${selectedProject.web_url}/merge_requests/new?merge_request%5Bsource_branch%5D=${branch}`,
);
} else {
commit(types.SET_BRANCH_WORKING_REFERENCE, {
@@ -134,12 +141,18 @@ export const commitChanges = (
});
dispatch('discardAllChanges');
- dispatch('closeAllFiles');
window.scrollTo(0, 0);
}
})
- .catch(() => flash('Error committing changes. Please try again.'));
+ .catch((err) => {
+ let errMsg = 'Error committing changes. Please try again.';
+ if (err.responseJSON && err.responseJSON.message) {
+ errMsg += ` (${stripHtml(err.responseJSON.message)})`;
+ }
+ flash(errMsg, 'alert', document, null, false, true);
+ window.dispatchEvent(new Event('resize'));
+ });
export const createTempEntry = (
{ state, dispatch },
diff --git a/app/assets/javascripts/ide/stores/actions/branch.js b/app/assets/javascripts/ide/stores/actions/branch.js
index 32bdf7fec22..589ec28c6a4 100644
--- a/app/assets/javascripts/ide/stores/actions/branch.js
+++ b/app/assets/javascripts/ide/stores/actions/branch.js
@@ -17,7 +17,7 @@ export const getBranchData = (
resolve(data);
})
.catch(() => {
- flash('Error loading branch data. Please try again.');
+ flash('Error loading branch data. Please try again.', 'alert', document, null, false, true);
reject(new Error(`Branch not loaded - ${projectId}/${branchId}`));
});
} else {
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 0f27d5bf1c3..670af2fb89e 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -69,7 +69,7 @@ export const getFileData = ({ state, commit, dispatch }, file) => {
})
.catch(() => {
commit(types.TOGGLE_LOADING, file);
- flash('Error loading file data. Please try again.');
+ flash('Error loading file data. Please try again.', 'alert', document, null, false, true);
});
};
@@ -77,22 +77,28 @@ export const getRawFileData = ({ commit, dispatch }, file) => service.getRawFile
.then((raw) => {
commit(types.SET_FILE_RAW_DATA, { file, raw });
})
- .catch(() => flash('Error loading file content. Please try again.'));
+ .catch(() => flash('Error loading file content. Please try again.', 'alert', document, null, false, true));
export const changeFileContent = ({ commit }, { file, content }) => {
commit(types.UPDATE_FILE_CONTENT, { file, content });
};
export const setFileLanguage = ({ state, commit }, { fileLanguage }) => {
- commit(types.SET_FILE_LANGUAGE, { file: state.selectedFile, fileLanguage });
+ if (state.selectedFile) {
+ commit(types.SET_FILE_LANGUAGE, { file: state.selectedFile, fileLanguage });
+ }
};
export const setFileEOL = ({ state, commit }, { eol }) => {
- commit(types.SET_FILE_EOL, { file: state.selectedFile, eol });
+ if (state.selectedFile) {
+ commit(types.SET_FILE_EOL, { file: state.selectedFile, eol });
+ }
};
export const setEditorPosition = ({ state, commit }, { editorRow, editorColumn }) => {
- commit(types.SET_FILE_POSITION, { file: state.selectedFile, editorRow, editorColumn });
+ if (state.selectedFile) {
+ commit(types.SET_FILE_POSITION, { file: state.selectedFile, editorRow, editorColumn });
+ }
};
export const createTempFile = ({ state, commit, dispatch }, { projectId, branchId, parent, name, content = '', base64 = '' }) => {
@@ -112,7 +118,7 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI
url: newUrl,
});
- if (findEntry(parent.tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`);
+ if (findEntry(parent.tree, 'blob', file.name)) return flash(`The name "${file.name}" is already taken in this directory.`, 'alert', document, null, false, true);
commit(types.CREATE_TMP_FILE, {
parent,
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index 02d4bd87ab0..faeceb430a2 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -18,7 +18,7 @@ export const getProjectData = (
resolve(data);
})
.catch(() => {
- flash('Error loading project data. Please try again.');
+ flash('Error loading project data. Please try again.', 'alert', document, null, false, true);
reject(new Error(`Project not loaded ${namespace}/${projectId}`));
});
} else {
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index 25909400a75..302ba45edee 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -52,7 +52,7 @@ export const getTreeData = (
resolve(data);
})
.catch((e) => {
- flash('Error loading tree data. Please try again.');
+ flash('Error loading tree data. Please try again.', 'alert', document, null, false, true);
if (tree) commit(types.TOGGLE_LOADING, tree);
reject(e);
});
@@ -151,7 +151,7 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s
dispatch('getLastCommitData', tree);
})
- .catch(() => flash('Error fetching log data.'));
+ .catch(() => flash('Error fetching log data.', 'alert', document, null, false, true));
};
export const updateDirectoryData = (
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 5f3655b0092..72db1c180c9 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -64,7 +64,7 @@ export default {
},
[types.DISCARD_FILE_CHANGES](state, file) {
Object.assign(file, {
- content: '',
+ content: file.raw,
changed: false,
});
},
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index f85d66e9b1d..e87a8ed7fea 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -152,6 +152,13 @@
hasUpdated() {
return !!this.state.updatedAt;
},
+ issueChanged() {
+ const descriptionChanged =
+ this.initialDescriptionText !== this.store.formState.description;
+ const titleChanged =
+ this.initialTitleText !== this.store.formState.title;
+ return descriptionChanged || titleChanged;
+ },
},
created() {
this.service = new Service(this.endpoint);
@@ -176,6 +183,8 @@
}
});
+ window.addEventListener('beforeunload', this.handleBeforeUnloadEvent);
+
eventHub.$on('delete.issuable', this.deleteIssuable);
eventHub.$on('update.issuable', this.updateIssuable);
eventHub.$on('close.form', this.closeForm);
@@ -186,8 +195,17 @@
eventHub.$off('update.issuable', this.updateIssuable);
eventHub.$off('close.form', this.closeForm);
eventHub.$off('open.form', this.openForm);
+ window.removeEventListener('beforeunload', this.handleBeforeUnloadEvent);
},
methods: {
+ handleBeforeUnloadEvent(e) {
+ const event = e;
+ if (this.showForm && this.issueChanged) {
+ event.returnValue = 'Are you sure you want to lose your issue information?';
+ }
+ return undefined;
+ },
+
openForm() {
if (!this.showForm) {
this.showForm = true;
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index f7a1c9f1e40..664e793fc8e 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -231,7 +231,7 @@ export default class LabelsSelect {
selectedClass.push('label-item');
$a.attr('data-label-id', label.id);
}
- $a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title);
+ $a.addClass(selectedClass.join(' ')).html(`${colorEl} ${_.escape(label.title)}`);
// Return generated html
return $li.html($a).prop('outerHTML');
},
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index 8aff0556011..585214049c7 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -22,3 +22,11 @@ axios.interceptors.response.use((config) => {
});
export default axios;
+
+/**
+ * @return The adapter that axios uses for dispatching requests. This may be overwritten in tests.
+ *
+ * @see https://github.com/axios/axios/tree/master/lib/adapters
+ * @see https://github.com/ctimmerm/axios-mock-adapter/blob/v1.12.0/src/index.js#L39
+ */
+export const getDefaultAdapter = () => axios.defaults.adapter;
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index cb6e06ea584..62d80c4a649 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -72,4 +72,4 @@ export function capitalizeFirstCharacter(text) {
* @param {*} replace
* @returns {String}
*/
-export const stripeHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace);
+export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace);
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index e26bf437efc..bedd50de1bb 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */
import 'vendor/jquery.waitforimages';
+import { __ } from '~/locale';
import TaskList from './task_list';
import MergeRequestTabs from './merge_request_tabs';
import IssuablesHelper from './helpers/issuables_helper';
@@ -110,12 +111,12 @@ MergeRequest.prototype.initCommitMessageListeners = function() {
});
};
-MergeRequest.updateStatusText = function(classToRemove, classToAdd, newStatusText) {
+MergeRequest.setStatusBoxToMerged = function() {
$('.detail-page-header .status-box')
- .removeClass(classToRemove)
- .addClass(classToAdd)
+ .removeClass('status-box-open')
+ .addClass('status-box-mr-merged')
.find('span')
- .text(newStatusText);
+ .text(__('Merged'));
};
MergeRequest.decreaseCounter = function(by = 1) {
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index 104432ef5de..c3b0ef7e9ca 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import Dashboard from './components/dashboard.vue';
-document.addEventListener('DOMContentLoaded', () => new Vue({
+export default () => new Vue({
el: '#prometheus-graphs',
render: createElement => createElement(Dashboard),
-}));
+});
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index d0ec70f1fcf..3d09d24b6ab 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -1,6 +1,7 @@
<script>
/* global katex */
import marked from 'marked';
+ import sanitize from 'sanitize-html';
import Prompt from './prompt.vue';
const renderer = new marked.Renderer();
@@ -82,7 +83,12 @@
},
computed: {
markdown() {
- return marked(this.cell.source.join('').replace(/\\/g, '\\\\'));
+ return sanitize(marked(this.cell.source.join('').replace(/\\/g, '\\\\')), {
+ allowedTags: false,
+ allowedAttributes: {
+ '*': ['class'],
+ },
+ });
},
},
};
diff --git a/app/assets/javascripts/notebook/cells/output/html.vue b/app/assets/javascripts/notebook/cells/output/html.vue
index ebba5954de9..0535ee7afa8 100644
--- a/app/assets/javascripts/notebook/cells/output/html.vue
+++ b/app/assets/javascripts/notebook/cells/output/html.vue
@@ -1,4 +1,5 @@
<script>
+ import sanitize from 'sanitize-html';
import Prompt from '../prompt.vue';
export default {
@@ -11,12 +12,24 @@
required: true,
},
},
+ computed: {
+ sanitizedOutput() {
+ return sanitize(this.rawCode, {
+ allowedTags: sanitize.defaults.allowedTags.concat([
+ 'img', 'svg',
+ ]),
+ allowedAttributes: {
+ img: ['src'],
+ },
+ });
+ },
+ },
};
</script>
<template>
<div class="output">
<prompt />
- <div v-html="rawCode"></div>
+ <div v-html="sanitizedOutput"></div>
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index b28dda4904d..5b255d4a710 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -66,9 +66,7 @@
<template>
<div class="note-header-info">
<a :href="author.path">
- <span class="note-header-author-name">
- {{ author.name }}
- </span>
+ <span class="note-header-author-name">{{ author.name }}</span>
<span class="note-headline-light">
@{{ author.username }}
</span>
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
new file mode 100644
index 00000000000..555725cbe12
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
@@ -0,0 +1,47 @@
+<script>
+ import axios from '~/lib/utils/axios_utils';
+ import Flash from '~/flash';
+ import modal from '~/vue_shared/components/modal.vue';
+ import { s__ } from '~/locale';
+ import { redirectTo } from '~/lib/utils/url_utility';
+
+ export default {
+ components: {
+ modal,
+ },
+ props: {
+ url: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ text() {
+ return s__('AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.');
+ },
+ },
+ methods: {
+ onSubmit() {
+ return axios.post(this.url)
+ .then((response) => {
+ // follow the rediect to refresh the page
+ redirectTo(response.request.responseURL);
+ })
+ .catch((error) => {
+ Flash(s__('AdminArea|Stopping jobs failed'));
+ throw error;
+ });
+ },
+ },
+ };
+</script>
+
+<template>
+ <modal
+ id="stop-jobs-modal"
+ :title="s__('AdminArea|Stop all jobs?')"
+ :text="text"
+ kind="danger"
+ :primary-button-label="s__('AdminArea|Stop jobs')"
+ @submit="onSubmit" />
+</template>
diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js
new file mode 100644
index 00000000000..0e004bd9174
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/jobs/index/index.js
@@ -0,0 +1,29 @@
+import Vue from 'vue';
+
+import Translate from '~/vue_shared/translate';
+
+import stopJobsModal from './components/stop_jobs_modal.vue';
+
+Vue.use(Translate);
+
+export default () => {
+ const stopJobsButton = document.getElementById('stop-jobs-button');
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: '#stop-jobs-modal',
+ components: {
+ stopJobsModal,
+ },
+ mounted() {
+ stopJobsButton.classList.remove('disabled');
+ },
+ render(createElement) {
+ return createElement('stop-jobs-modal', {
+ props: {
+ url: stopJobsButton.dataset.url,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/pages/constants.js b/app/assets/javascripts/pages/constants.js
new file mode 100644
index 00000000000..328b6541636
--- /dev/null
+++ b/app/assets/javascripts/pages/constants.js
@@ -0,0 +1,6 @@
+/* eslint-disable import/prefer-default-export */
+
+export const FILTERED_SEARCH = {
+ MERGE_REQUESTS: 'merge_requests',
+ ISSUES: 'issues',
+};
diff --git a/app/assets/javascripts/pages/dashboard/groups/index/index.js b/app/assets/javascripts/pages/dashboard/groups/index/index.js
new file mode 100644
index 00000000000..8a2aae706c0
--- /dev/null
+++ b/app/assets/javascripts/pages/dashboard/groups/index/index.js
@@ -0,0 +1,5 @@
+import initGroupsList from '../../../../groups';
+
+export default () => {
+ initGroupsList();
+};
diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js
index 859b073f1cb..e59c38b8bc4 100644
--- a/app/assets/javascripts/pages/explore/groups/index.js
+++ b/app/assets/javascripts/pages/explore/groups/index.js
@@ -1,8 +1,10 @@
import GroupsList from '~/groups_list';
import Landing from '~/landing';
+import initGroupsList from '../../../groups';
export default function () {
new GroupsList(); // eslint-disable-line no-new
+ initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing');
if (!landingElement) return;
const exploreGroupsLanding = new Landing(
diff --git a/app/assets/javascripts/pages/groups/activity/index.js b/app/assets/javascripts/pages/groups/activity/index.js
new file mode 100644
index 00000000000..95faf1f1e98
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/activity/index.js
@@ -0,0 +1,3 @@
+import Activities from '~/activities';
+
+export default () => new Activities();
diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js
new file mode 100644
index 00000000000..48e8c9550bf
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/edit/index.js
@@ -0,0 +1,3 @@
+import groupAvatar from '~/group_avatar';
+
+export default groupAvatar;
diff --git a/app/assets/javascripts/pages/groups/group_members/index/index.js b/app/assets/javascripts/pages/groups/group_members/index/index.js
new file mode 100644
index 00000000000..29319b97ae2
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/group_members/index/index.js
@@ -0,0 +1,11 @@
+/* eslint-disable no-new */
+
+import memberExpirationDate from '~/member_expiration_date';
+import Members from '~/members';
+import UsersSelect from '~/users_select';
+
+export default () => {
+ memberExpirationDate();
+ new Members();
+ new UsersSelect();
+};
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
new file mode 100644
index 00000000000..78db543a64d
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -0,0 +1,8 @@
+import projectSelect from '~/project_select';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import { FILTERED_SEARCH } from '~/pages/constants';
+
+export default () => {
+ initFilteredSearch(FILTERED_SEARCH.ISSUES);
+ projectSelect();
+};
diff --git a/app/assets/javascripts/pages/groups/labels/edit/index.js b/app/assets/javascripts/pages/groups/labels/edit/index.js
new file mode 100644
index 00000000000..72c5e4744ac
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/labels/edit/index.js
@@ -0,0 +1,3 @@
+import Labels from '~/labels';
+
+export default () => new Labels();
diff --git a/app/assets/javascripts/pages/groups/labels/index/index.js b/app/assets/javascripts/pages/groups/labels/index/index.js
new file mode 100644
index 00000000000..018345fa112
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/labels/index/index.js
@@ -0,0 +1,3 @@
+import initLabels from '~/init_labels';
+
+export default initLabels;
diff --git a/app/assets/javascripts/pages/groups/labels/new/index.js b/app/assets/javascripts/pages/groups/labels/new/index.js
new file mode 100644
index 00000000000..72c5e4744ac
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/labels/new/index.js
@@ -0,0 +1,3 @@
+import Labels from '~/labels';
+
+export default () => new Labels();
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
new file mode 100644
index 00000000000..9b3af4537e7
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -0,0 +1,8 @@
+import projectSelect from '~/project_select';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import { FILTERED_SEARCH } from '~/pages/constants';
+
+export default () => {
+ initFilteredSearch(FILTERED_SEARCH.MERGE_REQUESTS);
+ projectSelect();
+};
diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js
new file mode 100644
index 00000000000..c9a18353f2e
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/milestones/show/index.js
@@ -0,0 +1,3 @@
+import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
+
+export default initMilestonesShow;
diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js
new file mode 100644
index 00000000000..7850b90d3d2
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/new/index.js
@@ -0,0 +1,9 @@
+import BindInOut from '~/behaviors/bind_in_out';
+import Group from '~/group';
+import groupAvatar from '~/group_avatar';
+
+export default () => {
+ BindInOut.initAll();
+ new Group(); // eslint-disable-line no-new
+ groupAvatar();
+};
diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
new file mode 100644
index 00000000000..f26c7360fbe
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
@@ -0,0 +1,11 @@
+import SecretValues from '~/behaviors/secret_values';
+
+export default () => {
+ const secretVariableTable = document.querySelector('.js-secret-variable-table');
+ if (secretVariableTable) {
+ const secretVariableTableValues = new SecretValues({
+ container: secretVariableTable,
+ });
+ secretVariableTableValues.init();
+ }
+};
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
new file mode 100644
index 00000000000..6ed0f010f15
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -0,0 +1,22 @@
+/* eslint-disable no-new */
+
+import NewGroupChild from '~/groups/new_group_child';
+import notificationsDropdown from '~/notifications_dropdown';
+import NotificationsForm from '~/notifications_form';
+import ProjectsList from '~/projects_list';
+import ShortcutsNavigation from '~/shortcuts_navigation';
+import initGroupsList from '../../../groups';
+
+export default () => {
+ const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
+ new ShortcutsNavigation();
+ new NotificationsForm();
+ notificationsDropdown();
+ new ProjectsList();
+
+ if (newGroupChildWrapper) {
+ new NewGroupChild(newGroupChildWrapper);
+ }
+
+ initGroupsList();
+};
diff --git a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
new file mode 100644
index 00000000000..c43e0a0490f
--- /dev/null
+++ b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
@@ -0,0 +1,110 @@
+<script>
+ import axios from '~/lib/utils/axios_utils';
+
+ import Flash from '~/flash';
+ import modal from '~/vue_shared/components/modal.vue';
+ import { n__, s__, sprintf } from '~/locale';
+ import { redirectTo } from '~/lib/utils/url_utility';
+ import eventHub from '../event_hub';
+
+ export default {
+ components: {
+ modal,
+ },
+ props: {
+ issueCount: {
+ type: Number,
+ required: true,
+ },
+ mergeRequestCount: {
+ type: Number,
+ required: true,
+ },
+ milestoneId: {
+ type: Number,
+ required: true,
+ },
+ milestoneTitle: {
+ type: String,
+ required: true,
+ },
+ milestoneUrl: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ text() {
+ const milestoneTitle = sprintf('<strong>%{milestoneTitle}</strong>', { milestoneTitle: this.milestoneTitle });
+
+ if (this.issueCount === 0 && this.mergeRequestCount === 0) {
+ return sprintf(
+ s__(`Milestones|
+You’re about to permanently delete the milestone %{milestoneTitle} from this project.
+%{milestoneTitle} is not currently used in any issues or merge requests.`),
+ {
+ milestoneTitle,
+ },
+ false,
+ );
+ }
+
+ return sprintf(
+ s__(`Milestones|
+You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
+Once deleted, it cannot be undone or recovered.`),
+ {
+ milestoneTitle,
+ issuesWithCount: n__('%d issue', '%d issues', this.issueCount),
+ mergeRequestsWithCount: n__('%d merge request', '%d merge requests', this.mergeRequestCount),
+ },
+ false,
+ );
+ },
+ title() {
+ return sprintf(s__('Milestones|Delete milestone %{milestoneTitle}?'), { milestoneTitle: this.milestoneTitle });
+ },
+ },
+ methods: {
+ onSubmit() {
+ eventHub.$emit('deleteMilestoneModal.requestStarted', this.milestoneUrl);
+
+ return axios.delete(this.milestoneUrl)
+ .then((response) => {
+ eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: true });
+
+ // follow the rediect to milestones overview page
+ redirectTo(response.request.responseURL);
+ })
+ .catch((error) => {
+ eventHub.$emit('deleteMilestoneModal.requestFinished', { milestoneUrl: this.milestoneUrl, successful: false });
+
+ if (error.response && error.response.status === 404) {
+ Flash(sprintf(s__('Milestones|Milestone %{milestoneTitle} was not found'), { milestoneTitle: this.milestoneTitle }));
+ } else {
+ Flash(sprintf(s__('Milestones|Failed to delete milestone %{milestoneTitle}'), { milestoneTitle: this.milestoneTitle }));
+ }
+ throw error;
+ });
+ },
+ },
+ };
+</script>
+
+<template>
+ <modal
+ id="delete-milestone-modal"
+ :title="title"
+ :text="text"
+ kind="danger"
+ :primary-button-label="s__('Milestones|Delete milestone')"
+ @submit="onSubmit">
+
+ <template
+ slot="body"
+ slot-scope="props">
+ <p v-html="props.text"></p>
+ </template>
+
+ </modal>
+</template>
diff --git a/app/assets/javascripts/pages/milestones/shared/event_hub.js b/app/assets/javascripts/pages/milestones/shared/event_hub.js
new file mode 100644
index 00000000000..0948c2e5352
--- /dev/null
+++ b/app/assets/javascripts/pages/milestones/shared/event_hub.js
@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue();
diff --git a/app/assets/javascripts/pages/milestones/shared/index.js b/app/assets/javascripts/pages/milestones/shared/index.js
new file mode 100644
index 00000000000..327e2cf569c
--- /dev/null
+++ b/app/assets/javascripts/pages/milestones/shared/index.js
@@ -0,0 +1,88 @@
+import Vue from 'vue';
+
+import Translate from '~/vue_shared/translate';
+
+import deleteMilestoneModal from './components/delete_milestone_modal.vue';
+import eventHub from './event_hub';
+
+export default () => {
+ Vue.use(Translate);
+
+ const onRequestFinished = ({ milestoneUrl, successful }) => {
+ const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
+
+ if (!successful) {
+ button.removeAttribute('disabled');
+ }
+
+ button.querySelector('.js-loading-icon').classList.add('hidden');
+ };
+
+ const onRequestStarted = (milestoneUrl) => {
+ const button = document.querySelector(`.js-delete-milestone-button[data-milestone-url="${milestoneUrl}"]`);
+ button.setAttribute('disabled', '');
+ button.querySelector('.js-loading-icon').classList.remove('hidden');
+ eventHub.$once('deleteMilestoneModal.requestFinished', onRequestFinished);
+ };
+
+ const onDeleteButtonClick = (event) => {
+ const button = event.currentTarget;
+ const modalProps = {
+ milestoneId: parseInt(button.dataset.milestoneId, 10),
+ milestoneTitle: button.dataset.milestoneTitle,
+ milestoneUrl: button.dataset.milestoneUrl,
+ issueCount: parseInt(button.dataset.milestoneIssueCount, 10),
+ mergeRequestCount: parseInt(button.dataset.milestoneMergeRequestCount, 10),
+ };
+ eventHub.$once('deleteMilestoneModal.requestStarted', onRequestStarted);
+ eventHub.$emit('deleteMilestoneModal.props', modalProps);
+ };
+
+ const deleteMilestoneButtons = document.querySelectorAll('.js-delete-milestone-button');
+ for (let i = 0; i < deleteMilestoneButtons.length; i += 1) {
+ const button = deleteMilestoneButtons[i];
+ button.addEventListener('click', onDeleteButtonClick);
+ }
+
+ eventHub.$once('deleteMilestoneModal.mounted', () => {
+ for (let i = 0; i < deleteMilestoneButtons.length; i += 1) {
+ const button = deleteMilestoneButtons[i];
+ button.removeAttribute('disabled');
+ }
+ });
+
+ return new Vue({
+ el: '#delete-milestone-modal',
+ components: {
+ deleteMilestoneModal,
+ },
+ data() {
+ return {
+ modalProps: {
+ milestoneId: -1,
+ milestoneTitle: '',
+ milestoneUrl: '',
+ issueCount: -1,
+ mergeRequestCount: -1,
+ },
+ };
+ },
+ mounted() {
+ eventHub.$on('deleteMilestoneModal.props', this.setModalProps);
+ eventHub.$emit('deleteMilestoneModal.mounted');
+ },
+ beforeDestroy() {
+ eventHub.$off('deleteMilestoneModal.props', this.setModalProps);
+ },
+ methods: {
+ setModalProps(modalProps) {
+ this.modalProps = modalProps;
+ },
+ },
+ render(createElement) {
+ return createElement(deleteMilestoneModal, {
+ props: this.modalProps,
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js
new file mode 100644
index 00000000000..7aa5be0d5b9
--- /dev/null
+++ b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js
@@ -0,0 +1,9 @@
+/* eslint-disable no-new */
+
+import Milestone from '~/milestone';
+import Sidebar from '~/right_sidebar';
+
+export default () => {
+ new Milestone();
+ new Sidebar();
+};
diff --git a/app/assets/javascripts/pages/omniauth_callbacks/index.js b/app/assets/javascripts/pages/omniauth_callbacks/index.js
new file mode 100644
index 00000000000..54f4e56359a
--- /dev/null
+++ b/app/assets/javascripts/pages/omniauth_callbacks/index.js
@@ -0,0 +1,5 @@
+import initU2F from '../../shared/sessions/u2f';
+
+export default () => {
+ initU2F();
+};
diff --git a/app/assets/javascripts/pages/projects/constants.js b/app/assets/javascripts/pages/projects/constants.js
new file mode 100644
index 00000000000..9efbf7cd36e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/constants.js
@@ -0,0 +1,6 @@
+/* eslint-disable import/prefer-default-export */
+
+export const ISSUABLE_INDEX = {
+ MERGE_REQUEST: 'merge_request_',
+ ISSUE: 'issue_',
+};
diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js
index 7f662ef6b6a..9edf36d66b1 100644
--- a/app/assets/javascripts/pages/projects/edit/index.js
+++ b/app/assets/javascripts/pages/projects/edit/index.js
@@ -1,8 +1,14 @@
import initSettingsPanels from '~/settings_panels';
import setupProjectEdit from '~/project_edit';
+import ProjectNew from '../shared/project_new';
+import projectAvatar from '../shared/project_avatar';
+import initProjectPermissionsSettings from '../shared/permissions';
export default () => {
+ new ProjectNew(); // eslint-disable-line no-new
setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
+ projectAvatar();
+ initProjectPermissionsSettings();
};
diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js
new file mode 100644
index 00000000000..f4760cb2720
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js
@@ -0,0 +1,3 @@
+import monitoringBundle from '~/monitoring/monitoring_bundle';
+
+export default monitoringBundle;
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
new file mode 100644
index 00000000000..9b1d52692a3
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -0,0 +1,7 @@
+import Project from './project';
+import ShortcutsNavigation from '../../shortcuts_navigation';
+
+export default () => {
+ new Project(); // eslint-disable-line no-new
+ new ShortcutsNavigation(); // eslint-disable-line no-new
+};
diff --git a/app/assets/javascripts/pages/projects/init_form.js b/app/assets/javascripts/pages/projects/init_form.js
new file mode 100644
index 00000000000..0b6c5c1d30b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/init_form.js
@@ -0,0 +1,7 @@
+import ZenMode from '~/zen_mode';
+import GLForm from '~/gl_form';
+
+export default function ($formEl) {
+ new ZenMode(); // eslint-disable-line no-new
+ new GLForm($formEl, true); // eslint-disable-line no-new
+}
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index fd395a45f00..0d3f35f044d 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -1,17 +1,15 @@
-
/* eslint-disable no-new */
import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/shortcuts_navigation';
import UsersSelect from '~/users_select';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import { FILTERED_SEARCH } from '~/pages/constants';
+import { ISSUABLE_INDEX } from '~/pages/projects/constants';
export default () => {
- const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
- if (filteredSearchEnabled) {
- const filteredSearchManager = new gl.FilteredSearchManager('issues');
- filteredSearchManager.setup();
- }
- new IssuableIndex('issue_');
+ initFilteredSearch(FILTERED_SEARCH.ISSUES);
+ new IssuableIndex(ISSUABLE_INDEX.ISSUE);
new ShortcutsNavigation();
new UsersSelect();
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js
new file mode 100644
index 00000000000..734d01ae6f2
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js
@@ -0,0 +1,3 @@
+import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
+
+export default initMergeRequest;
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
new file mode 100644
index 00000000000..ccd0b54c5ed
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
@@ -0,0 +1,18 @@
+import Compare from '~/compare';
+import MergeRequest from '~/merge_request';
+
+export default () => {
+ const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
+ if (mrNewCompareNode) {
+ new Compare({ // eslint-disable-line no-new
+ targetProjectUrl: mrNewCompareNode.dataset.targetProjectUrl,
+ sourceBranchUrl: mrNewCompareNode.dataset.sourceBranchUrl,
+ targetBranchUrl: mrNewCompareNode.dataset.targetBranchUrl,
+ });
+ } else {
+ const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit');
+ new MergeRequest({ // eslint-disable-line no-new
+ action: mrNewSubmitNode.dataset.mrSubmitAction,
+ });
+ }
+};
diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
new file mode 100644
index 00000000000..734d01ae6f2
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
@@ -0,0 +1,3 @@
+import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
+
+export default initMergeRequest;
diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
index a52bea03aa2..b386e8fb48d 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
@@ -1,16 +1,13 @@
import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/shortcuts_navigation';
import UsersSelect from '~/users_select';
+import initFilteredSearch from '~/pages/search/init_filtered_search';
+import { FILTERED_SEARCH } from '~/pages/constants';
+import { ISSUABLE_INDEX } from '~/pages/projects/constants';
export default () => {
- const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
-
- if (filteredSearchEnabled) {
- const filteredSearchManager = new gl.FilteredSearchManager('merge_requests');
- filteredSearchManager.setup();
- }
-
- new IssuableIndex('merge_request_'); // eslint-disable-line no-new
+ initFilteredSearch(FILTERED_SEARCH.MERGE_REQUESTS);
+ new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
new UsersSelect(); // eslint-disable-line no-new
};
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
new file mode 100644
index 00000000000..8bfac606aab
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
@@ -0,0 +1,19 @@
+/* eslint-disable no-new */
+
+import Diff from '~/diff';
+import ShortcutsNavigation from '~/shortcuts_navigation';
+import GLForm from '~/gl_form';
+import IssuableForm from '~/issuable_form';
+import LabelsSelect from '~/labels_select';
+import MilestoneSelect from '~/milestone_select';
+import IssuableTemplateSelectors from '~/templates/issuable_template_selectors';
+
+export default () => {
+ new Diff();
+ new ShortcutsNavigation();
+ new GLForm($('.merge-request-form'), true);
+ new IssuableForm($('.merge-request-form'));
+ new LabelsSelect();
+ new MilestoneSelect();
+ new IssuableTemplateSelectors();
+};
diff --git a/app/assets/javascripts/pages/projects/milestones/index/index.js b/app/assets/javascripts/pages/projects/milestones/index/index.js
new file mode 100644
index 00000000000..8fb4d83d8a3
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/milestones/index/index.js
@@ -0,0 +1,3 @@
+import milestones from '~/pages/milestones/shared';
+
+export default milestones;
diff --git a/app/assets/javascripts/pages/projects/milestones/show/index.js b/app/assets/javascripts/pages/projects/milestones/show/index.js
new file mode 100644
index 00000000000..35b5c9c2ced
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/milestones/show/index.js
@@ -0,0 +1,7 @@
+import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
+import milestones from '~/pages/milestones/shared';
+
+export default () => {
+ initMilestonesShow();
+ milestones();
+};
diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js
new file mode 100644
index 00000000000..71c49deb9d0
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/new/index.js
@@ -0,0 +1,9 @@
+import ProjectNew from '../shared/project_new';
+import initProjectVisibilitySelector from '../../../project_visibility';
+import initProjectNew from '../../../projects/project_new';
+
+export default () => {
+ new ProjectNew(); // eslint-disable-line no-new
+ initProjectVisibilitySelector();
+ initProjectNew.bindEvents();
+};
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/pages/projects/project.js
index d4f26b81f30..e30d558726b 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -1,8 +1,8 @@
/* eslint-disable func-names, space-before-function-paren, no-var, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */
import Cookies from 'js-cookie';
-import { visitUrl } from './lib/utils/url_utility';
-import projectSelect from './project_select';
+import { visitUrl } from '../../lib/utils/url_utility';
+import projectSelect from '../../project_select';
export default class Project {
constructor() {
diff --git a/app/assets/javascripts/pages/projects/releases/edit/index.js b/app/assets/javascripts/pages/projects/releases/edit/index.js
new file mode 100644
index 00000000000..3d997cdfff0
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/releases/edit/index.js
@@ -0,0 +1,3 @@
+import initForm from '~/pages/projects/init_form';
+
+export default initForm($('.release-form'));
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
new file mode 100644
index 00000000000..18dc1dc03a5
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -0,0 +1,22 @@
+import initSettingsPanels from '~/settings_panels';
+import SecretValues from '~/behaviors/secret_values';
+
+export default function () {
+ // Initialize expandable settings panels
+ initSettingsPanels();
+ const runnerToken = document.querySelector('.js-secret-runner-token');
+ if (runnerToken) {
+ const runnerTokenSecretValue = new SecretValues({
+ container: runnerToken,
+ });
+ runnerTokenSecretValue.init();
+ }
+
+ const secretVariableTable = document.querySelector('.js-secret-variable-table');
+ if (secretVariableTable) {
+ const secretVariableTableValues = new SecretValues({
+ container: secretVariableTable,
+ });
+ secretVariableTableValues.init();
+ }
+}
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
new file mode 100644
index 00000000000..83b5467fbc0
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
@@ -0,0 +1,3 @@
+import initSettingsPanels from '~/settings_panels';
+
+export default initSettingsPanels;
diff --git a/app/assets/javascripts/projects/permissions/components/project_feature_setting.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
index 3ebfe82597a..9b13b2a524f 100644
--- a/app/assets/javascripts/projects/permissions/components/project_feature_setting.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
@@ -1,5 +1,5 @@
<script>
- import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue';
+ import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
export default {
components: {
diff --git a/app/assets/javascripts/projects/permissions/components/project_setting_row.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
index 25a88f846eb..25a88f846eb 100644
--- a/app/assets/javascripts/projects/permissions/components/project_setting_row.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue
diff --git a/app/assets/javascripts/projects/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index c96ce12d9fb..755a34b7348 100644
--- a/app/assets/javascripts/projects/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -1,6 +1,6 @@
<script>
import projectFeatureSetting from './project_feature_setting.vue';
- import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue';
+ import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
import projectSettingRow from './project_setting_row.vue';
import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
import { toggleHiddenClassBySelector } from '../external';
@@ -215,7 +215,7 @@
</div>
<span class="help-block">{{ visibilityLevelDescription }}</span>
<label
- v-if="visibilityLevel !== visibilityOptions.PUBLIC"
+ v-if="visibilityLevel !== visibilityOptions.PRIVATE"
class="request-access"
>
<input
diff --git a/app/assets/javascripts/projects/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
index ce47562f259..ce47562f259 100644
--- a/app/assets/javascripts/projects/permissions/constants.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
diff --git a/app/assets/javascripts/projects/permissions/external.js b/app/assets/javascripts/pages/projects/shared/permissions/external.js
index 460af4a2111..460af4a2111 100644
--- a/app/assets/javascripts/projects/permissions/external.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/external.js
diff --git a/app/assets/javascripts/projects/permissions/index.js b/app/assets/javascripts/pages/projects/shared/permissions/index.js
index dbde8dda634..dbde8dda634 100644
--- a/app/assets/javascripts/projects/permissions/index.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/index.js
diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/pages/projects/shared/project_avatar.js
index 56627aa155c..56627aa155c 100644
--- a/app/assets/javascripts/project_avatar.js
+++ b/app/assets/javascripts/pages/projects/shared/project_avatar.js
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/pages/projects/shared/project_new.js
index ca548d011b6..86faba0b910 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/pages/projects/shared/project_new.js
@@ -1,6 +1,6 @@
/* eslint-disable func-names, no-var, no-underscore-dangle, prefer-template, prefer-arrow-callback*/
-import VisibilitySelect from './visibility_select';
+import VisibilitySelect from '../../../visibility_select';
function highlightChanges($elm) {
$elm.addClass('highlight-changes');
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
new file mode 100644
index 00000000000..55154cdddcb
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -0,0 +1,27 @@
+import ShortcutsNavigation from '~/shortcuts_navigation';
+import NotificationsForm from '~/notifications_form';
+import UserCallout from '~/user_callout';
+import TreeView from '~/tree';
+import BlobViewer from '~/blob/viewer/index';
+import Activities from '~/activities';
+import { ajaxGet } from '~/lib/utils/common_utils';
+import Star from '../../../star';
+import notificationsDropdown from '../../../notifications_dropdown';
+
+export default () => {
+ new Star(); // eslint-disable-line no-new
+ notificationsDropdown();
+ new ShortcutsNavigation(); // eslint-disable-line no-new
+ new NotificationsForm(); // eslint-disable-line no-new
+ new UserCallout({ // eslint-disable-line no-new
+ setCalloutPerProject: true,
+ className: 'js-autodevops-banner',
+ });
+
+ if ($('#tree-slider').length) new TreeView(); // eslint-disable-line no-new
+ if ($('.blob-viewer').length) new BlobViewer(); // eslint-disable-line no-new
+ if ($('.project-show-activity').length) new Activities(); // eslint-disable-line no-new
+ $('#tree-slider').waitForImages(() => {
+ ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
+ });
+};
diff --git a/app/assets/javascripts/pages/projects/snippets/edit/index.js b/app/assets/javascripts/pages/projects/snippets/edit/index.js
new file mode 100644
index 00000000000..9edb16dc73b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/snippets/edit/index.js
@@ -0,0 +1,3 @@
+import initForm from '~/pages/projects/init_form';
+
+export default initForm($('.snippet-form'));
diff --git a/app/assets/javascripts/pages/projects/snippets/new/index.js b/app/assets/javascripts/pages/projects/snippets/new/index.js
new file mode 100644
index 00000000000..9edb16dc73b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/snippets/new/index.js
@@ -0,0 +1,3 @@
+import initForm from '~/pages/projects/init_form';
+
+export default initForm($('.snippet-form'));
diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js
new file mode 100644
index 00000000000..a3cf75c385b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/snippets/show/index.js
@@ -0,0 +1,11 @@
+import initNotes from '~/init_notes';
+import ZenMode from '~/zen_mode';
+import LineHighlighter from '../../../../line_highlighter';
+import BlobViewer from '../../../../blob/viewer';
+
+export default function () {
+ new LineHighlighter(); // eslint-disable-line no-new
+ new BlobViewer(); // eslint-disable-line no-new
+ initNotes();
+ new ZenMode(); // eslint-disable-line no-new
+}
diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js
new file mode 100644
index 00000000000..eb14c7a0e78
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/wikis/index.js
@@ -0,0 +1,11 @@
+import Wikis from './wikis';
+import ShortcutsWiki from '../../../shortcuts_wiki';
+import ZenMode from '../../../zen_mode';
+import GLForm from '../../../gl_form';
+
+export default () => {
+ new Wikis(); // eslint-disable-line no-new
+ new ShortcutsWiki(); // eslint-disable-line no-new
+ new ZenMode(); // eslint-disable-line no-new
+ new GLForm($('.wiki-form'), true); // eslint-disable-line no-new
+};
diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/pages/projects/wikis/wikis.js
index 7a865587444..34a12ef76a1 100644
--- a/app/assets/javascripts/wikis.js
+++ b/app/assets/javascripts/pages/projects/wikis/wikis.js
@@ -1,5 +1,5 @@
-import bp from './breakpoints';
-import { slugify } from './lib/utils/text_utility';
+import bp from '../../../breakpoints';
+import { slugify } from '../../../lib/utils/text_utility';
export default class Wikis {
constructor() {
diff --git a/app/assets/javascripts/pages/search/init_filtered_search.js b/app/assets/javascripts/pages/search/init_filtered_search.js
new file mode 100644
index 00000000000..44853636aea
--- /dev/null
+++ b/app/assets/javascripts/pages/search/init_filtered_search.js
@@ -0,0 +1,7 @@
+export default (page) => {
+ const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
+ if (filteredSearchEnabled) {
+ const filteredSearchManager = new gl.FilteredSearchManager(page);
+ filteredSearchManager.setup();
+ }
+};
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index d44195f6b72..dc621bc87c0 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -15,6 +15,7 @@ export default class Search {
$groupDropdown.glDropdown({
selectable: true,
filterable: true,
+ filterRemote: true,
fieldName: 'group_id',
search: {
fields: ['full_name'],
@@ -43,6 +44,7 @@ export default class Search {
$projectDropdown.glDropdown({
selectable: true,
filterable: true,
+ filterRemote: true,
fieldName: 'project_id',
search: {
fields: ['name'],
diff --git a/app/assets/javascripts/pages/sessions/index.js b/app/assets/javascripts/pages/sessions/index.js
new file mode 100644
index 00000000000..54f4e56359a
--- /dev/null
+++ b/app/assets/javascripts/pages/sessions/index.js
@@ -0,0 +1,5 @@
+import initU2F from '../../shared/sessions/u2f';
+
+export default () => {
+ initU2F();
+};
diff --git a/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js b/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js
index f99573e5c74..08f0afdcce3 100644
--- a/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js
+++ b/app/assets/javascripts/pages/sessions/new/signin_tabs_memoizer.js
@@ -1,5 +1,3 @@
-/* eslint no-param-reassign: ["error", { "props": false }]*/
-/* eslint no-new: "off" */
import AccessorUtilities from '~/lib/utils/accessor';
/**
@@ -11,6 +9,10 @@ export default class SigninTabsMemoizer {
this.currentTabKey = currentTabKey;
this.tabSelector = tabSelector;
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
+ // sets selected tab if given as hash tag
+ if (window.location.hash) {
+ this.saveData(window.location.hash);
+ }
this.bootstrap();
}
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index c6638cdcf1e..6681b89e629 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -50,13 +50,13 @@
Pipeline
</div>
<div
- class="table-section section-25 js-pipeline-commit pipeline-commit"
+ class="table-section section-20 js-pipeline-commit pipeline-commit"
role="rowheader"
>
Commit
</div>
<div
- class="table-section section-15 js-pipeline-stages pipeline-stages"
+ class="table-section section-20 js-pipeline-stages pipeline-stages"
role="rowheader"
>
Stages
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index d87e24cc8a7..d0e4cf7ff40 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -239,7 +239,7 @@
:auto-devops-help-path="autoDevopsHelpPath"
/>
- <div class="table-section section-25">
+ <div class="table-section section-20">
<div
class="table-mobile-header"
role="rowheader">
@@ -258,7 +258,7 @@
</div>
</div>
- <div class="table-section section-wrap section-15 stage-cell">
+ <div class="table-section section-wrap section-20 stage-cell">
<div
class="table-mobile-header"
role="rowheader">
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 4710e70d619..f5133111d04 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -100,8 +100,6 @@ const bindEvents = () => {
$projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl));
};
-document.addEventListener('DOMContentLoaded', bindEvents);
-
export default {
bindEvents,
deriveProjectPathFromUrl,
diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js
index a759992cd54..15205d8a4e2 100644
--- a/app/assets/javascripts/render_math.js
+++ b/app/assets/javascripts/render_math.js
@@ -18,7 +18,7 @@ function renderWithKaTeX(elements) {
const display = $this.attr('data-math-style') === 'display';
try {
- katex.render($this.text(), mathNode.get(0), { displayMode: display });
+ katex.render($this.text(), mathNode.get(0), { displayMode: display, throwOnError: false });
mathNode.insertAfter($this);
$this.remove();
} catch (err) {
diff --git a/app/assets/javascripts/render_mermaid.js b/app/assets/javascripts/render_mermaid.js
index b7cde6fb092..31c7a772cf4 100644
--- a/app/assets/javascripts/render_mermaid.js
+++ b/app/assets/javascripts/render_mermaid.js
@@ -19,7 +19,11 @@ export default function renderMermaid($els) {
import(/* webpackChunkName: 'mermaid' */ 'blackst0ne-mermaid').then((mermaid) => {
mermaid.initialize({
- loadOnStart: false,
+ // mermaid core options
+ mermaid: {
+ startOnLoad: false,
+ },
+ // mermaidAPI options
theme: 'neutral',
});
diff --git a/app/assets/javascripts/shared/sessions/u2f.js b/app/assets/javascripts/shared/sessions/u2f.js
new file mode 100644
index 00000000000..1d075f7e872
--- /dev/null
+++ b/app/assets/javascripts/shared/sessions/u2f.js
@@ -0,0 +1,16 @@
+import U2FAuthenticate from '../../u2f/authenticate';
+
+export default () => {
+ if (!gon.u2f) return;
+
+ const u2fAuthenticate = new U2FAuthenticate(
+ $('#js-authenticate-u2f'),
+ '#js-login-u2f-form',
+ gon.u2f,
+ document.querySelector('#js-login-2fa-device'),
+ document.querySelector('.js-2fa-form'),
+ );
+ u2fAuthenticate.start();
+ // needed in rspec
+ gl.u2fAuthenticate = u2fAuthenticate;
+};
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index d2f0d7410da..cd5ab53eace 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -13,12 +13,10 @@ Mousetrap.stopCallback = (e, element, combo) => {
};
export default class Shortcuts {
- constructor(skipResetBindings) {
+ constructor() {
this.onToggleHelp = this.onToggleHelp.bind(this);
this.enabledHelp = [];
- if (!skipResetBindings) {
- Mousetrap.reset();
- }
+
Mousetrap.bind('?', this.onToggleHelp);
Mousetrap.bind('s', Shortcuts.focusSearch);
Mousetrap.bind('f', this.focusFilter.bind(this));
@@ -62,7 +60,7 @@ export default class Shortcuts {
e.preventDefault();
const performanceBarCookieName = 'perf_bar_enabled';
if (Cookies.get(performanceBarCookieName) === 'true') {
- Cookies.remove(performanceBarCookieName, { path: '/' });
+ Cookies.set(performanceBarCookieName, 'false', { path: '/' });
} else {
Cookies.set(performanceBarCookieName, 'true', { path: '/' });
}
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 6aeae84cdc5..689befc742e 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -1,10 +1,10 @@
import Mousetrap from 'mousetrap';
import _ from 'underscore';
import Sidebar from './right_sidebar';
-import ShortcutsNavigation from './shortcuts_navigation';
+import Shortcuts from './shortcuts';
import { CopyAsGFM } from './behaviors/copy_as_gfm';
-export default class ShortcutsIssuable extends ShortcutsNavigation {
+export default class ShortcutsIssuable extends Shortcuts {
constructor(isMergeRequest) {
super();
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.js b/app/assets/javascripts/sidebar/components/assignees/assignee_title.js
index 77f070d48cc..129ba2e4e89 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.js
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.js
@@ -39,7 +39,7 @@ export default {
class="js-sidebar-dropdown-toggle edit-link pull-right"
href="#"
>
- Edit
+ {{ __('Edit') }}
</a>
<a
v-if="showToggle"
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.js b/app/assets/javascripts/sidebar/components/assignees/assignees.js
index 7e5feac622c..643877b9d47 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.js
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.js
@@ -84,7 +84,7 @@ export default {
return !this.showLess || (index < this.defaultRenderCount && this.showLess);
},
avatarUrl(user) {
- return user.avatar || user.avatar_url;
+ return user.avatar || user.avatar_url || gon.default_avatar_url;
},
assigneeUrl(user) {
return `${this.rootPath}${user.username}`;
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 839f9ec88b9..02153fb86a5 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -65,7 +65,7 @@
href="#"
@click.prevent="toggleForm"
>
- Edit
+ {{ __('Edit') }}
</a>
</div>
<div class="value sidebar-item-value hide-collapsed">
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js
index 8e167f5bf08..4cc1c96b870 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js
+++ b/app/assets/javascripts/templates/issuable_template_selector.js
@@ -32,8 +32,8 @@ export default class IssuableTemplateSelector extends TemplateSelector {
this.startLoadingSpinner();
Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => {
this.currentTemplate = currentTemplate;
- if (err) return; // Error handled by global AJAX error handler
this.stopLoadingSpinner();
+ if (err) return; // Error handled by global AJAX error handler
this.setInputValueToTemplateContent();
});
return;
diff --git a/app/assets/javascripts/toggle_buttons.js b/app/assets/javascripts/toggle_buttons.js
new file mode 100644
index 00000000000..974dc3ee052
--- /dev/null
+++ b/app/assets/javascripts/toggle_buttons.js
@@ -0,0 +1,61 @@
+import $ from 'jquery';
+import Flash from './flash';
+import { __ } from './locale';
+import { convertPermissionToBoolean } from './lib/utils/common_utils';
+
+/*
+ example HAML:
+ ```
+ %button.js-project-feature-toggle.project-feature-toggle{ type: "button",
+ class: "#{'is-checked' if enabled?}",
+ 'aria-label': _('Toggle Cluster') }
+ %input{ type: "hidden", class: 'js-project-feature-toggle-input', value: enabled? }
+ ```
+*/
+
+function updatetoggle(toggle, isOn) {
+ toggle.classList.toggle('is-checked', isOn);
+}
+
+function onToggleClicked(toggle, input, clickCallback) {
+ const previousIsOn = convertPermissionToBoolean(input.value);
+
+ // Visually change the toggle and start loading
+ updatetoggle(toggle, !previousIsOn);
+ toggle.setAttribute('disabled', true);
+ toggle.classList.toggle('is-loading', true);
+
+ Promise.resolve(clickCallback(!previousIsOn, toggle))
+ .then(() => {
+ // Actually change the input value
+ input.setAttribute('value', !previousIsOn);
+ })
+ .catch(() => {
+ // Revert the visuals if something goes wrong
+ updatetoggle(toggle, previousIsOn);
+ })
+ .then(() => {
+ // Remove the loading indicator in any case
+ toggle.removeAttribute('disabled');
+ toggle.classList.toggle('is-loading', false);
+
+ $(input).trigger('trigger-change');
+ })
+ .catch(() => {
+ Flash(__('Something went wrong when toggling the button'));
+ });
+}
+
+export default function setupToggleButtons(container, clickCallback = () => {}) {
+ const toggles = container.querySelectorAll('.js-project-feature-toggle');
+
+ toggles.forEach((toggle) => {
+ const input = toggle.querySelector('.js-project-feature-toggle-input');
+ const isOn = convertPermissionToBoolean(input.value);
+
+ // Get the visible toggle in sync with the hidden input
+ updatetoggle(toggle, isOn);
+
+ toggle.addEventListener('click', onToggleClicked.bind(null, toggle, input, clickCallback));
+ });
+}
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index f249bd036d6..ab108906732 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -492,7 +492,7 @@ function UsersSelect(currentUser, els, options = {}) {
renderRow: function(user) {
var avatar, img, listClosingTags, listWithName, listWithUserName, username;
username = user.username ? "@" + user.username : "";
- avatar = user.avatar_url ? user.avatar_url : false;
+ avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url;
let selected = false;
@@ -513,9 +513,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (user.beforeDivider != null) {
`<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${_.escape(user.name)}</a></li>`;
} else {
- if (avatar) {
- img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
- }
+ img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
}
return `
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
index d48f3a01420..d174a900f63 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
@@ -2,7 +2,7 @@ import { getTimeago } from '~/lib/utils/datetime_utility';
import { visitUrl } from '../../lib/utils/url_utility';
import Flash from '../../flash';
import MemoryUsage from './mr_widget_memory_usage';
-import StatusIcon from './mr_widget_status_icon';
+import StatusIcon from './mr_widget_status_icon.vue';
import MRWidgetService from '../services/mr_widget_service';
export default {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js
index 85bfd03a3cf..de6e5149a87 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js
@@ -76,6 +76,7 @@ export default {
<a
href="#modal_merge_info"
data-toggle="modal"
+ :disabled="mr.sourceBranchRemoved"
class="btn btn-sm inline">
Check out branch
</a>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js
deleted file mode 100644
index eeb990908f6..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import ciIcon from '../../vue_shared/components/ci_icon.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
-export default {
- props: {
- status: { type: String, required: true },
- showDisabledButton: { type: Boolean, required: false },
- },
- components: {
- ciIcon,
- loadingIcon,
- },
- computed: {
- statusObj() {
- return {
- group: this.status,
- icon: `status_${this.status}`,
- };
- },
- },
- template: `
- <div class="space-children flex-container-block append-right-10">
- <div v-if="status === 'loading'" class="mr-widget-icon">
- <loading-icon />
- </div>
- <ci-icon v-else :status="statusObj" />
- <button
- v-if="showDisabledButton"
- type="button"
- class="js-disabled-merge-button btn btn-success btn-sm"
- disabled="true">
- Merge
- </button>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
new file mode 100644
index 00000000000..1fdc3218671
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -0,0 +1,57 @@
+<script>
+ import ciIcon from '../../vue_shared/components/ci_icon.vue';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+ export default {
+ components: {
+ ciIcon,
+ loadingIcon,
+ },
+ props: {
+ status: {
+ type: String,
+ required: true,
+ },
+ showDisabledButton: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ isLoading() {
+ return this.status === 'loading';
+ },
+ statusObj() {
+ return {
+ group: this.status,
+ icon: `status_${this.status}`,
+ };
+ },
+ },
+ };
+</script>
+<template>
+ <div class="space-children flex-container-block append-right-10">
+ <div
+ v-if="isLoading"
+ class="mr-widget-icon"
+ >
+ <loading-icon />
+ </div>
+
+ <ci-icon
+ v-else
+ :status="statusObj"
+ />
+
+ <button
+ v-if="showDisabledButton"
+ type="button"
+ class="js-disabled-merge-button btn btn-success btn-sm"
+ disabled="true"
+ >
+ {{ s__("mrWidget|Merge") }}
+ </button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.js
deleted file mode 100644
index b4e4a6aa161..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetArchived',
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <div class="space-children">
- <status-icon status="failed" />
- <button
- type="button"
- class="btn btn-success btn-sm"
- disabled="true">
- Merge
- </button>
- </div>
- <div class="media-body">
- <span class="bold">
- This project is archived, write access has been disabled
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
new file mode 100644
index 00000000000..cfbd44d41b2
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
@@ -0,0 +1,31 @@
+<script>
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetArchived',
+ components: {
+ statusIcon,
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <div class="space-children">
+ <status-icon
+ status="warning"
+ />
+ <button
+ type="button"
+ class="btn btn-success btn-sm"
+ disabled="true"
+ >
+ {{ s__("mrWidget|Merge") }}
+ </button>
+ </div>
+ <div class="media-body">
+ <span class="bold">
+ {{ s__("mrWidget|This project is archived, write access has been disabled") }}
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.js
deleted file mode 100644
index 5648208f7b1..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import eventHub from '../../event_hub';
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetAutoMergeFailed',
- props: {
- mr: { type: Object, required: true },
- },
- data() {
- return {
- isRefreshing: false,
- };
- },
- components: {
- statusIcon,
- },
- methods: {
- refreshWidget() {
- this.isRefreshing = true;
- eventHub.$emit('MRWidgetUpdateRequested', () => {
- this.isRefreshing = false;
- });
- },
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="failed" />
- <div class="media-body space-children">
- <span class="bold">
- <template v-if="mr.mergeError">{{mr.mergeError}}.</template>
- This merge request failed to be merged automatically
- </span>
- <button
- @click="refreshWidget"
- :disabled="isRefreshing"
- type="button"
- class="btn btn-xs btn-default">
- <i
- v-if="isRefreshing"
- class="fa fa-spinner fa-spin"
- aria-hidden="true" />
- Refresh
- </button>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
new file mode 100644
index 00000000000..40c3cb500bb
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -0,0 +1,52 @@
+<script>
+ import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+ import eventHub from '../../event_hub';
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetAutoMergeFailed',
+ components: {
+ statusIcon,
+ loadingIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isRefreshing: false,
+ };
+ },
+ methods: {
+ refreshWidget() {
+ this.isRefreshing = true;
+ eventHub.$emit('MRWidgetUpdateRequested', () => {
+ this.isRefreshing = false;
+ });
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon status="warning" />
+ <div class="media-body space-children">
+ <span class="bold">
+ <template v-if="mr.mergeError">{{ mr.mergeError }}.</template>
+ {{ s__("mrWidget|This merge request failed to be merged automatically") }}
+ </span>
+ <button
+ @click="refreshWidget"
+ :disabled="isRefreshing"
+ type="button"
+ class="btn btn-xs btn-default"
+ >
+ <loading-icon v-if="isRefreshing" />
+ {{ s__("mrWidget|Refresh") }}
+ </button>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.js
deleted file mode 100644
index 09561694939..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetChecking',
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="loading" :show-disabled-button="true" />
- <div class="media-body space-children">
- <span class="bold">
- Checking ability to merge automatically
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
new file mode 100644
index 00000000000..caeaac75b45
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -0,0 +1,23 @@
+<script>
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetChecking',
+ components: {
+ statusIcon,
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon
+ status="loading"
+ :show-disabled-button="true"
+ />
+ <div class="media-body space-children">
+ <span class="bold">
+ {{ s__("mrWidget|Checking ability to merge automatically") }}
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js
deleted file mode 100644
index dd8b2665b1d..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetClosed',
- props: {
- mr: { type: Object, required: true },
- },
- components: {
- 'mr-widget-author-and-time': mrWidgetAuthorTime,
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="failed" />
- <div class="media-body">
- <mr-widget-author-and-time
- actionText="Closed by"
- :author="mr.metrics.closedBy"
- :dateTitle="mr.metrics.closedAt"
- :dateReadable="mr.metrics.readableClosedAt"
- />
- <section class="mr-info-list">
- <p>
- The changes were not merged into
- <a
- :href="mr.targetBranchPath"
- class="label-branch">
- {{mr.targetBranch}}</a>
- </p>
- </section>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
new file mode 100644
index 00000000000..71bfdaf801e
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -0,0 +1,48 @@
+<script>
+ import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetClosed',
+ components: {
+ mrWidgetAuthorTime,
+ statusIcon,
+ },
+ props: {
+ /* TODO: This is providing all store and service down when it
+ only needs metrics and targetBranch */
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon
+ status="warning"
+ />
+ <div class="media-body">
+ <mr-widget-author-time
+ :action-text="s__('mrWidget|Closed by')"
+ :author="mr.metrics.closedBy"
+ :date-title="mr.metrics.closedAt"
+ :date-readable="mr.metrics.readableClosedAt"
+ />
+
+ <section class="mr-info-list">
+ <p>
+ {{ s__("mrWidget|The changes were not merged into") }}
+ <a
+ :href="mr.targetBranchPath"
+ class="label-branch"
+ >
+ {{ mr.targetBranch }}
+ </a>
+ </p>
+ </section>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js
deleted file mode 100644
index 5d468a085cb..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetConflicts',
- props: {
- mr: { type: Object, required: true },
- },
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon
- status="failed"
- :show-disabled-button="true" />
- <div class="media-body space-children">
- <span
- v-if="mr.shouldBeRebased"
- class="bold">
- Fast-forward merge is not possible.
- To merge this request, first rebase locally.
- </span>
- <template v-else>
- <span class="bold">
- There are merge conflicts<span v-if="!mr.canMerge">.</span>
- <span v-if="!mr.canMerge">
- Resolve these conflicts or ask someone with write access to this repository to merge it locally
- </span>
- </span>
- <a
- v-if="mr.canMerge && mr.conflictResolutionPath"
- :href="mr.conflictResolutionPath"
- class="js-resolve-conflicts-button btn btn-default btn-xs">
- Resolve conflicts
- </a>
- <a
- v-if="mr.canMerge"
- class="js-merge-locally-button btn btn-default btn-xs"
- data-toggle="modal"
- href="#modal_merge_info">
- Merge locally
- </a>
- </template>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
new file mode 100644
index 00000000000..dad4b0fe49d
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
@@ -0,0 +1,61 @@
+<script>
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetConflicts',
+ components: {
+ statusIcon,
+ },
+ props: {
+ /* TODO: This is providing all store and service down when it
+ only needs a few props */
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon
+ status="warning"
+ :show-disabled-button="true"
+ />
+
+ <div class="media-body space-children">
+ <span
+ v-if="mr.shouldBeRebased"
+ class="bold"
+ >
+ {{ s__(`mrWidget|Fast-forward merge is not possible.
+To merge this request, first rebase locally.`) }}
+ </span>
+ <template v-else>
+ <span class="bold">
+ {{ s__("mrWidget|There are merge conflicts") }}<span v-if="!mr.canMerge">.</span>
+ <span v-if="!mr.canMerge">
+ {{ s__(`mrWidget|Resolve these conflicts or ask someone
+ with write access to this repository to merge it locally`) }}
+ </span>
+ </span>
+ <a
+ v-if="mr.canMerge && mr.conflictResolutionPath"
+ :href="mr.conflictResolutionPath"
+ class="js-resolve-conflicts-button btn btn-default btn-xs"
+ >
+ {{ s__("mrWidget|Resolve conflicts") }}
+ </a>
+ <button
+ v-if="mr.canMerge"
+ class="js-merge-locally-button btn btn-default btn-xs"
+ data-toggle="modal"
+ data-target="#modal_merge_info"
+ >
+ {{ s__("mrWidget|Merge locally") }}
+ </button>
+ </template>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js
deleted file mode 100644
index c25d6c359bb..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import statusIcon from '../mr_widget_status_icon';
-import eventHub from '../../event_hub';
-
-export default {
- name: 'MRWidgetFailedToMerge',
- props: {
- mr: { type: Object, required: true },
- },
- data() {
- return {
- timer: 10,
- isRefreshing: false,
- };
- },
- mounted() {
- setInterval(() => {
- this.updateTimer();
- }, 1000);
- },
- created() {
- eventHub.$emit('DisablePolling');
- },
- computed: {
- timerText() {
- return this.timer > 1 ? `${this.timer} seconds` : 'a second';
- },
- },
- methods: {
- refresh() {
- this.isRefreshing = true;
- eventHub.$emit('MRWidgetUpdateRequested');
- eventHub.$emit('EnablePolling');
- },
- updateTimer() {
- this.timer = this.timer - 1;
-
- if (this.timer === 0) {
- this.refresh();
- }
- },
- },
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <template v-if="isRefreshing">
- <status-icon status="loading" />
- <span class="media-body bold js-refresh-label">
- Refreshing now
- </span>
- </template>
- <template v-else>
- <status-icon status="failed" :show-disabled-button="true" />
- <div class="media-body space-children">
- <span class="bold">
- <span
- class="has-error-message"
- v-if="mr.mergeError">
- {{mr.mergeError}}.
- </span>
- <span v-else>Merge failed.</span>
- <span
- :class="{ 'has-custom-error': mr.mergeError }">
- Refreshing in {{timerText}} to show the updated status...
- </span>
- </span>
- <button
- @click="refresh"
- class="btn btn-default btn-xs js-refresh-button"
- type="button">
- Refresh now
- </button>
- </div>
- </template>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
new file mode 100644
index 00000000000..602b68ea572
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
@@ -0,0 +1,105 @@
+<script>
+ import { n__ } from '~/locale';
+ import statusIcon from '../mr_widget_status_icon.vue';
+ import eventHub from '../../event_hub';
+
+ export default {
+ name: 'MRWidgetFailedToMerge',
+
+ components: {
+ statusIcon,
+ },
+
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+
+ data() {
+ return {
+ timer: 10,
+ isRefreshing: false,
+ };
+ },
+
+ computed: {
+ timerText() {
+ return n__(
+ 'Refreshing in a second to show the updated status...',
+ 'Refreshing in %d seconds to show the updated status...',
+ this.timer,
+ );
+ },
+ },
+
+ mounted() {
+ setInterval(() => {
+ this.updateTimer();
+ }, 1000);
+ },
+
+ created() {
+ eventHub.$emit('DisablePolling');
+ },
+
+ methods: {
+ refresh() {
+ this.isRefreshing = true;
+ eventHub.$emit('MRWidgetUpdateRequested');
+ eventHub.$emit('EnablePolling');
+ },
+ updateTimer() {
+ this.timer = this.timer - 1;
+
+ if (this.timer === 0) {
+ this.refresh();
+ }
+ },
+ },
+
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <template v-if="isRefreshing">
+ <status-icon status="loading" />
+ <span class="media-body bold js-refresh-label">
+ {{ s__("mrWidget|Refreshing now") }}
+ </span>
+ </template>
+ <template v-else>
+ <status-icon
+ status="warning"
+ :show-disabled-button="true"
+ />
+ <div class="media-body space-children">
+ <span class="bold">
+ <span
+ class="has-error-message"
+ v-if="mr.mergeError"
+ >
+ {{ mr.mergeError }}.
+ </span>
+ <span v-else>
+ {{ s__("mrWidget|Merge failed.") }}
+ </span>
+ <span
+ :class="{ 'has-custom-error': mr.mergeError }"
+ >
+ {{ timerText }}
+ </span>
+ </span>
+ <button
+ @click="refresh"
+ class="btn btn-default btn-xs js-refresh-button"
+ type="button"
+ >
+ {{ s__("mrWidget|Refresh now") }}
+ </button>
+ </div>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
deleted file mode 100644
index bd349111bbd..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import Flash from '../../../flash';
-import statusIcon from '../mr_widget_status_icon';
-import MRWidgetAuthor from '../../components/mr_widget_author';
-import eventHub from '../../event_hub';
-
-export default {
- name: 'MRWidgetMergeWhenPipelineSucceeds',
- props: {
- mr: { type: Object, required: true },
- service: { type: Object, required: true },
- },
- components: {
- 'mr-widget-author': MRWidgetAuthor,
- statusIcon,
- },
- data() {
- return {
- isCancellingAutoMerge: false,
- isRemovingSourceBranch: false,
- };
- },
- computed: {
- canRemoveSourceBranch() {
- const { shouldRemoveSourceBranch, canRemoveSourceBranch,
- mergeUserId, currentUserId } = this.mr;
-
- return !shouldRemoveSourceBranch && canRemoveSourceBranch && mergeUserId === currentUserId;
- },
- },
- methods: {
- cancelAutomaticMerge() {
- this.isCancellingAutoMerge = true;
- this.service.cancelAutomaticMerge()
- .then(res => res.data)
- .then((data) => {
- eventHub.$emit('UpdateWidgetData', data);
- })
- .catch(() => {
- this.isCancellingAutoMerge = false;
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
- });
- },
- removeSourceBranch() {
- const options = {
- sha: this.mr.sha,
- merge_when_pipeline_succeeds: true,
- should_remove_source_branch: true,
- };
-
- this.isRemovingSourceBranch = true;
- this.service.mergeResource.save(options)
- .then(res => res.data)
- .then((data) => {
- if (data.status === 'merge_when_pipeline_succeeds') {
- eventHub.$emit('MRWidgetUpdateRequested');
- }
- })
- .catch(() => {
- this.isRemovingSourceBranch = false;
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
- });
- },
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="success" />
- <div class="media-body">
- <h4 class="flex-container-block">
- <span class="append-right-10">
- Set by
- <mr-widget-author :author="mr.setToMWPSBy" />
- to be merged automatically when the pipeline succeeds
- </span>
- <a
- v-if="mr.canCancelAutomaticMerge"
- @click.prevent="cancelAutomaticMerge"
- :disabled="isCancellingAutoMerge"
- role="button"
- href="#"
- class="btn btn-xs btn-default js-cancel-auto-merge">
- <i
- v-if="isCancellingAutoMerge"
- class="fa fa-spinner fa-spin"
- aria-hidden="true" />
- Cancel automatic merge
- </a>
- </h4>
- <section class="mr-info-list">
- <p>The changes will be merged into
- <a
- :href="mr.targetBranchPath"
- class="label-branch">
- {{mr.targetBranch}}
- </a>
- </p>
- <p v-if="mr.shouldRemoveSourceBranch">
- The source branch will be removed
- </p>
- <p
- v-else
- class="flex-container-block"
- >
- <span class="append-right-10">
- The source branch will not be removed
- </span>
- <a
- v-if="canRemoveSourceBranch"
- :disabled="isRemovingSourceBranch"
- @click.prevent="removeSourceBranch"
- role="button"
- class="btn btn-xs btn-default js-remove-source-branch"
- href="#">
- <i
- v-if="isRemovingSourceBranch"
- class="fa fa-spinner fa-spin"
- aria-hidden="true" />
- Remove source branch
- </a>
- </p>
- </section>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
new file mode 100644
index 00000000000..72887528bd8
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
@@ -0,0 +1,147 @@
+<script>
+ import Flash from '../../../flash';
+ import statusIcon from '../mr_widget_status_icon.vue';
+ import mrWidgetAuthor from '../../components/mr_widget_author';
+ import eventHub from '../../event_hub';
+
+ export default {
+ name: 'MRWidgetMergeWhenPipelineSucceeds',
+ components: {
+ mrWidgetAuthor,
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ service: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ data() {
+ return {
+ isCancellingAutoMerge: false,
+ isRemovingSourceBranch: false,
+ };
+ },
+ computed: {
+ canRemoveSourceBranch() {
+ const {
+ shouldRemoveSourceBranch,
+ canRemoveSourceBranch,
+ mergeUserId,
+ currentUserId,
+ } = this.mr;
+
+ return !shouldRemoveSourceBranch &&
+ canRemoveSourceBranch &&
+ mergeUserId === currentUserId;
+ },
+ },
+ methods: {
+ cancelAutomaticMerge() {
+ this.isCancellingAutoMerge = true;
+ this.service.cancelAutomaticMerge()
+ .then(res => res.data)
+ .then((data) => {
+ eventHub.$emit('UpdateWidgetData', data);
+ })
+ .catch(() => {
+ this.isCancellingAutoMerge = false;
+ Flash('Something went wrong. Please try again.');
+ });
+ },
+ removeSourceBranch() {
+ const options = {
+ sha: this.mr.sha,
+ merge_when_pipeline_succeeds: true,
+ should_remove_source_branch: true,
+ };
+
+ this.isRemovingSourceBranch = true;
+ this.service.mergeResource.save(options)
+ .then(res => res.data)
+ .then((data) => {
+ if (data.status === 'merge_when_pipeline_succeeds') {
+ eventHub.$emit('MRWidgetUpdateRequested');
+ }
+ })
+ .catch(() => {
+ this.isRemovingSourceBranch = false;
+ Flash('Something went wrong. Please try again.');
+ });
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon status="success" />
+ <div class="media-body">
+ <h4 class="flex-container-block">
+ <span class="append-right-10">
+ {{ s__("mrWidget|Set by") }}
+ <mr-widget-author :author="mr.setToMWPSBy" />
+ {{ s__("mrWidget|to be merged automatically when the pipeline succeeds") }}
+ </span>
+ <a
+ v-if="mr.canCancelAutomaticMerge"
+ @click.prevent="cancelAutomaticMerge"
+ :disabled="isCancellingAutoMerge"
+ role="button"
+ href="#"
+ class="btn btn-xs btn-default js-cancel-auto-merge">
+ <i
+ v-if="isCancellingAutoMerge"
+ class="fa fa-spinner fa-spin"
+ aria-hidden="true"
+ >
+ </i>
+ {{ s__("mrWidget|Cancel automatic merge") }}
+ </a>
+ </h4>
+ <section class="mr-info-list">
+ <p>
+ {{ s__("mrWidget|The changes will be merged into") }}
+ <a
+ :href="mr.targetBranchPath"
+ class="label-branch"
+ >
+ {{ mr.targetBranch }}
+ </a>
+ </p>
+ <p v-if="mr.shouldRemoveSourceBranch">
+ {{ s__("mrWidget|The source branch will be removed") }}
+ </p>
+ <p
+ v-else
+ class="flex-container-block"
+ >
+ <span class="append-right-10">
+ {{ s__("mrWidget|The source branch will not be removed") }}
+ </span>
+ <a
+ v-if="canRemoveSourceBranch"
+ :disabled="isRemovingSourceBranch"
+ @click.prevent="removeSourceBranch"
+ role="button"
+ class="btn btn-xs btn-default js-remove-source-branch"
+ href="#"
+ >
+ <i
+ v-if="isRemovingSourceBranch"
+ class="fa fa-spinner fa-spin"
+ aria-hidden="true"
+ >
+ </i>
+ {{ s__("mrWidget|Remove source branch") }}
+ </a>
+ </p>
+ </section>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
deleted file mode 100644
index ba9681680ef..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import Flash from '../../../flash';
-import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
-import tooltip from '../../../vue_shared/directives/tooltip';
-import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
-import statusIcon from '../mr_widget_status_icon';
-import eventHub from '../../event_hub';
-
-export default {
- name: 'MRWidgetMerged',
- props: {
- mr: { type: Object, required: true },
- service: { type: Object, required: true },
- },
- data() {
- return {
- isMakingRequest: false,
- };
- },
- directives: {
- tooltip,
- },
- components: {
- 'mr-widget-author-and-time': mrWidgetAuthorTime,
- loadingIcon,
- statusIcon,
- },
- computed: {
- shouldShowRemoveSourceBranch() {
- const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
-
- return !sourceBranchRemoved && canRemoveSourceBranch &&
- !this.isMakingRequest && !isRemovingSourceBranch;
- },
- shouldShowSourceBranchRemoving() {
- const { sourceBranchRemoved, isRemovingSourceBranch } = this.mr;
- return !sourceBranchRemoved && (isRemovingSourceBranch || this.isMakingRequest);
- },
- shouldShowMergedButtons() {
- const { canRevertInCurrentMR, canCherryPickInCurrentMR, revertInForkPath,
- cherryPickInForkPath } = this.mr;
-
- return canRevertInCurrentMR || canCherryPickInCurrentMR ||
- revertInForkPath || cherryPickInForkPath;
- },
- },
- methods: {
- removeSourceBranch() {
- this.isMakingRequest = true;
- this.service.removeSourceBranch()
- .then(res => res.data)
- .then((data) => {
- if (data.message === 'Branch was removed') {
- eventHub.$emit('MRWidgetUpdateRequested', () => {
- this.isMakingRequest = false;
- });
- }
- })
- .catch(() => {
- this.isMakingRequest = false;
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
- });
- },
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="success" />
- <div class="media-body">
- <div class="space-children">
- <mr-widget-author-and-time
- actionText="Merged by"
- :author="mr.metrics.mergedBy"
- :date-title="mr.metrics.mergedAt"
- :date-readable="mr.metrics.readableMergedAt" />
- <a
- v-if="mr.canRevertInCurrentMR"
- v-tooltip
- class="btn btn-close btn-xs"
- href="#modal-revert-commit"
- data-toggle="modal"
- data-container="body"
- title="Revert this merge request in a new merge request">
- Revert
- </a>
- <a
- v-else-if="mr.revertInForkPath"
- v-tooltip
- class="btn btn-close btn-xs"
- data-method="post"
- :href="mr.revertInForkPath"
- title="Revert this merge request in a new merge request">
- Revert
- </a>
- <a
- v-if="mr.canCherryPickInCurrentMR"
- v-tooltip
- class="btn btn-default btn-xs"
- href="#modal-cherry-pick-commit"
- data-toggle="modal"
- data-container="body"
- title="Cherry-pick this merge request in a new merge request">
- Cherry-pick
- </a>
- <a
- v-else-if="mr.cherryPickInForkPath"
- v-tooltip
- class="btn btn-default btn-xs"
- data-method="post"
- :href="mr.cherryPickInForkPath"
- title="Cherry-pick this merge request in a new merge request">
- Cherry-pick
- </a>
- </div>
- <section class="mr-info-list">
- <p>
- The changes were merged into
- <span class="label-branch">
- <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
- </span>
- </p>
- <p v-if="mr.sourceBranchRemoved">The source branch has been removed</p>
- <p v-if="shouldShowRemoveSourceBranch" class="space-children">
- <span>You can remove source branch now</span>
- <button
- @click="removeSourceBranch"
- :disabled="isMakingRequest"
- type="button"
- class="btn btn-xs btn-default js-remove-branch-button">
- Remove Source Branch
- </button>
- </p>
- <p v-if="shouldShowSourceBranchRemoving">
- <loading-icon inline />
- <span>The source branch is being removed</span>
- </p>
- </section>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
new file mode 100644
index 00000000000..a92e0b3c124
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -0,0 +1,192 @@
+<script>
+ import Flash from '~/flash';
+ import tooltip from '~/vue_shared/directives/tooltip';
+ import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+ import { s__, __ } from '~/locale';
+ import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
+ import statusIcon from '../mr_widget_status_icon.vue';
+ import eventHub from '../../event_hub';
+
+ export default {
+ name: 'MRWidgetMerged',
+ directives: {
+ tooltip,
+ },
+ components: {
+ mrWidgetAuthorTime,
+ loadingIcon,
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ service: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ data() {
+ return {
+ isMakingRequest: false,
+ };
+ },
+ computed: {
+ shouldShowRemoveSourceBranch() {
+ const {
+ sourceBranchRemoved,
+ isRemovingSourceBranch,
+ canRemoveSourceBranch,
+ } = this.mr;
+
+ return !sourceBranchRemoved &&
+ canRemoveSourceBranch &&
+ !this.isMakingRequest &&
+ !isRemovingSourceBranch;
+ },
+ shouldShowSourceBranchRemoving() {
+ const {
+ sourceBranchRemoved,
+ isRemovingSourceBranch,
+ } = this.mr;
+ return !sourceBranchRemoved &&
+ (isRemovingSourceBranch || this.isMakingRequest);
+ },
+ shouldShowMergedButtons() {
+ const {
+ canRevertInCurrentMR,
+ canCherryPickInCurrentMR,
+ revertInForkPath,
+ cherryPickInForkPath,
+ } = this.mr;
+
+ return canRevertInCurrentMR ||
+ canCherryPickInCurrentMR ||
+ revertInForkPath ||
+ cherryPickInForkPath;
+ },
+ revertTitle() {
+ return s__('mrWidget|Revert this merge request in a new merge request');
+ },
+ cherryPickTitle() {
+ return s__('mrWidget|Cherry-pick this merge request in a new merge request');
+ },
+ revertLabel() {
+ return s__('mrWidget|Revert');
+ },
+ cherryPickLabel() {
+ return s__('mrWidget|Cherry-pick');
+ },
+ },
+ methods: {
+ removeSourceBranch() {
+ this.isMakingRequest = true;
+
+ this.service.removeSourceBranch()
+ .then(res => res.data)
+ .then((data) => {
+ if (data.message === 'Branch was removed') {
+ eventHub.$emit('MRWidgetUpdateRequested', () => {
+ this.isMakingRequest = false;
+ });
+ }
+ })
+ .catch(() => {
+ this.isMakingRequest = false;
+ Flash(__('Something went wrong. Please try again.'));
+ });
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon status="success" />
+ <div class="media-body">
+ <div class="space-children">
+ <mr-widget-author-time
+ :action-text="s__('mrWidget|Merged by')"
+ :author="mr.metrics.mergedBy"
+ :date-title="mr.metrics.mergedAt"
+ :date-readable="mr.metrics.readableMergedAt"
+ />
+ <a
+ v-if="mr.canRevertInCurrentMR"
+ v-tooltip
+ class="btn btn-close btn-xs"
+ href="#modal-revert-commit"
+ data-toggle="modal"
+ data-container="body"
+ :title="revertTitle"
+ >
+ {{ revertLabel }}
+ </a>
+ <a
+ v-else-if="mr.revertInForkPath"
+ v-tooltip
+ class="btn btn-close btn-xs"
+ data-method="post"
+ :href="mr.revertInForkPath"
+ :title="revertTitle"
+ >
+ {{ revertLabel }}
+ </a>
+ <a
+ v-if="mr.canCherryPickInCurrentMR"
+ v-tooltip
+ class="btn btn-default btn-xs"
+ href="#modal-cherry-pick-commit"
+ data-toggle="modal"
+ data-container="body"
+ :title="cherryPickTitle"
+ >
+ {{ cherryPickLabel }}
+ </a>
+ <a
+ v-else-if="mr.cherryPickInForkPath"
+ v-tooltip
+ class="btn btn-default btn-xs"
+ data-method="post"
+ :href="mr.cherryPickInForkPath"
+ :title="cherryPickTitle"
+ >
+ {{ cherryPickLabel }}
+ </a>
+ </div>
+ <section class="mr-info-list">
+ <p>
+ {{ s__("mrWidget|The changes were merged into") }}
+ <span class="label-branch">
+ <a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
+ </span>
+ </p>
+ <p v-if="mr.sourceBranchRemoved">
+ {{ s__("mrWidget|The source branch has been removed") }}
+ </p>
+ <p
+ v-if="shouldShowRemoveSourceBranch"
+ class="space-children"
+ >
+ <span>{{ s__("mrWidget|You can remove source branch now") }}</span>
+ <button
+ @click="removeSourceBranch"
+ :disabled="isMakingRequest"
+ type="button"
+ class="btn btn-xs btn-default js-remove-branch-button"
+ >
+ {{ s__("mrWidget|Remove Source Branch") }}
+ </button>
+ </p>
+ <p v-if="shouldShowSourceBranchRemoving">
+ <loading-icon :inline="true" />
+ <span>
+ {{ s__("mrWidget|The source branch is being removed") }}
+ </span>
+ </p>
+ </section>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js
deleted file mode 100644
index f6d1a4feeb2..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetMerging',
- props: {
- mr: { type: Object, required: true },
- },
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body mr-state-locked media">
- <status-icon status="loading" />
- <div class="media-body">
- <h4>
- This merge request is in the process of being merged
- </h4>
- <section class="mr-info-list">
- <p>
- The changes will be merged into
- <span class="label-branch">
- <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
- </span>
- </p>
- </section>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
new file mode 100644
index 00000000000..953ddf40a51
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
@@ -0,0 +1,35 @@
+<script>
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetMerging',
+ components: {
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body mr-state-locked media">
+ <status-icon status="loading" />
+ <div class="media-body">
+ <h4>
+ {{ s__("mrWidget|This merge request is in the process of being merged") }}
+ </h4>
+ <section class="mr-info-list">
+ <p>
+ {{ s__("mrWidget|The changes will be merged into") }}
+ <span class="label-branch">
+ <a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
+ </span>
+ </p>
+ </section>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
index 1bc0b7e0819..303877d6fbf 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help';
@@ -24,7 +24,7 @@ export default {
},
template: `
<div class="mr-widget-body media">
- <status-icon status="failed" :show-disabled-button="true" />
+ <status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold js-branch-text">
<span class="capitalize">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
index 00047718201..cea3d97fa88 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetNotAllowed',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
index 1cedf86e811..e66ce071ab4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
@@ -7,7 +7,7 @@ export default {
},
template: `
<div class="mr-widget-body media">
- <status-icon status="failed" :show-disabled-button="true" />
+ <status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
Pipeline blocked. The pipeline for this merge request requires a manual action to proceed
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js
index 6853ba4b9f8..4d9a2ca530f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
@@ -7,7 +7,7 @@ export default {
},
template: `
<div class="mr-widget-body media">
- <status-icon status="failed" :show-disabled-button="true" />
+ <status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
index 60f42c46ffe..7ba6c29006a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
@@ -3,7 +3,7 @@ import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
export default {
@@ -69,7 +69,7 @@ export default {
},
iconClass() {
if (this.status === 'failed' || !this.commitMessage.length || !this.mr.isMergeAllowed || this.mr.preventMerge) {
- return 'failed';
+ return 'warning';
}
return 'success';
},
@@ -166,7 +166,7 @@ export default {
// If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested');
eventHub.$emit('FetchActionsContent');
- MergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged');
+ MergeRequest.setStatusBoxToMerged();
MergeRequest.hideCloseButton();
MergeRequest.decreaseCounter();
stopPolling();
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 52dd0245ff0..2968af0d5cb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -1,7 +1,7 @@
<script>
import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Flash from '../../../flash';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js
index af19cf6ab87..142ddf477f1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetSHAMismatch',
@@ -7,7 +7,7 @@ export default {
},
template: `
<div class="mr-widget-body media">
- <status-icon status="failed" :show-disabled-button="true" />
+ <status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
The source branch HEAD has recently changed. Please reload the page and review the changes before merging
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js
index a119ecbbdfe..67b271c69ca 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetUnresolvedDiscussions',
@@ -10,7 +10,7 @@ export default {
},
template: `
<div class="mr-widget-body media">
- <status-icon status="failed" :show-disabled-button="true" />
+ <status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
There are unresolved discussions. Please resolve these discussions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
index 13461440ef2..bbca641f65e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub';
@@ -37,7 +37,7 @@ export default {
},
template: `
<div class="mr-widget-body media">
- <status-icon status="failed" :show-disabled-button="Boolean(mr.removeWIPPath)" />
+ <status-icon status="warning" :show-disabled-button="Boolean(mr.removeWIPPath)" />
<div class="media-body space-children">
<span class="bold">
This is a Work in Progress
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index 940f3d9b2d0..2917090e073 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -16,13 +16,13 @@ export { default as WidgetMergeHelp } from './components/mr_widget_merge_help';
export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
export { default as WidgetDeployment } from './components/mr_widget_deployment';
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links';
-export { default as MergedState } from './components/states/mr_widget_merged';
-export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge';
-export { default as ClosedState } from './components/states/mr_widget_closed';
-export { default as MergingState } from './components/states/mr_widget_merging';
+export { default as MergedState } from './components/states/mr_widget_merged.vue';
+export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue';
+export { default as ClosedState } from './components/states/mr_widget_closed.vue';
+export { default as MergingState } from './components/states/mr_widget_merging.vue';
export { default as WipState } from './components/states/mr_widget_wip';
-export { default as ArchivedState } from './components/states/mr_widget_archived';
-export { default as ConflictsState } from './components/states/mr_widget_conflicts';
+export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
+export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed';
@@ -31,10 +31,10 @@ export { default as SHAMismatchState } from './components/states/mr_widget_sha_m
export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions';
export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked';
export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed';
-export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds';
+export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
-export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed';
-export { default as CheckingState } from './components/states/mr_widget_checking';
+export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue';
+export { default as CheckingState } from './components/states/mr_widget_checking.vue';
export { default as MRWidgetStore } from './stores/mr_widget_store';
export { default as MRWidgetService } from './services/mr_widget_service';
export { default as eventHub } from './event_hub';
diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js
index 43ef468c303..6b9918b65b0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/index.js
@@ -2,6 +2,9 @@ import {
Vue,
mrWidgetOptions,
} from './dependencies';
+import Translate from '../vue_shared/translate';
+
+Vue.use(Translate);
document.addEventListener('DOMContentLoaded', () => {
gl.mrWidgetData.gitlabLogo = gon.gitlab_logo;
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index 2075f8e4fec..98d33f9efaa 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -1,4 +1,4 @@
-import Project from '~/project';
+import Project from '~/pages/projects/project';
import SmartInterval from '~/smart_interval';
import Flash from '../flash';
import {
diff --git a/app/assets/javascripts/vue_shared/components/loading_icon.vue b/app/assets/javascripts/vue_shared/components/loading_icon.vue
index 1eba117b18f..12a75e016d7 100644
--- a/app/assets/javascripts/vue_shared/components/loading_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_icon.vue
@@ -33,7 +33,7 @@
<template>
<component
:is="rootElementType"
- class="text-center">
+ class="loading-container text-center">
<i
class="fa fa-spin fa-spinner"
:class="cssClass"
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index e0d2ed80de5..a538b5a2946 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -174,12 +174,13 @@
&.user-authored {
cursor: default;
- opacity: 0.65;
+ background-color: $gray-light;
+ border-color: $theme-gray-200;
+ color: $gl-text-color-disabled;
- &:hover,
- &:active {
- background-color: $white-light;
- border-color: $border-color;
+ gl-emoji {
+ opacity: 0.4;
+ filter: grayscale(100%);
}
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index fcc420923f9..d0b0c69b18f 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -220,14 +220,6 @@
@include btn-with-margin;
}
- &.disabled {
- pointer-events: auto !important;
- }
-
- &[disabled] {
- pointer-events: none !important;
- }
-
.fa-caret-down,
.fa-chevron-down {
margin-left: 5px;
@@ -450,3 +442,28 @@
.btn-svg svg {
@include btn-svg;
}
+
+// All disabled buttons, regardless of color, type, etc
+%disabled {
+ background-color: $gray-light !important;
+ border-color: $theme-gray-200 !important;
+ color: $gl-text-color-disabled !important;
+ opacity: 1 !important;
+ cursor: default !important;
+
+ i {
+ color: $gl-text-color-disabled !important;
+ }
+}
+
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn,
+.dropdown-toggle[disabled],
+[disabled].dropdown-menu-toggle {
+ @extend %disabled;
+
+ &:hover {
+ @extend %disabled;
+ }
+}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 1d2303a3a2b..691df098c70 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -30,7 +30,7 @@
@include set-visible;
min-height: $dropdown-min-height;
max-height: $dropdown-max-height;
- overflow: auto;
+ overflow-y: auto;
@media (max-width: $screen-xs-max) {
width: 100%;
@@ -63,11 +63,6 @@
border-radius: $border-radius-base;
white-space: nowrap;
- &[disabled] {
- opacity: .65;
- cursor: not-allowed;
- }
-
&.no-outline {
outline: 0;
}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 1e91db5af9b..d835d49d8b2 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -124,6 +124,10 @@
&.wiki {
padding: $gl-padding;
+
+ @media (min-width: $screen-md-min) {
+ padding: $gl-padding * 2;
+ }
}
&.blob-no-preview {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 3b7256f3000..634593aefd0 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -303,6 +303,8 @@
.projects-dropdown-menu {
padding: 0;
+ overflow-y: initial;
+ max-height: initial;
}
.dropdown-chevron {
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index e2084e8f85f..30314f3d6cb 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -26,15 +26,15 @@
}
.ci-status-icon-canceled,
-.ci-status-icon-disabled,
-.ci-status-icon-not-found {
+.ci-status-icon-disabled {
svg {
fill: $gl-text-color;
}
}
.ci-status-icon-created,
-.ci-status-icon-skipped {
+.ci-status-icon-skipped,
+.ci-status-icon-notfound {
svg {
fill: $gray-darkest;
}
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index fd5c3c81a53..2d015ef086b 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -20,7 +20,7 @@
width: 100%;
}
- $image-widths: 250 306 394;
+ $image-widths: 250 306 394 430;
@each $width in $image-widths {
&.svg-#{$width} {
img,
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 1d8bd26cf1a..d8c57a0e2d9 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -24,15 +24,13 @@
font-size: $gl-font-size;
line-height: 25px;
+ &.status-box-closed,
&.status-box-mr-closed {
background-color: $gl-danger;
}
- &.status-box-issue-closed {
- background-color: $gl-primary;
- }
-
- &.status-box-merged {
+ &.status-box-issue-closed,
+ &.status-box-mr-merged {
background-color: $gl-primary;
}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index fab3270b9f5..d107422e517 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -1,10 +1,16 @@
html {
overflow-y: scroll;
- &.touch .tooltip { display: none !important; }
+ &.touch .tooltip {
+ display: none !important;
+ }
}
body {
+ // Improves readability for dyslexic users; supported only in Chrome/Safari so far
+ // scss-lint:disable PropertySpelling
+ text-decoration-skip: ink;
+ // scss-lint:enable PropertySpelling
&.navless {
background-color: $white-light !important;
}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index e12b5aab381..ddd9dbb2be4 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -17,6 +17,8 @@
*/
@mixin markdown-table {
width: auto;
+ display: block;
+ overflow-x: auto;
}
/*
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 924472f2d7e..32b9894ae04 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -9,9 +9,11 @@
.modal-body {
background-color: $modal-body-bg;
+ line-height: $line-height-base;
min-height: $modal-body-height;
position: relative;
padding: #{3 * $grid-size} #{2 * $grid-size};
+ text-align: left;
.form-actions {
margin: #{2 * $grid-size} #{-2 * $grid-size} #{-2 * $grid-size};
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index da18ddf78d3..f76c6866463 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -164,6 +164,7 @@ $gl-text-color-tertiary: #949494;
$gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
+$gl-text-color-disabled: #919191;
$gl-text-green: $green-600;
$gl-text-green-hover: $green-700;
$gl-text-red: $red-500;
@@ -258,6 +259,8 @@ $general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px;
+$flash-height: 52px;
+$context-header-height: 60px;
/*
* Common component specific colors
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 0cba223c6a6..aeaa33bd3bd 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -310,8 +310,8 @@
}
&.invalid {
- @include status-color($gray-dark, $gray, $common-gray-dark);
- border-color: $common-gray-light;
+ @include status-color($gray-dark, $gray, $gray-darkest);
+ border-color: $gray-darkest;
}
}
@@ -335,8 +335,8 @@
&.invalid {
svg {
- border: 1px solid $common-gray-light;
- fill: $common-gray-light;
+ border: 1px solid $gray-darkest;
+ fill: $gray-darkest;
}
}
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 3b35beb7695..cfef6476d4d 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -117,47 +117,6 @@
top: $gl-padding-top;
}
- .content-list {
- li {
- padding: 18px $gl-padding $gl-padding;
-
- .container-fluid {
- padding: 0;
- }
- }
-
- .title-col {
- p {
- margin: 0;
-
- &.title {
- line-height: 19px;
- font-size: 14px;
- font-weight: $gl-font-weight-bold;
- color: $gl-text-color;
- }
-
- &.text {
- color: $layout-link-gray;
-
- &.value-col {
- color: $gl-text-color;
- }
- }
- }
- }
-
- .value-col {
- text-align: right;
-
- span {
- position: relative;
- vertical-align: middle;
- top: 3px;
- }
- }
- }
-
.fa-spinner {
font-size: 28px;
position: relative;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 6d4ccd53e12..bf8eb4c1f06 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -391,11 +391,17 @@
.dropdown-toggle {
float: right;
- .toggle-icon {
+ i {
color: $white-light;
padding-right: 2px;
margin-top: 2px;
}
+
+ &[disabled] {
+ i {
+ color: $gl-text-color-disabled;
+ }
+ }
}
.dropdown-menu {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 766e02b12ea..db88d4a16b7 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -228,7 +228,7 @@
.stage-cell {
&.table-section {
@media (min-width: $screen-md-min) {
- min-width: 148px;
+ min-width: 160px; /* Hack alert: Without this the mini graph pipeline won't work properly*/
margin-right: -4px;
}
}
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index acbd9936706..8265b8370f7 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -107,6 +107,11 @@ table.table tr td.multi-file-table-name {
vertical-align: middle;
margin-right: 2px;
}
+
+ .loading-container {
+ margin-right: 4px;
+ display: inline-block;
+ }
}
.multi-file-table-col-commit-message {
@@ -247,7 +252,6 @@ table.table tr td.multi-file-table-name {
display: flex;
position: relative;
flex-direction: column;
- height: 100%;
width: 290px;
padding: 0;
background-color: $gray-light;
@@ -256,6 +260,11 @@ table.table tr td.multi-file-table-name {
.projects-sidebar {
display: flex;
flex-direction: column;
+
+ .context-header {
+ width: auto;
+ margin-right: 0;
+ }
}
.multi-file-commit-panel-inner {
@@ -496,19 +505,70 @@ table.table tr td.multi-file-table-name {
}
}
-.ide-flash-container.flash-container {
- margin-top: $header-height;
- margin-bottom: 0;
+.ide.nav-only {
+ .flash-container {
+ margin-top: $header-height;
+ margin-bottom: 0;
+ }
+
+ .alert-wrapper .flash-container .flash-alert:last-child,
+ .alert-wrapper .flash-container .flash-notice:last-child {
+ margin-bottom: 0;
+ }
+
+ .content {
+ margin-top: $header-height;
+ }
+
+ .multi-file-commit-panel .multi-file-commit-panel-inner-scroll {
+ max-height: calc(100vh - #{$header-height + $context-header-height});
+ }
+
+ &.flash-shown {
+ .content {
+ margin-top: 0;
+ }
+
+ .ide-view {
+ height: calc(100vh - #{$header-height + $flash-height});
+ }
+
+ .multi-file-commit-panel .multi-file-commit-panel-inner-scroll {
+ max-height: calc(100vh - #{$header-height + $flash-height + $context-header-height});
+ }
+ }
}
-.with-performance-bar {
- .ide-flash-container.flash-container {
- margin-top: $header-height + $performance-bar-height;
+.with-performance-bar .ide.nav-only {
+ .flash-container {
+ margin-top: #{$header-height + $performance-bar-height};
+ }
+
+ .content {
+ margin-top: #{$header-height + $performance-bar-height};
}
.ide-view {
height: calc(100vh - #{$header-height + $performance-bar-height});
}
+
+ .multi-file-commit-panel .multi-file-commit-panel-inner-scroll {
+ max-height: calc(100vh - #{$header-height + $performance-bar-height + 60});
+ }
+
+ &.flash-shown {
+ .content {
+ margin-top: 0;
+ }
+
+ .ide-view {
+ height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height});
+ }
+
+ .multi-file-commit-panel .multi-file-commit-panel-inner-scroll {
+ max-height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height + $context-header-height});
+ }
+ }
}
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index a7ab481519d..b0c4c31cffc 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -50,10 +50,10 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def create_params
- params.require(:deploy_key).permit(:key, :title, :can_push)
+ params.require(:deploy_key).permit(:key, :title)
end
def update_params
- params.require(:deploy_key).permit(:title, :can_push)
+ params.require(:deploy_key).permit(:title)
end
end
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index 77e3c95d197..2b47819303e 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -59,11 +59,9 @@ class Admin::HooksController < Admin::ApplicationController
def hook_params
params.require(:hook).permit(
:enable_ssl_verification,
- :push_events,
- :tag_push_events,
- :repository_update_events,
:token,
- :url
+ :url,
+ *SystemHook.triggers.values
)
end
end
diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
index 5162273ef8a..ae7a7f6279c 100644
--- a/app/controllers/admin/jobs_controller.rb
+++ b/app/controllers/admin/jobs_controller.rb
@@ -20,6 +20,6 @@ class Admin::JobsController < Admin::ApplicationController
def cancel_all
Ci::Build.running_or_pending.each(&:cancel)
- redirect_to admin_jobs_path
+ redirect_to admin_jobs_path, status: 303
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ee21d81f23e..95ad38d9230 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -147,6 +147,8 @@ class ApplicationController < ActionController::Base
format.html do
render file: Rails.root.join("public", "404"), layout: false, status: "404"
end
+ # Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file
+ format.js { render json: '', status: :not_found, content_type: 'application/json' }
format.any { head :not_found }
end
end
diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb
index b569029283f..fafb10090ca 100644
--- a/app/controllers/concerns/group_tree.rb
+++ b/app/controllers/concerns/group_tree.rb
@@ -2,7 +2,11 @@ module GroupTree
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_group_tree(groups)
@groups = if params[:filter].present?
- Gitlab::GroupHierarchy.new(groups.search(params[:filter]))
+ # We find the ancestors by ID of the search results here.
+ # Otherwise the ancestors would also have filters applied,
+ # which would cause them not to be preloaded.
+ group_ids = groups.search(params[:filter]).select(:id)
+ Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
.base_and_ancestors
else
# Only show root groups if no parent-id is given
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 74a4f437dc8..337957c366d 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -45,7 +45,7 @@ module IssuableActions
}
if issuable.edited?
- response[:updated_at] = issuable.updated_at
+ response[:updated_at] = issuable.last_edited_at.to_time.iso8601
response[:updated_by_name] = issuable.last_edited_by.name
response[:updated_by_path] = user_path(issuable.last_edited_by)
end
diff --git a/app/controllers/concerns/with_performance_bar.rb b/app/controllers/concerns/with_performance_bar.rb
index 230bbe4b1aa..6a8b1a4de7b 100644
--- a/app/controllers/concerns/with_performance_bar.rb
+++ b/app/controllers/concerns/with_performance_bar.rb
@@ -6,13 +6,22 @@ module WithPerformanceBar
end
def peek_enabled?
- return true if Rails.env.development?
return false unless Gitlab::PerformanceBar.enabled?(current_user)
if RequestStore.active?
- RequestStore.fetch(:peek_enabled) { cookies[:perf_bar_enabled].present? }
+ RequestStore.fetch(:peek_enabled) { cookie_or_default_value }
else
- cookies[:perf_bar_enabled].present?
+ cookie_or_default_value
+ end
+ end
+
+ private
+
+ def cookie_or_default_value
+ if cookies[:perf_bar_enabled].present?
+ cookies[:perf_bar_enabled] == 'true'
+ else
+ cookies[:perf_bar_enabled] = 'true' if Rails.env.development?
end
end
end
diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb
index bc0948cd3fb..6d9c38d9581 100644
--- a/app/controllers/confirmations_controller.rb
+++ b/app/controllers/confirmations_controller.rb
@@ -17,7 +17,7 @@ class ConfirmationsController < Devise::ConfirmationsController
else
Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}")
flash[:notice] += " Please sign in."
- new_session_path(:user)
+ new_session_path(:user, anchor: 'login-pane')
end
end
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index f013d21275e..acf6aaf57f4 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -75,8 +75,6 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def milestones
- search_params = params.merge(group_ids: group.id)
-
milestones = MilestonesFinder.new(search_params).execute
legacy_milestones = GroupMilestone.build_collection(group, group_projects, params)
@@ -94,4 +92,8 @@ class Groups::MilestonesController < Groups::ApplicationController
render_404 unless @milestone
end
+
+ def search_params
+ params.permit(:state).merge(group_ids: group.id)
+ end
end
diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb
index a931b456a93..16abf7bab7e 100644
--- a/app/controllers/health_controller.rb
+++ b/app/controllers/health_controller.rb
@@ -8,7 +8,8 @@ class HealthController < ActionController::Base
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::FsShardsCheck
+ Gitlab::HealthChecks::FsShardsCheck,
+ Gitlab::HealthChecks::GitalyCheck
].freeze
def readiness
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 689d2e3db22..d631d09f1b8 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -112,6 +112,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
continue_login_process
end
+ rescue Gitlab::OAuth::SigninDisabledForProviderError
+ handle_disabled_provider
rescue Gitlab::OAuth::SignupDisabledError
handle_signup_error
end
@@ -168,6 +170,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_user_session_path
end
+ def handle_disabled_provider
+ label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
+ flash[:alert] = "Signing in using #{label} has been disabled"
+
+ redirect_to new_user_session_path
+ end
+
def log_audit_event(user, options = {})
AuditEventService.new(user, user, options)
.for_authentication.security_event
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 026708169f4..0a40c67368f 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -13,31 +13,37 @@ class Projects::CommitsController < Projects::ApplicationController
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
- respond_to do |format|
- format.html
- format.atom { render layout: 'xml.atom' }
-
- format.json do
- pager_json(
- 'projects/commits/_commits',
- @commits.size,
- project: @project,
- ref: @ref)
+ # https://gitlab.com/gitlab-org/gitaly/issues/931
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ respond_to do |format|
+ format.html
+ format.atom { render layout: 'xml.atom' }
+
+ format.json do
+ pager_json(
+ 'projects/commits/_commits',
+ @commits.size,
+ project: @project,
+ ref: @ref)
+ end
end
end
end
def signatures
- respond_to do |format|
- format.json do
- render json: {
- signatures: @commits.select(&:has_signature?).map do |commit|
- {
- commit_sha: commit.sha,
- html: view_to_html_string('projects/commit/_signature', signature: commit.signature)
- }
- end
- }
+ # https://gitlab.com/gitlab-org/gitaly/issues/931
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ respond_to do |format|
+ format.json do
+ render json: {
+ signatures: @commits.select(&:has_signature?).map do |commit|
+ {
+ commit_sha: commit.sha,
+ html: view_to_html_string('projects/commit/_signature', signature: commit.signature)
+ }
+ end
+ }
+ end
end
end
end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index e06dda1baa4..f43ef2e5f2f 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -24,7 +24,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
def create
@key = DeployKeys::CreateService.new(current_user, create_params).execute
- unless @key.valid? && @project.deploy_keys << @key
+ unless @key.valid?
flash[:alert] = @key.errors.full_messages.join(', ').html_safe
end
@@ -71,11 +71,14 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def create_params
- params.require(:deploy_key).permit(:key, :title, :can_push)
+ create_params = params.require(:deploy_key)
+ .permit(:key, :title, deploy_keys_projects_attributes: [:can_push])
+ create_params.dig(:deploy_keys_projects_attributes, '0')&.merge!(project_id: @project.id)
+ create_params
end
def update_params
- params.require(:deploy_key).permit(:title, :can_push)
+ params.require(:deploy_key).permit(:title, deploy_keys_projects_attributes: [:id, :can_push])
end
def authorize_update_deploy_key!
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 6f51e7b9b40..dd7aa1a67b9 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -64,18 +64,10 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params
params.require(:hook).permit(
- :job_events,
- :pipeline_events,
:enable_ssl_verification,
- :issues_events,
- :confidential_issues_events,
- :merge_requests_events,
- :note_events,
- :push_events,
- :tag_push_events,
:token,
:url,
- :wiki_page_events
+ *ProjectHook.triggers.values
)
end
end
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 980bbf699b6..75b17d05e22 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -83,7 +83,7 @@ class Projects::MilestonesController < Projects::ApplicationController
Milestones::DestroyService.new(project, current_user).execute(milestone)
respond_to do |format|
- format.html { redirect_to namespace_project_milestones_path, status: 302 }
+ format.html { redirect_to namespace_project_milestones_path, status: 303 }
format.js { head :ok }
end
end
@@ -92,12 +92,6 @@ class Projects::MilestonesController < Projects::ApplicationController
def milestones
@milestones ||= begin
- if @project.group && can?(current_user, :read_group, @project.group)
- group = @project.group
- end
-
- search_params = params.merge(project_ids: @project.id, group_ids: group&.id)
-
MilestonesFinder.new(search_params).execute
end
end
@@ -113,4 +107,12 @@ class Projects::MilestonesController < Projects::ApplicationController
def milestone_params
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
+
+ def search_params
+ if @project.group && can?(current_user, :read_group, @project.group)
+ group = @project.group
+ end
+
+ params.permit(:state).merge(project_ids: @project.id, group_ids: group&.id)
+ end
end
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 58570a580f1..e72fd8eb3a5 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -27,12 +27,16 @@ class GroupDescendantsFinder
end
def execute
- # The children array might be extended with the ancestors of projects when
- # filtering. In that case, take the maximum so the array does not get limited
- # Otherwise, allow paginating through all results
+ # The children array might be extended with the ancestors of projects and
+ # subgroups when filtering. In that case, take the maximum so the array does
+ # not get limited otherwise, allow paginating through all results.
#
all_required_elements = children
- all_required_elements |= ancestors_for_projects if params[:filter]
+ if params[:filter]
+ all_required_elements |= ancestors_of_filtered_subgroups
+ all_required_elements |= ancestors_of_filtered_projects
+ end
+
total_count = [all_required_elements.size, paginator.total_count].max
Kaminari.paginate_array(all_required_elements, total_count: total_count)
@@ -49,8 +53,11 @@ class GroupDescendantsFinder
end
def paginator
- @paginator ||= Gitlab::MultiCollectionPaginator.new(subgroups, projects,
- per_page: params[:per_page])
+ @paginator ||= Gitlab::MultiCollectionPaginator.new(
+ subgroups,
+ projects.with_route,
+ per_page: params[:per_page]
+ )
end
def direct_child_groups
@@ -94,15 +101,21 @@ class GroupDescendantsFinder
#
# So when searching 'project', on the 'subgroup' page we want to preload
# 'nested-group' but not 'subgroup' or 'root'
- def ancestors_for_groups(base_for_ancestors)
- Gitlab::GroupHierarchy.new(base_for_ancestors)
+ def ancestors_of_groups(base_for_ancestors)
+ group_ids = base_for_ancestors.except(:select, :sort).select(:id)
+ Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
.base_and_ancestors(upto: parent_group.id)
end
- def ancestors_for_projects
+ def ancestors_of_filtered_projects
projects_to_load_ancestors_of = projects.where.not(namespace: parent_group)
groups_to_load_ancestors_of = Group.where(id: projects_to_load_ancestors_of.select(:namespace_id))
- ancestors_for_groups(groups_to_load_ancestors_of)
+ ancestors_of_groups(groups_to_load_ancestors_of)
+ .with_selects_for_list(archived: params[:archived])
+ end
+
+ def ancestors_of_filtered_subgroups
+ ancestors_of_groups(subgroups)
.with_selects_for_list(archived: params[:archived])
end
@@ -112,7 +125,7 @@ class GroupDescendantsFinder
# When filtering subgroups, we want to find all matches withing the tree of
# descendants to show to the user
groups = if params[:filter]
- ancestors_for_groups(subgroups_matching_filter)
+ subgroups_matching_filter
else
direct_child_groups
end
@@ -121,8 +134,10 @@ class GroupDescendantsFinder
end
def direct_child_projects
- GroupProjectsFinder.new(group: parent_group, current_user: current_user, params: params)
- .execute
+ GroupProjectsFinder.new(group: parent_group,
+ current_user: current_user,
+ options: { only_owned: true },
+ params: params).execute
end
# Finds all projects nested under `parent_group` or any of its descendant
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index d2275139c42..98831f5be4a 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -15,6 +15,7 @@
# label_name: string
# sort: string
# my_reaction_emoji: string
+# public_only: boolean
#
class IssuesFinder < IssuableFinder
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER
@@ -40,7 +41,15 @@ class IssuesFinder < IssuableFinder
private
def init_collection
- with_confidentiality_access_check
+ if public_only?
+ Issue.public_only
+ else
+ with_confidentiality_access_check
+ end
+ end
+
+ def public_only?
+ params.fetch(:public_only, false)
end
def user_can_see_all_confidential_issues?
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 6de9eb89468..1427cdaa382 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -71,7 +71,7 @@ class LabelsFinder < UnionFinder
end
def projects?
- params[:project_ids].present?
+ params[:project_ids]
end
def only_group_labels?
diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
index 0a5a0ea2f35..b4605fca193 100644
--- a/app/finders/milestones_finder.rb
+++ b/app/finders/milestones_finder.rb
@@ -46,11 +46,7 @@ class MilestonesFinder
end
def order(items)
- if params.has_key?(:order)
- items.reorder(params[:order])
- else
- order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
- items.reorder(order_statement)
- end
+ order_statement = Gitlab::Database.nulls_last_order('due_date', 'ASC')
+ items.reorder(order_statement).order('title ASC')
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d13407a06c8..6530327698b 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -89,7 +89,7 @@ module ApplicationHelper
end
def default_avatar
- 'no_avatar.png'
+ asset_path('no_avatar.png')
end
def last_commit(project)
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 45f7d29eb05..8ef561d90e6 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -201,6 +201,7 @@ module ApplicationSettingsHelper
:metrics_sample_interval,
:metrics_timeout,
:password_authentication_enabled_for_web,
+ :password_authentication_enabled_for_git,
:performance_bar_allowed_group_id,
:performance_bar_enabled,
:plantuml_enabled,
diff --git a/app/helpers/auto_devops_helper.rb b/app/helpers/auto_devops_helper.rb
index f4310ca2f06..d72457efec0 100644
--- a/app/helpers/auto_devops_helper.rb
+++ b/app/helpers/auto_devops_helper.rb
@@ -14,13 +14,13 @@ module AutoDevopsHelper
if missing_service
params = {
- kubernetes: link_to('Kubernetes service', edit_project_service_path(project, 'kubernetes'))
+ kubernetes: link_to('Kubernetes cluster', project_clusters_path(project))
}
if missing_domain
- _('Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly.') % params
+ _('Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly.') % params
else
- _('Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly.') % params
+ _('Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly.') % params
end
elsif missing_domain
_('Auto Review Apps and Auto Deploy need a domain name to work correctly.')
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 878bc9b5c9c..4ddc1dbed49 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -80,4 +80,20 @@ module EmailsHelper
'text-align:center'
].join(';')
end
+
+ # "You are receiving this email because #{reason}"
+ def notification_reason_text(reason)
+ string = case reason
+ when NotificationReason::OWN_ACTIVITY
+ 'of your activity'
+ when NotificationReason::ASSIGNED
+ 'you have been assigned an item'
+ when NotificationReason::MENTIONED
+ 'you have been mentioned'
+ else
+ 'of your account'
+ end
+
+ "#{string} on #{Gitlab.config.gitlab.host}"
+ end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 2668cf78afe..7cd84fe69c9 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -241,7 +241,7 @@ module IssuablesHelper
return {} unless issuable.edited?
{
- updatedAt: issuable.updated_at.to_time.iso8601,
+ updatedAt: issuable.last_edited_at.to_time.iso8601,
updatedBy: {
name: issuable.last_edited_by.name,
path: user_path(issuable.last_edited_by)
@@ -304,6 +304,12 @@ module IssuablesHelper
issuable.model_name.human.downcase
end
+ def selected_labels
+ Array(params[:label_name]).map do |label_name|
+ Label.new(title: label_name)
+ end
+ end
+
private
def sidebar_gutter_collapsed?
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 0f110bd25c5..64cd3032780 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -72,7 +72,7 @@ module IssuesHelper
if item.try(:expired?)
'status-box-expired'
elsif item.try(:merged?)
- 'status-box-merged'
+ 'status-box-mr-merged'
elsif item.closed?
'status-box-mr-closed'
elsif item.try(:upcoming?)
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 0f9ac958f95..e6a6496871a 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -170,4 +170,8 @@ module SearchHelper
# Truncato's filtered_tags and filtered_attributes are not quite the same
sanitize(html, tags: %w(a p ol ul li pre code))
end
+
+ def limited_count(count, limit = 1000)
+ count > limit ? "#{limit}+" : count
+ end
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index e7c953e749e..ddb48371c79 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -54,8 +54,16 @@ module TodosHelper
def todo_target_state_pill(todo)
return unless show_todo_state?(todo)
+ type =
+ case todo.target
+ when MergeRequest
+ 'mr'
+ when Issue
+ 'issue'
+ end
+
content_tag(:span, nil, class: 'target-status') do
- content_tag(:span, nil, class: "status-box status-box-#{todo.target.state.dasherize}") do
+ content_tag(:span, nil, class: "status-box status-box-#{type}-#{todo.target.state.dasherize}") do
todo.target.state.capitalize
end
end
diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb
index 33453dd178f..77433acb92a 100644
--- a/app/helpers/webpack_helper.rb
+++ b/app/helpers/webpack_helper.rb
@@ -1,12 +1,12 @@
require 'webpack/rails/manifest'
module WebpackHelper
- def webpack_bundle_tag(bundle)
- javascript_include_tag(*gitlab_webpack_asset_paths(bundle))
+ def webpack_bundle_tag(bundle, force_same_domain: false)
+ javascript_include_tag(*gitlab_webpack_asset_paths(bundle, force_same_domain: force_same_domain))
end
# override webpack-rails gem helper until changes can make it upstream
- def gitlab_webpack_asset_paths(source, extension: nil)
+ def gitlab_webpack_asset_paths(source, extension: nil, force_same_domain: false)
return "" unless source.present?
paths = Webpack::Rails::Manifest.asset_paths(source)
@@ -14,9 +14,11 @@ module WebpackHelper
paths.select! { |p| p.ends_with? ".#{extension}" }
end
- force_host = webpack_public_host
- if force_host
- paths.map! { |p| "#{force_host}#{p}" }
+ unless force_same_domain
+ force_host = webpack_public_host
+ if force_host
+ paths.map! { |p| "#{force_host}#{p}" }
+ end
end
paths
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 64ca2d2eacf..b33131becd3 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -1,54 +1,54 @@
module Emails
module Issues
- def new_issue_email(recipient_id, issue_id)
+ def new_issue_email(recipient_id, issue_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
- mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
+ mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id, reason))
end
- def new_mention_in_issue_email(recipient_id, issue_id, updated_by_user_id)
+ def new_mention_in_issue_email(recipient_id, issue_id, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
- mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
- def reassigned_issue_email(recipient_id, issue_id, previous_assignee_ids, updated_by_user_id)
+ def reassigned_issue_email(recipient_id, issue_id, previous_assignee_ids, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
@previous_assignees = []
@previous_assignees = User.where(id: previous_assignee_ids) if previous_assignee_ids.any?
- mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
- def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
+ def closed_issue_email(recipient_id, issue_id, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
@updated_by = User.find(updated_by_user_id)
- mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
- def relabeled_issue_email(recipient_id, issue_id, label_names, updated_by_user_id)
+ def relabeled_issue_email(recipient_id, issue_id, label_names, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
@label_names = label_names
@labels_url = project_labels_url(@project)
- mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
- def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
+ def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
@issue_status = status
@updated_by = User.find(updated_by_user_id)
- mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
end
- def issue_moved_email(recipient, issue, new_issue, updated_by_user)
+ def issue_moved_email(recipient, issue, new_issue, updated_by_user, reason = nil)
setup_issue_mail(issue.id, recipient.id)
@new_issue = new_issue
@new_project = new_issue.project
- mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id))
+ mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id, reason))
end
private
@@ -61,11 +61,12 @@ module Emails
@sent_notification = SentNotification.record(@issue, recipient_id, reply_key)
end
- def issue_thread_options(sender_id, recipient_id)
+ def issue_thread_options(sender_id, recipient_id, reason)
{
from: sender(sender_id),
to: recipient(recipient_id),
- subject: subject("#{@issue.title} (##{@issue.iid})")
+ subject: subject("#{@issue.title} (##{@issue.iid})"),
+ 'X-GitLab-NotificationReason' => reason
}
end
end
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 3626f8ce416..5fe09cea83f 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -1,57 +1,57 @@
module Emails
module MergeRequests
- def new_merge_request_email(recipient_id, merge_request_id)
+ def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
- mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id))
+ mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
end
- def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
+ def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
- mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
- def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
+ def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
- mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
- def relabeled_merge_request_email(recipient_id, merge_request_id, label_names, updated_by_user_id)
+ def relabeled_merge_request_email(recipient_id, merge_request_id, label_names, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@label_names = label_names
@labels_url = project_labels_url(@project)
- mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
- def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
+ def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@updated_by = User.find(updated_by_user_id)
- mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
- def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
+ def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
- mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
- def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id)
+ def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@mr_status = status
@updated_by = User.find(updated_by_user_id)
- mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
- def resolved_all_discussions_email(recipient_id, merge_request_id, resolved_by_user_id)
+ def resolved_all_discussions_email(recipient_id, merge_request_id, resolved_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@resolved_by = User.find(resolved_by_user_id)
- mail_answer_thread(@merge_request, merge_request_thread_options(resolved_by_user_id, recipient_id))
+ mail_answer_thread(@merge_request, merge_request_thread_options(resolved_by_user_id, recipient_id, reason))
end
private
@@ -64,11 +64,12 @@ module Emails
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
end
- def merge_request_thread_options(sender_id, recipient_id)
+ def merge_request_thread_options(sender_id, recipient_id, reason = nil)
{
from: sender(sender_id),
to: recipient(recipient_id),
- subject: subject("#{@merge_request.title} (#{@merge_request.to_reference})")
+ subject: subject("#{@merge_request.title} (#{@merge_request.to_reference})"),
+ 'X-GitLab-NotificationReason' => reason
}
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index ec886e993c3..eade0fe278f 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -112,6 +112,8 @@ class Notify < BaseMailer
headers["X-GitLab-#{model.class.name}-ID"] = model.id
headers['X-GitLab-Reply-Key'] = reply_key
+ @reason = headers['X-GitLab-NotificationReason']
+
if Gitlab::IncomingEmail.enabled? && @sent_notification
address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index df67fb243ad..6ced5fb0e24 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -466,7 +466,7 @@ module Ci
if cache && project.jobs_cache_index
cache = cache.merge(
- key: "#{cache[:key]}:#{project.jobs_cache_index}")
+ key: "#{cache[:key]}_#{project.jobs_cache_index}")
end
[cache]
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index d7153d7b816..f84bf132854 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -524,7 +524,7 @@ module Ci
return unless sha
project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path)
- rescue GRPC::NotFound, Rugged::ReferenceError, GRPC::Internal
+ rescue GRPC::NotFound, GRPC::Internal
nil
end
diff --git a/app/models/concerns/triggerable_hooks.rb b/app/models/concerns/triggerable_hooks.rb
new file mode 100644
index 00000000000..ec0ed3b795a
--- /dev/null
+++ b/app/models/concerns/triggerable_hooks.rb
@@ -0,0 +1,40 @@
+module TriggerableHooks
+ AVAILABLE_TRIGGERS = {
+ repository_update_hooks: :repository_update_events,
+ push_hooks: :push_events,
+ tag_push_hooks: :tag_push_events,
+ issue_hooks: :issues_events,
+ confidential_issue_hooks: :confidential_issues_events,
+ note_hooks: :note_events,
+ merge_request_hooks: :merge_requests_events,
+ job_hooks: :job_events,
+ pipeline_hooks: :pipeline_events,
+ wiki_page_hooks: :wiki_page_events
+ }.freeze
+
+ extend ActiveSupport::Concern
+
+ class_methods do
+ attr_reader :triggerable_hooks
+
+ attr_reader :triggers
+
+ def hooks_for(trigger)
+ callable_scopes = triggers.keys + [:all]
+ return none unless callable_scopes.include?(trigger)
+
+ public_send(trigger) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ private
+
+ def triggerable_hooks(hooks)
+ triggers = AVAILABLE_TRIGGERS.slice(*hooks)
+ @triggers = triggers
+
+ triggers.each do |trigger, event|
+ scope trigger, -> { where(event => true) }
+ end
+ end
+ end
+end
diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb
index eae5eee4fee..c2e0a5fa126 100644
--- a/app/models/deploy_key.rb
+++ b/app/models/deploy_key.rb
@@ -1,10 +1,16 @@
class DeployKey < Key
- has_many :deploy_keys_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ include IgnorableColumn
+
+ has_many :deploy_keys_projects, inverse_of: :deploy_key, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :deploy_keys_projects
scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) }
scope :are_public, -> { where(public: true) }
+ ignore_column :can_push
+
+ accepts_nested_attributes_for :deploy_keys_projects
+
def private?
!public?
end
@@ -22,10 +28,18 @@ class DeployKey < Key
end
def has_access_to?(project)
- projects.include?(project)
+ deploy_keys_project_for(project).present?
end
def can_push_to?(project)
- can_push? && has_access_to?(project)
+ !!deploy_keys_project_for(project)&.can_push?
+ end
+
+ def deploy_keys_project_for(project)
+ deploy_keys_projects.find_by(project: project)
+ end
+
+ def projects_with_write_access
+ Project.preload(:route).where(id: deploy_keys_projects.with_write_access.select(:project_id))
end
end
diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb
index b37b9bfbdac..6eef12c4373 100644
--- a/app/models/deploy_keys_project.rb
+++ b/app/models/deploy_keys_project.rb
@@ -1,8 +1,14 @@
class DeployKeysProject < ActiveRecord::Base
belongs_to :project
- belongs_to :deploy_key
+ belongs_to :deploy_key, inverse_of: :deploy_keys_projects
- validates :deploy_key_id, presence: true
+ scope :without_project_deleted, -> { joins(:project).where(projects: { pending_delete: false }) }
+ scope :in_project, ->(project) { where(project: project) }
+ scope :with_write_access, -> { where(can_push: true) }
+
+ accepts_nested_attributes_for :deploy_key
+
+ validates :deploy_key, presence: true
validates :deploy_key_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
validates :project_id, presence: true
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 7bcded5b5e1..3aed071dd49 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -45,14 +45,7 @@ class Deployment < ActiveRecord::Base
def includes_commit?(commit)
return false unless commit
- # Before 8.10, deployments didn't have keep-around refs. Any deployment
- # created before then could have a `sha` referring to a commit that no
- # longer exists in the repository, so just ignore those.
- begin
- project.repository.ancestor?(commit.id, sha)
- rescue Rugged::OdbError
- false
- end
+ project.repository.ancestor?(commit.id, sha)
end
def update_merge_request_metrics!
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index c0864769314..dc2f6817190 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -44,10 +44,10 @@ class GlobalMilestone
def self.group_milestones_states_count(group)
return STATE_COUNT_HASH unless group
- params = { group_ids: [group.id], state: 'all', order: nil }
+ params = { group_ids: [group.id], state: 'all' }
relation = MilestonesFinder.new(params).execute
- grouped_by_state = relation.group(:state).count
+ grouped_by_state = relation.reorder(nil).group(:state).count
{
opened: grouped_by_state['active'] || 0,
@@ -60,10 +60,10 @@ class GlobalMilestone
def self.legacy_group_milestone_states_count(projects)
return STATE_COUNT_HASH unless projects
- params = { project_ids: projects.map(&:id), state: 'all', order: nil }
+ params = { project_ids: projects.map(&:id), state: 'all' }
relation = MilestonesFinder.new(params).execute
- project_milestones_by_state_and_title = relation.group(:state, :title).count
+ project_milestones_by_state_and_title = relation.reorder(nil).group(:state, :title).count
opened = count_by_state(project_milestones_by_state_and_title, 'active')
closed = count_by_state(project_milestones_by_state_and_title, 'closed')
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index a8c424a6614..b6dd39b860b 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -1,19 +1,17 @@
class ProjectHook < WebHook
- TRIGGERS = {
- push_hooks: :push_events,
- tag_push_hooks: :tag_push_events,
- issue_hooks: :issues_events,
- confidential_issue_hooks: :confidential_issues_events,
- note_hooks: :note_events,
- merge_request_hooks: :merge_requests_events,
- job_hooks: :job_events,
- pipeline_hooks: :pipeline_events,
- wiki_page_hooks: :wiki_page_events
- }.freeze
+ include TriggerableHooks
- TRIGGERS.each do |trigger, event|
- scope trigger, -> { where(event => true) }
- end
+ triggerable_hooks [
+ :push_hooks,
+ :tag_push_hooks,
+ :issue_hooks,
+ :confidential_issue_hooks,
+ :note_hooks,
+ :merge_request_hooks,
+ :job_hooks,
+ :pipeline_hooks,
+ :wiki_page_hooks
+ ]
belongs_to :project
validates :project, presence: true
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 180c479c41b..0528266e5b3 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -1,14 +1,14 @@
class SystemHook < WebHook
- TRIGGERS = {
- repository_update_hooks: :repository_update_events,
- push_hooks: :push_events,
- tag_push_hooks: :tag_push_events
- }.freeze
+ include TriggerableHooks
- TRIGGERS.each do |trigger, event|
- scope trigger, -> { where(event => true) }
- end
+ triggerable_hooks [
+ :repository_update_hooks,
+ :push_hooks,
+ :tag_push_hooks,
+ :merge_request_hooks
+ ]
default_value_for :push_events, false
default_value_for :repository_update_events, true
+ default_value_for :merge_requests_events, false
end
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 5a70e114f56..27729deeac9 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -4,6 +4,7 @@ class WebHook < ActiveRecord::Base
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
validates :url, presence: true, url: true
+ validates :token, format: { without: /\n/ }
def execute(data, hook_name)
WebHookService.new(self, data, hook_name).execute
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 8028ff3875b..f6d4843abc3 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -990,7 +990,13 @@ class MergeRequest < ActiveRecord::Base
notes_association = notes_with_associations
if merged_at
- notes_association = notes_association.where('created_at > ?', merged_at)
+ # It is not guaranteed that Note#created_at will be strictly later than
+ # MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
+ # comparison, as will a HA environment if clocks are not *precisely*
+ # synchronized. Add a minute's leeway to compensate for both possibilities
+ cutoff = merged_at - 1.minute
+
+ notes_association = notes_association.where('created_at >= ?', cutoff)
end
!merge_commit.has_been_reverted?(current_user, notes_association)
diff --git a/app/models/notification_reason.rb b/app/models/notification_reason.rb
new file mode 100644
index 00000000000..c3965565022
--- /dev/null
+++ b/app/models/notification_reason.rb
@@ -0,0 +1,19 @@
+# Holds reasons for a notification to have been sent as well as a priority list to select which reason to use
+# above the rest
+class NotificationReason
+ OWN_ACTIVITY = 'own_activity'.freeze
+ ASSIGNED = 'assigned'.freeze
+ MENTIONED = 'mentioned'.freeze
+
+ # Priority list for selecting which reason to return in the notification
+ REASON_PRIORITY = [
+ OWN_ACTIVITY,
+ ASSIGNED,
+ MENTIONED
+ ].freeze
+
+ # returns the priority of a reason as an integer
+ def self.priority(reason)
+ REASON_PRIORITY.index(reason) || REASON_PRIORITY.length + 1
+ end
+end
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index ab5a96209c7..472b348a545 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -1,27 +1,19 @@
class NotificationRecipient
- attr_reader :user, :type
- def initialize(
- user, type,
- custom_action: nil,
- target: nil,
- acting_user: nil,
- project: nil,
- group: nil,
- skip_read_ability: false
- )
-
+ attr_reader :user, :type, :reason
+ def initialize(user, type, **opts)
unless NotificationSetting.levels.key?(type) || type == :subscription
raise ArgumentError, "invalid type: #{type.inspect}"
end
- @custom_action = custom_action
- @acting_user = acting_user
- @target = target
- @project = project || default_project
- @group = group || @project&.group
+ @custom_action = opts[:custom_action]
+ @acting_user = opts[:acting_user]
+ @target = opts[:target]
+ @project = opts[:project] || default_project
+ @group = opts[:group] || @project&.group
@user = user
@type = type
- @skip_read_ability = skip_read_ability
+ @reason = opts[:reason]
+ @skip_read_ability = opts[:skip_read_ability]
end
def notification_setting
@@ -77,9 +69,15 @@ class NotificationRecipient
def own_activity?
return false unless @acting_user
- return false if @acting_user.notified_of_own_activity?
- user == @acting_user
+ if user == @acting_user
+ # if activity was generated by the same user, change reason to :own_activity
+ @reason = NotificationReason::OWN_ACTIVITY
+ # If the user wants to be notified, we must return `false`
+ !@acting_user.notified_of_own_activity?
+ else
+ false
+ end
end
def has_access?
diff --git a/app/models/project.rb b/app/models/project.rb
index d011b614c69..d0d0fd6e093 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -314,6 +314,7 @@ class Project < ActiveRecord::Base
scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
+ scope :with_issues_available_for_user, ->(current_user) { with_feature_available_for_user(:issues, current_user) }
scope :with_merge_requests_enabled, -> { with_feature_enabled(:merge_requests) }
enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
@@ -567,6 +568,9 @@ class Project < ActiveRecord::Base
RepositoryForkWorker.perform_async(id,
forked_from_project.repository_storage_path,
forked_from_project.disk_path)
+ elsif gitlab_project_import?
+ # Do not retry on Import/Export until https://gitlab.com/gitlab-org/gitlab-ce/issues/26189 is solved.
+ RepositoryImportWorker.set(retry: false).perform_async(self.id)
else
RepositoryImportWorker.perform_async(self.id)
end
@@ -967,9 +971,11 @@ class Project < ActiveRecord::Base
def execute_hooks(data, hooks_scope = :push_hooks)
run_after_commit_or_now do
- hooks.public_send(hooks_scope).each do |hook| # rubocop:disable GitlabSecurity/PublicSend
+ hooks.hooks_for(hooks_scope).each do |hook|
hook.async_execute(data, hooks_scope.to_s)
end
+
+ SystemHooksService.new.execute_hooks(data, hooks_scope)
end
end
@@ -1029,6 +1035,8 @@ class Project < ActiveRecord::Base
end
def fork_source
+ return nil unless forked?
+
forked_from_project || fork_network&.root_project
end
@@ -1435,7 +1443,7 @@ class Project < ActiveRecord::Base
# We'd need to keep track of project full path otherwise directory tree
# created with hashed storage enabled cannot be usefully imported using
# the import rake task.
- repository.rugged.config['gitlab.fullpath'] = gl_full_path
+ repository.raw_repository.write_config(full_path: gl_full_path)
rescue Gitlab::Git::Repository::NoRepository => e
Rails.logger.error("Error writing to .git/config for project #{full_path} (#{id}): #{e.message}.")
nil
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
index 1a236e232f9..b604d860a87 100644
--- a/app/models/project_services/emails_on_push_service.rb
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -2,7 +2,7 @@ class EmailsOnPushService < Service
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
prop_accessor :recipients
- validates :recipients, presence: true, if: :activated?
+ validates :recipients, presence: true, if: :valid_recipients?
def title
'Emails on push'
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 19357f90810..27bdf708c80 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -4,7 +4,7 @@ class IrkerService < Service
prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :recipients, :channels
boolean_accessor :colorize_messages
- validates :recipients, presence: true, if: :activated?
+ validates :recipients, presence: true, if: :valid_recipients?
before_validation :get_channels
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index 6a3118a11b8..9c7b58dead5 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -1,7 +1,7 @@
class PipelinesEmailService < Service
prop_accessor :recipients
boolean_accessor :notify_only_broken_pipelines
- validates :recipients, presence: true, if: :activated?
+ validates :recipients, presence: true, if: :valid_recipients?
def initialize_properties
self.properties ||= { notify_only_broken_pipelines: true }
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 17b9d2cf7b4..87a4350f022 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -37,7 +37,7 @@ class ProjectStatistics < ActiveRecord::Base
def update_build_artifacts_size
self.build_artifacts_size =
project.builds.sum(:artifacts_size) +
- Ci::JobArtifact.artifacts_size_for(self)
+ Ci::JobArtifact.artifacts_size_for(self.project)
end
def update_storage_size
diff --git a/app/models/repository.rb b/app/models/repository.rb
index b4bc0f87458..5b06dc5a39b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -20,6 +20,7 @@ class Repository
attr_accessor :full_path, :disk_path, :project, :is_wiki
delegate :ref_name_for_sha, to: :raw_repository
+ delegate :bundle_to_disk, :create_from_bundle, to: :raw_repository
CreateTreeError = Class.new(StandardError)
@@ -165,16 +166,10 @@ class Repository
return []
end
- raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled|
- commits =
- if is_enabled
- find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- else
- find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- end
-
- CommitCollection.new(project, commits, ref)
+ commits = raw_repository.find_commits_by_message(query, ref, path, limit, offset).map do |c|
+ commit(c)
end
+ CommitCollection.new(project, commits, ref)
end
def find_branch(name, fresh_repo: true)
@@ -259,15 +254,7 @@ class Repository
return if kept_around?(sha)
# This will still fail if the file is corrupted (e.g. 0 bytes)
- begin
- raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
- rescue Rugged::ReferenceError => ex
- Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
- rescue Rugged::OSError => ex
- raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
-
- Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
- end
+ raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
end
def kept_around?(sha)
@@ -747,23 +734,6 @@ class Repository
Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end
- def refs_contains_sha(ref_type, sha)
- args = %W(#{ref_type} --contains #{sha})
- names = run_git(args).first
-
- if names.respond_to?(:split)
- names = names.split("\n").map(&:strip)
-
- names.each do |name|
- name.slice! '* '
- end
-
- names
- else
- []
- end
- end
-
def branch_names_contains(sha)
refs_contains_sha('branch', sha)
end
@@ -928,25 +898,6 @@ class Repository
end
end
- def search_files_by_content(query, ref)
- return [] if empty? || query.blank?
-
- offset = 2
- args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
-
- run_git(args).first.scrub.split(/^--$/)
- end
-
- def search_files_by_name(query, ref)
- safe_query = Regexp.escape(query.sub(/^\/*/, ""))
-
- return [] if empty? || safe_query.blank?
-
- args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query})
-
- run_git(args).first.lines.map(&:strip)
- end
-
def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil)
unless remote_name
remote_name = "tmp-#{SecureRandom.hex}"
@@ -980,6 +931,18 @@ class Repository
raw_repository.ls_files(actual_ref)
end
+ def search_files_by_content(query, ref)
+ return [] if empty? || query.blank?
+
+ raw_repository.search_files_by_content(query, ref)
+ end
+
+ def search_files_by_name(query, ref)
+ return [] if empty?
+
+ raw_repository.search_files_by_name(query, ref)
+ end
+
def copy_gitattributes(ref)
actual_ref = ref || root_ref
begin
@@ -1140,25 +1103,4 @@ class Repository
def rugged_can_be_merged?(their_commit, our_commit)
!rugged.merge_commits(our_commit, their_commit).conflicts?
end
-
- def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- ref ||= root_ref
-
- args = %W(
- log #{ref} --pretty=%H --skip #{offset}
- --max-count #{limit} --grep=#{query} --regexp-ignore-case
- )
- args = args.concat(%W(-- #{path})) if path.present?
-
- git_log_results = run_git(args).first.lines
-
- git_log_results.map { |c| commit(c.chomp) }.compact
- end
-
- def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- raw_repository
- .gitaly_commit_client
- .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
- .map { |c| commit(c) }
- end
end
diff --git a/app/models/route.rb b/app/models/route.rb
index 412f5fb45a5..3d4b5a8b5ee 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -1,4 +1,6 @@
class Route < ActiveRecord::Base
+ include CaseSensitivity
+
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
validates :source, presence: true
@@ -10,6 +12,7 @@ class Route < ActiveRecord::Base
validate :ensure_permanent_paths, if: :path_changed?
+ before_validation :delete_conflicting_orphaned_routes
after_create :delete_conflicting_redirects
after_update :delete_conflicting_redirects, if: :path_changed?
after_update :create_redirect_for_old_path
@@ -78,4 +81,13 @@ class Route < ActiveRecord::Base
def conflicting_redirect_exists?
RedirectRoute.permanent.matching_path_and_descendants(path).exists?
end
+
+ def delete_conflicting_orphaned_routes
+ conflicting = self.class.iwhere(path: path)
+ conflicting_orphaned_routes = conflicting.select do |route|
+ route.source.nil?
+ end
+
+ conflicting_orphaned_routes.each(&:destroy)
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 7f260f7a96b..369cae2e85f 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -2,6 +2,8 @@
# and implement a set of methods
class Service < ActiveRecord::Base
include Sortable
+ include Importable
+
serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
default_value_for :active, false
@@ -118,6 +120,11 @@ class Service < ActiveRecord::Base
nil
end
+ def api_field_names
+ fields.map { |field| field[:name] }
+ .reject { |field_name| field_name =~ /(password|token|key)/ }
+ end
+
def global_fields
fields
end
@@ -290,4 +297,8 @@ class Service < ActiveRecord::Base
project.cache_has_external_wiki
end
end
+
+ def valid_recipients?
+ activated? && !importing?
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 09aa5a7b318..9403da98268 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -318,6 +318,8 @@ class User < ActiveRecord::Base
#
# Returns an ActiveRecord::Relation.
def search(query)
+ return none if query.blank?
+
query = query.downcase
order = <<~SQL
@@ -341,6 +343,8 @@ class User < ActiveRecord::Base
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
def search_with_secondary_emails(query)
+ return none if query.blank?
+
query = query.downcase
email_table = Email.arel_table
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index 229311eb6ee..c226586fba5 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -7,7 +7,7 @@ module Projects
delegate :size, to: :available_public_keys, prefix: true
def new_key
- @key ||= DeployKey.new
+ @key ||= DeployKey.new.tap { |dk| dk.deploy_keys_projects.build }
end
def enabled_keys
diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb
index c75431a79ae..2678f99510c 100644
--- a/app/serializers/deploy_key_entity.rb
+++ b/app/serializers/deploy_key_entity.rb
@@ -3,19 +3,20 @@ class DeployKeyEntity < Grape::Entity
expose :user_id
expose :title
expose :fingerprint
- expose :can_push
expose :destroyed_when_orphaned?, as: :destroyed_when_orphaned
expose :almost_orphaned?, as: :almost_orphaned
expose :created_at
expose :updated_at
- expose :projects, using: ProjectEntity do |deploy_key|
- deploy_key.projects.without_deleted.select { |project| options[:user].can?(:read_project, project) }
+ expose :deploy_keys_projects, using: DeployKeysProjectEntity do |deploy_key|
+ deploy_key.deploy_keys_projects
+ .without_project_deleted
+ .select { |deploy_key_project| Ability.allowed?(options[:user], :read_project, deploy_key_project.project) }
end
expose :can_edit
private
def can_edit
- options[:user].can?(:update_deploy_key, object)
+ Ability.allowed?(options[:user], :update_deploy_key, object)
end
end
diff --git a/app/serializers/deploy_keys_project_entity.rb b/app/serializers/deploy_keys_project_entity.rb
new file mode 100644
index 00000000000..568ef5ab75e
--- /dev/null
+++ b/app/serializers/deploy_keys_project_entity.rb
@@ -0,0 +1,4 @@
+class DeployKeysProjectEntity < Grape::Entity
+ expose :can_push
+ expose :project, using: ProjectEntity
+end
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index 49cf534dc0d..634bf3bd690 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -1,15 +1,11 @@
module MergeRequests
class CreateService < MergeRequests::BaseService
def execute
- # @project is used to determine whether the user can set the merge request's
- # assignee, milestone and labels. Whether they can depends on their
- # permissions on the target project.
- source_project = @project
- @project = Project.find(params[:target_project_id]) if params[:target_project_id]
+ set_projects!
merge_request = MergeRequest.new
merge_request.target_project = @project
- merge_request.source_project = source_project
+ merge_request.source_project = @source_project
merge_request.source_branch = params[:source_branch]
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
@@ -58,5 +54,25 @@ module MergeRequests
pipelines.order(id: :desc).first
end
+
+ def set_projects!
+ # @project is used to determine whether the user can set the merge request's
+ # assignee, milestone and labels. Whether they can depends on their
+ # permissions on the target project.
+ @source_project = @project
+ @project = Project.find(params[:target_project_id]) if params[:target_project_id]
+
+ # make sure that source/target project ids are not in
+ # params so it can't be overridden later when updating attributes
+ # from params when applying quick actions
+ params.delete(:source_project_id)
+ params.delete(:target_project_id)
+
+ unless can?(current_user, :read_project, @source_project) &&
+ can?(current_user, :read_project, @project)
+
+ raise Gitlab::Access::AccessDeniedError
+ end
+ end
end
end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 9f05535d4d4..262622f8bd0 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -9,7 +9,8 @@ module MergeRequests
Gitlab::GitalyClient.allow_n_plus_1_calls(&method(:find_new_commits))
# Be sure to close outstanding MRs before reloading them to avoid generating an
# empty diff during a manual merge
- close_merge_requests
+ close_upon_missing_source_branch_ref
+ post_merge_manually_merged
reload_merge_requests
reset_merge_when_pipeline_succeeds
mark_pending_todos_done
@@ -29,11 +30,22 @@ module MergeRequests
private
+ def close_upon_missing_source_branch_ref
+ # MergeRequest#reload_diff ignores not opened MRs. This means it won't
+ # create an `empty` diff for `closed` MRs without a source branch, keeping
+ # the latest diff state as the last _valid_ one.
+ merge_requests_for_source_branch.reject(&:source_branch_exists?).each do |mr|
+ MergeRequests::CloseService
+ .new(mr.target_project, @current_user)
+ .execute(mr)
+ end
+ end
+
# Collect open merge requests that target same branch we push into
# and close if push to master include last commit from merge request
# We need this to close(as merged) merge requests that were merged into
# target branch manually
- def close_merge_requests
+ def post_merge_manually_merged
commit_ids = @commits.map(&:id)
merge_requests = @project.merge_requests.preload(:latest_merge_request_diff).opened.where(target_branch: @branch_name).to_a
merge_requests = merge_requests.select(&:diff_head_commit)
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 3eb8cfcca9b..6835b14648b 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -11,11 +11,11 @@ module NotificationRecipientService
end
def self.build_recipients(*a)
- Builder::Default.new(*a).recipient_users
+ Builder::Default.new(*a).notification_recipients
end
def self.build_new_note_recipients(*a)
- Builder::NewNote.new(*a).recipient_users
+ Builder::NewNote.new(*a).notification_recipients
end
module Builder
@@ -49,25 +49,24 @@ module NotificationRecipientService
@recipients ||= []
end
- def <<(pair)
- users, type = pair
-
+ def add_recipients(users, type, reason)
if users.is_a?(ActiveRecord::Relation)
users = users.includes(:notification_settings)
end
users = Array(users)
users.compact!
- recipients.concat(users.map { |u| make_recipient(u, type) })
+ recipients.concat(users.map { |u| make_recipient(u, type, reason) })
end
def user_scope
User.includes(:notification_settings)
end
- def make_recipient(user, type)
+ def make_recipient(user, type, reason)
NotificationRecipient.new(
user, type,
+ reason: reason,
project: project,
custom_action: custom_action,
target: target,
@@ -75,14 +74,13 @@ module NotificationRecipientService
)
end
- def recipient_users
- @recipient_users ||=
+ def notification_recipients
+ @notification_recipients ||=
begin
build!
filter!
- users = recipients.map(&:user)
- users.uniq!
- users.freeze
+ recipients = self.recipients.sort_by { |r| NotificationReason.priority(r.reason) }.uniq(&:user)
+ recipients.freeze
end
end
@@ -95,13 +93,13 @@ module NotificationRecipientService
def add_participants(user)
return unless target.respond_to?(:participants)
- self << [target.participants(user), :participating]
+ add_recipients(target.participants(user), :participating, nil)
end
def add_mentions(user, target:)
return unless target.respond_to?(:mentioned_users)
- self << [target.mentioned_users(user), :mention]
+ add_recipients(target.mentioned_users(user), :mention, NotificationReason::MENTIONED)
end
# Get project/group users with CUSTOM notification level
@@ -119,11 +117,11 @@ module NotificationRecipientService
global_users_ids = user_ids_with_project_level_global.concat(user_ids_with_group_level_global)
user_ids += user_ids_with_global_level_custom(global_users_ids, custom_action)
- self << [user_scope.where(id: user_ids), :watch]
+ add_recipients(user_scope.where(id: user_ids), :watch, nil)
end
def add_project_watchers
- self << [project_watchers, :watch]
+ add_recipients(project_watchers, :watch, nil)
end
# Get project users with WATCH notification level
@@ -144,7 +142,7 @@ module NotificationRecipientService
def add_subscribed_users
return unless target.respond_to? :subscribers
- self << [target.subscribers(project), :subscription]
+ add_recipients(target.subscribers(project), :subscription, nil)
end
def user_ids_notifiable_on(resource, notification_level = nil)
@@ -195,7 +193,7 @@ module NotificationRecipientService
return unless target.respond_to? :labels
(labels || target.labels).each do |label|
- self << [label.subscribers(project), :subscription]
+ add_recipients(label.subscribers(project), :subscription, nil)
end
end
end
@@ -222,12 +220,12 @@ module NotificationRecipientService
# Re-assign is considered as a mention of the new assignee
case custom_action
when :reassign_merge_request
- self << [previous_assignee, :mention]
- self << [target.assignee, :mention]
+ add_recipients(previous_assignee, :mention, nil)
+ add_recipients(target.assignee, :mention, NotificationReason::ASSIGNED)
when :reassign_issue
previous_assignees = Array(previous_assignee)
- self << [previous_assignees, :mention]
- self << [target.assignees, :mention]
+ add_recipients(previous_assignees, :mention, nil)
+ add_recipients(target.assignees, :mention, NotificationReason::ASSIGNED)
end
add_subscribed_users
@@ -238,6 +236,12 @@ module NotificationRecipientService
# receive them, too.
add_mentions(current_user, target: target)
+ # Add the assigned users, if any
+ assignees = custom_action == :new_issue ? target.assignees : target.assignee
+ # We use the `:participating` notification level in order to match existing legacy behavior as captured
+ # in existing specs (notification_service_spec.rb ~ line 507)
+ add_recipients(assignees, :participating, NotificationReason::ASSIGNED) if assignees
+
add_labels_subscribers
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index be3b4b2ba07..8c84ccfcc92 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -85,10 +85,11 @@ class NotificationService
recipients.each do |recipient|
mailer.send(
:reassigned_issue_email,
- recipient.id,
+ recipient.user.id,
issue.id,
previous_assignee_ids,
- current_user.id
+ current_user.id,
+ recipient.reason
).deliver_later
end
end
@@ -176,7 +177,7 @@ class NotificationService
action: "resolve_all_discussions")
recipients.each do |recipient|
- mailer.resolved_all_discussions_email(recipient.id, merge_request.id, current_user.id).deliver_later
+ mailer.resolved_all_discussions_email(recipient.user.id, merge_request.id, current_user.id, recipient.reason).deliver_later
end
end
@@ -199,7 +200,7 @@ class NotificationService
recipients = NotificationRecipientService.build_new_note_recipients(note)
recipients.each do |recipient|
- mailer.send(notify_method, recipient.id, note.id).deliver_later
+ mailer.send(notify_method, recipient.user.id, note.id).deliver_later
end
end
@@ -299,7 +300,7 @@ class NotificationService
recipients = NotificationRecipientService.build_recipients(issue, current_user, action: 'moved')
recipients.map do |recipient|
- email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
+ email = mailer.issue_moved_email(recipient.user, issue, new_issue, current_user, recipient.reason)
email.deliver_later
email
end
@@ -339,16 +340,16 @@ class NotificationService
recipients = NotificationRecipientService.build_recipients(target, target.author, action: "new")
recipients.each do |recipient|
- mailer.send(method, recipient.id, target.id).deliver_later
+ mailer.send(method, recipient.user.id, target.id, recipient.reason).deliver_later
end
end
def new_mentions_in_resource_email(target, new_mentioned_users, current_user, method)
recipients = NotificationRecipientService.build_recipients(target, current_user, action: "new")
- recipients = recipients & new_mentioned_users
+ recipients = recipients.select {|r| new_mentioned_users.include?(r.user) }
recipients.each do |recipient|
- mailer.send(method, recipient.id, target.id, current_user.id).deliver_later
+ mailer.send(method, recipient.user.id, target.id, current_user.id, recipient.reason).deliver_later
end
end
@@ -363,7 +364,7 @@ class NotificationService
)
recipients.each do |recipient|
- mailer.send(method, recipient.id, target.id, current_user.id).deliver_later
+ mailer.send(method, recipient.user.id, target.id, current_user.id, recipient.reason).deliver_later
end
end
@@ -381,10 +382,11 @@ class NotificationService
recipients.each do |recipient|
mailer.send(
method,
- recipient.id,
+ recipient.user.id,
target.id,
previous_assignee_id,
- current_user.id
+ current_user.id,
+ recipient.reason
).deliver_later
end
end
@@ -408,7 +410,7 @@ class NotificationService
recipients = NotificationRecipientService.build_recipients(target, current_user, action: "reopen")
recipients.each do |recipient|
- mailer.send(method, recipient.id, target.id, status, current_user.id).deliver_later
+ mailer.send(method, recipient.user.id, target.id, status, current_user.id, recipient.reason).deliver_later
end
end
diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb
index 4ca6414b73b..a3d7f5cbed5 100644
--- a/app/services/projects/gitlab_projects_import_service.rb
+++ b/app/services/projects/gitlab_projects_import_service.rb
@@ -26,7 +26,7 @@ module Projects
end
def tmp_filename
- "#{SecureRandom.hex}_#{params[:path]}"
+ SecureRandom.hex
end
def file
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index af6d77ef5e8..a6b7a6e1416 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -8,7 +8,7 @@ class SystemHooksService
end
def execute_hooks(data, hooks_scope = :all)
- SystemHook.public_send(hooks_scope).find_each do |hook| # rubocop:disable GitlabSecurity/PublicSend
+ SystemHook.hooks_for(hooks_scope).find_each do |hook|
hook.async_execute(data, 'system_hooks')
end
end
diff --git a/app/services/test_hooks/base_service.rb b/app/services/test_hooks/base_service.rb
index 20d90504bd2..e9aefb1fb75 100644
--- a/app/services/test_hooks/base_service.rb
+++ b/app/services/test_hooks/base_service.rb
@@ -9,7 +9,7 @@ module TestHooks
end
def execute
- trigger_key = hook.class::TRIGGERS.key(trigger.to_sym)
+ trigger_key = hook.class.triggers.key(trigger.to_sym)
trigger_data_method = "#{trigger}_data"
if trigger_key.nil? || !self.respond_to?(trigger_data_method, true)
diff --git a/app/services/test_hooks/system_service.rb b/app/services/test_hooks/system_service.rb
index 67552edefc9..9016c77b7f0 100644
--- a/app/services/test_hooks/system_service.rb
+++ b/app/services/test_hooks/system_service.rb
@@ -13,5 +13,12 @@ module TestHooks
def repository_update_events_data
Gitlab::DataBuilder::Repository.sample_data
end
+
+ def merge_requests_events_data
+ merge_request = MergeRequest.of_projects(current_user.projects.select(:id)).first
+ throw(:validation_error, 'Ensure one of your projects has merge requests.') unless merge_request.present?
+
+ merge_request.to_hook_data(current_user)
+ end
end
end
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 6ebc7c89500..36e589d5aa8 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -113,7 +113,7 @@ class WebHookService
'Content-Type' => 'application/json',
'X-Gitlab-Event' => hook_name.singularize.titleize
}.tap do |hash|
- hash['X-Gitlab-Token'] = hook.token if hook.token.present?
+ hash['X-Gitlab-Token'] = Gitlab::Utils.remove_line_breaks(hook.token) if hook.token.present?
end
end
end
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 92370034baa..1420163fd5a 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -12,7 +12,7 @@
%tr
%th.col-sm-2 Title
%th.col-sm-4 Fingerprint
- %th.col-sm-2 Write access allowed
+ %th.col-sm-2 Projects with write access
%th.col-sm-2 Added at
%th.col-sm-2
%tbody
@@ -23,10 +23,8 @@
%td
%code.key-fingerprint= deploy_key.fingerprint
%td
- - if deploy_key.can_push?
- Yes
- - else
- No
+ - deploy_key.projects_with_write_access.each do |project|
+ = link_to project.full_name, admin_project_path(project), class: 'label deploy-project-label'
%td
%span.cgray
added #{time_ago_with_tooltip(deploy_key.created_at)}
diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml
index 645005c6deb..d8f96ed5b0d 100644
--- a/app/views/admin/hooks/_form.html.haml
+++ b/app/views/admin/hooks/_form.html.haml
@@ -38,6 +38,13 @@
%strong Tag push events
%p.light
This URL will be triggered when a new tag is pushed to the repository
+ %div
+ = form.check_box :merge_requests_events, class: 'pull-left'
+ .prepend-left-20
+ = form.label :merge_requests_events, class: 'list-label' do
+ %strong Merge request events
+ %p.light
+ This URL will be triggered when a merge request is created/updated/merged
.form-group
= form.label :enable_ssl_verification, 'SSL verification', class: 'control-label checkbox'
.col-sm-10
diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml
index efb15ccc8df..629b1a9940f 100644
--- a/app/views/admin/hooks/edit.html.haml
+++ b/app/views/admin/hooks/edit.html.haml
@@ -13,7 +13,7 @@
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
= f.submit 'Save changes', class: 'btn btn-create'
- = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: @hook
+ = render 'shared/web_hooks/test_button', triggers: SystemHook.triggers, hook: @hook
= link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index b6e1df5f3ac..bc02d9969d6 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -22,12 +22,12 @@
- @hooks.each do |hook|
%li
.controls
- = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: hook, button_class: 'btn-sm'
+ = render 'shared/web_hooks/test_button', triggers: SystemHook.triggers, hook: hook, button_class: 'btn-sm'
= link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
.monospace= hook.url
%div
- - SystemHook::TRIGGERS.each_value do |event|
+ - SystemHook.triggers.each_value do |event|
- if hook.public_send(event)
%span.label.label-gray= event.to_s.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml
index 7066ed12b95..a01676d82a8 100644
--- a/app/views/admin/jobs/index.html.haml
+++ b/app/views/admin/jobs/index.html.haml
@@ -9,7 +9,12 @@
.nav-controls
- if @all_builds.running_or_pending.any?
- = link_to 'Cancel all', cancel_all_admin_jobs_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+ #stop-jobs-modal
+
+ %button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal',
+ target: '#stop-jobs-modal',
+ url: cancel_all_admin_jobs_path } }
+ = s_('AdminArea|Stop all jobs')
.row-content-block.second-block
#{(@scope || 'all').capitalize} jobs
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index b75dab0acc5..8db7727b80c 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -8,7 +8,7 @@
= (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
%li
= _("Specify the following URL during the Runner setup:")
- %code= root_url(only_path: false)
+ %code#coordinator_address= root_url(only_path: false)
%li
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index ad35d05c29a..31d4b3da4f1 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -7,10 +7,8 @@
- page_title "Activity"
- header_title "Activity", activity_dashboard_path
-.hidden-xs
- = render "projects/last_push"
-
%div{ class: container_class }
+ = render "projects/last_push"
= render 'dashboard/activity_head'
%section.activities
diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml
index 601b6a8b1a7..db856ef7d7b 100644
--- a/app/views/dashboard/groups/_groups.html.haml
+++ b/app/views/dashboard/groups/_groups.html.haml
@@ -1,2 +1,4 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 25bf08c6c12..50f39f93283 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -3,9 +3,6 @@
- header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head'
-= webpack_bundle_tag 'common_vue'
-= webpack_bundle_tag 'groups'
-
- if params[:filter].blank? && @groups.empty?
= render 'shared/groups/empty_state'
- else
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 57a4da353fe..deed774a4a5 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -7,9 +7,8 @@
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
-= render "projects/last_push"
-
%div{ class: container_class }
+ = render "projects/last_push"
- if show_projects?(@projects, params)
= render 'dashboard/projects_head'
= render 'nav'
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index 14f9f8cd70a..b1efe59aadc 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -4,9 +4,8 @@
- page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path
-= render "projects/last_push"
-
%div{ class: container_class }
+ = render "projects/last_push"
= render 'dashboard/projects_head'
- if params[:filter_projects] || any_projects?(@projects)
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 6087f4a0b37..5ddb3ece1cb 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -8,7 +8,7 @@
.login-body
= render 'devise/sessions/new_ldap', server: server
- if password_authentication_enabled_for_web?
- .login-box.tab-pane{ id: 'ldap-standard', role: 'tabpanel' }
+ .login-box.tab-pane{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'devise/sessions/new_base'
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index 94f19ccd44c..270191f9452 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -7,7 +7,7 @@
= link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab'
- if password_authentication_enabled_for_web?
%li
- = link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab'
+ = link_to 'Standard', '#login-pane', 'data-toggle' => 'tab'
- if allow_signup?
%li
= link_to 'Register', '#register-pane', 'data-toggle' => 'tab'
diff --git a/app/views/explore/groups/_groups.html.haml b/app/views/explore/groups/_groups.html.haml
index 91149498248..ff57b39e947 100644
--- a/app/views/explore/groups/_groups.html.haml
+++ b/app/views/explore/groups/_groups.html.haml
@@ -1,2 +1,4 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: explore_groups_path(format: :json), path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 86abdf547cc..efa8b2706da 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -2,9 +2,6 @@
- page_title "Groups"
- header_title "Groups", dashboard_groups_path
-= webpack_bundle_tag 'common_vue'
-= webpack_bundle_tag 'groups'
-
- if current_user
= render 'dashboard/groups_head'
- else
diff --git a/app/views/groups/_children.html.haml b/app/views/groups/_children.html.haml
index 3afb6b2f849..742b40784d3 100644
--- a/app/views/groups/_children.html.haml
+++ b/app/views/groups/_children.html.haml
@@ -1,5 +1,4 @@
-= webpack_bundle_tag 'common_vue'
-= webpack_bundle_tag 'groups'
-
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'false', group_id: group.id, endpoint: group_children_path(group, format: :json), path: group_path(group), form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index b8692009225..fdd72ead2cb 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -5,15 +5,16 @@
= markdown_field(current_application_settings, :help_page_text)
%hr
-- unless current_application_settings.help_page_hide_commercial_content?
- %h1
- GitLab
- Community Edition
- - if user_signed_in?
- %span= Gitlab::VERSION
- %small= link_to Gitlab::REVISION, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ce', Gitlab::REVISION)
- = version_status_badge
+%h1
+ GitLab
+ Community Edition
+ - if user_signed_in?
+ %span= Gitlab::VERSION
+ %small= link_to Gitlab::REVISION, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ce', Gitlab::REVISION)
+ = version_status_badge
+ %hr
+- unless current_application_settings.help_page_hide_commercial_content?
%p.slead
GitLab is open source software to collaborate on code.
%br
diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml
index cb413f197de..3dbdfc97654 100644
--- a/app/views/ide/index.html.haml
+++ b/app/views/ide/index.html.haml
@@ -1,10 +1,9 @@
+- @body_class = 'ide'
- page_title 'IDE'
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'ide'
-
-.ide-flash-container.flash-container
+ = webpack_bundle_tag 'ide', force_same_domain: true
#ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg')} }
.text-center
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 1597621fa78..ea13a5e6d62 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -43,7 +43,6 @@
= webpack_bundle_tag "main"
= webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled
= webpack_bundle_tag "test" if Rails.env.test?
- = webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
diff --git a/app/views/layouts/nav_only.html.haml b/app/views/layouts/nav_only.html.haml
index 6fa4b39dc10..0811211f7b2 100644
--- a/app/views/layouts/nav_only.html.haml
+++ b/app/views/layouts/nav_only.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: I18n.locale, class: page_class }
= render "layouts/head"
- %body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page } }
+ %body{ class: "#{user_application_theme} #{@body_class} nav-only", data: { page: body_data_page } }
= render 'peek/bar'
= render "layouts/header/default"
= render 'shared/outdated_browser'
@@ -10,4 +10,5 @@
= render "layouts/broadcast"
= yield :flash_message
= render "layouts/flash"
- = yield
+ .content{ id: "content-body" }
+ = yield
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 40bf45cece7..ab8b1271212 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -20,7 +20,7 @@
#{link_to "View it on GitLab", @target_url}.
%br
-# Don't link the host in the line below, one link in the email is easier to quickly click than two.
- You're receiving this email because of your account on #{Gitlab.config.gitlab.host}.
+ You're receiving this email because #{notification_reason_text(@reason)}.
If you'd like to receive fewer emails, you can
- if @labels_url
adjust your #{link_to 'label subscriptions', @labels_url}.
diff --git a/app/views/layouts/notify.text.erb b/app/views/layouts/notify.text.erb
index b4ce02eead8..de48f548a1b 100644
--- a/app/views/layouts/notify.text.erb
+++ b/app/views/layouts/notify.text.erb
@@ -9,4 +9,4 @@
<% end -%>
<% end -%>
-You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>.
+<%= "You're receiving this email because #{notification_reason_text(@reason)}." %>
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index aeae7455a1c..66d1d1e8d44 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -3,23 +3,6 @@
= render 'profiles/head'
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
- .col-lg-4
- %h4.prepend-top-0
- Web IDE (Beta)
- %p Enable the new web IDE on this device to make it possible to open and edit multiple files with a single commit
- .col-lg-8.multi-file-editor-options
- = label_tag do
- .preview.append-bottom-10= image_tag "multi-editor-off.png"
- = f.radio_button :multi_file, "off", checked: true
- Off
- = label_tag do
- .preview.append-bottom-10= image_tag "multi-editor-on.png"
- = f.radio_button :multi_file, "on", checked: false
- On
-
- .col-sm-12
- %hr
-
.col-lg-4.application-theme
%h4.prepend-top-0
GitLab navigation theme
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 0f773933ac2..5c76d2d8f51 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -13,11 +13,11 @@
- if @user.avatar?
You can change your avatar here
- if gravatar_enabled?
- or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host}
+ or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
- else
You can upload an avatar here
- if gravatar_enabled?
- or change it at #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host}
+ or change it at #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
.col-lg-8
.clearfix.avatar-image.append-bottom-default
= link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml
index b68eb47c6b4..6f5eb828902 100644
--- a/app/views/projects/_last_push.html.haml
+++ b/app/views/projects/_last_push.html.haml
@@ -1,19 +1,18 @@
- event = last_push_event
- if event && show_last_push_widget?(event)
- %div{ class: container_class }
- .row-content-block.top-block.hidden-xs.white
- .event-last-push
- .event-last-push-text
- %span= s_("LastPushEvent|You pushed to")
- %strong
- = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name'
+ .row-content-block.top-block.hidden-xs.white
+ .event-last-push
+ .event-last-push-text
+ %span= s_("LastPushEvent|You pushed to")
+ %strong
+ = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name'
- - if event.project != @project
- %span= s_("LastPushEvent|at")
- %strong= link_to_project event.project
+ - if event.project != @project
+ %span= s_("LastPushEvent|at")
+ %strong= link_to_project event.project
- #{time_ago_with_tooltip(event.created_at)}
+ #{time_ago_with_tooltip(event.created_at)}
- .pull-right
- = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do
- #{ _('Create merge request') }
+ .pull-right
+ = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm qa-create-merge-request" do
+ #{ _('Create merge request') }
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index bd99eb93cc8..d367bd6be7b 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -34,7 +34,7 @@
.form-group.visibility-level-setting
= f.label :visibility_level, class: 'label-light' do
Visibility Level
- = link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }
+ = link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml
index 32901d30b96..aebdfbc8218 100644
--- a/app/views/projects/_readme.html.haml
+++ b/app/views/projects/_readme.html.haml
@@ -9,15 +9,15 @@
- else
.row-content-block.second-block.center
- %h3.page-title
+ %h4
This project does not have a README yet
+
- if can?(current_user, :push_code, @project)
%p
A
%code README
file contains information about other files in a repository and is commonly
distributed with computer software, forming part of its documentation.
+ GitLab will render it here instead of this message.
%p
- We recommend you to
- = link_to "add a README", add_special_file_path(@project, file_name: 'README.md')
- file to the repository and GitLab will render it here instead of this message.
+ = link_to "Add Readme", add_special_file_path(@project, file_name: 'README.md'), class: 'btn btn-new'
diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml
index d0ab39033cf..b28a375e956 100644
--- a/app/views/projects/activity.html.haml
+++ b/app/views/projects/activity.html.haml
@@ -2,6 +2,7 @@
- page_title _("Activity")
-= render 'projects/last_push'
+%div{ class: container_class }
+ = render 'projects/last_push'
= render 'projects/activity'
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 4d358052d43..2ed454131af 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -6,9 +6,10 @@
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'blob'
-= render 'projects/last_push'
%div{ class: container_class }
+ = render 'projects/last_push'
+
#tree-holder.tree-holder
= render 'blob', blob: @blob
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index a94d9c14722..18e948ce35a 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -6,37 +6,33 @@
%ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
- can_create_issue = can?(current_user, :create_issue, @project)
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
- - can_create_snippet = can?(current_user, :create_snippet, @project)
+ - can_create_project_snippet = can?(current_user, :create_project_snippet, @project)
+
+ - if can_create_issue || merge_project || can_create_project_snippet
+ %li.dropdown-header= _('This project')
- if can_create_issue
- %li
- = link_to _('New issue'), new_project_issue_path(@project)
+ %li= link_to _('New issue'), new_project_issue_path(@project)
+
- if merge_project
- %li
- = link_to _('New merge request'), project_new_merge_request_path(merge_project)
- - if can_create_snippet
- %li
- = link_to _('New snippet'), new_project_snippet_path(@project)
+ %li= link_to _('New merge request'), project_new_merge_request_path(merge_project)
- - if can_create_issue || merge_project || can_create_snippet
- %li.divider
+ - if can_create_project_snippet
+ %li= link_to _('New snippet'), new_project_snippet_path(@project)
+
+ - if can?(current_user, :push_code, @project)
+ %li.dropdown-header= _('This repository')
- if can?(current_user, :push_code, @project)
- %li
- = link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master')
+ %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master')
- unless @project.empty_repo?
- %li
- = link_to _('New branch'), new_project_branch_path(@project)
- %li
- = link_to _('New tag'), new_project_tag_path(@project)
+ %li= link_to _('New branch'), new_project_branch_path(@project)
+ %li= link_to _('New tag'), new_project_tag_path(@project)
- elsif current_user && current_user.already_forked?(@project)
- %li
- = link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master')
+ %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master')
- elsif can?(current_user, :fork_project, @project)
- %li
- - continue_params = { to: project_new_blob_path(@project, @project.default_branch || 'master'),
- notice: edit_in_new_fork_notice,
- notice_now: edit_in_new_fork_notice_now }
- - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id,
- continue: continue_params)
- = link_to _('New file'), fork_path, method: :post
+ - continue_params = { to: project_new_blob_path(@project, @project.default_branch || 'master'),
+ notice: edit_in_new_fork_notice,
+ notice_now: edit_in_new_fork_notice_now }
+ - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
+ %li= link_to _('New file'), fork_path, method: :post
diff --git a/app/views/projects/clusters/_cluster.html.haml b/app/views/projects/clusters/_cluster.html.haml
index 3943dfc0856..20ee8086f93 100644
--- a/app/views/projects/clusters/_cluster.html.haml
+++ b/app/views/projects/clusters/_cluster.html.haml
@@ -12,11 +12,12 @@
.table-section.section-10
.table-mobile-header{ role: "rowheader" }
.table-mobile-content
- %button{ type: "button",
- class: "js-toggle-cluster-list project-feature-toggle #{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}",
+ %button.js-project-feature-toggle.project-feature-toggle{ type: "button",
+ class: "#{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}",
"aria-label": s_("ClusterIntegration|Toggle Cluster"),
disabled: !cluster.can_toggle_cluster?,
data: { endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
+ %input.js-project-feature-toggle-input{ type: "hidden", value: cluster.enabled? }
= icon("spinner spin", class: "loading-icon")
%span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
diff --git a/app/views/projects/clusters/_integration_form.html.haml b/app/views/projects/clusters/_integration_form.html.haml
index 9d593ffc021..0af6e6e0577 100644
--- a/app/views/projects/clusters/_integration_form.html.haml
+++ b/app/views/projects/clusters/_integration_form.html.haml
@@ -10,13 +10,12 @@
= s_('ClusterIntegration|Cluster integration is enabled for this project.')
- else
= s_('ClusterIntegration|Cluster integration is disabled for this project.')
- %label.append-bottom-10
- = field.hidden_field :enabled, { class: 'js-toggle-input'}
-
+ %label.append-bottom-10.js-cluster-enable-toggle-area
%button{ type: 'button',
- class: "js-toggle-cluster project-feature-toggle #{'is-checked' unless !@cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
+ class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if @cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
"aria-label": s_("ClusterIntegration|Toggle Cluster"),
disabled: !can?(current_user, :update_cluster, @cluster) }
+ = field.hidden_field :enabled, { class: 'js-project-feature-toggle-input'}
%span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index d0a380516f9..93407956f56 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -4,6 +4,7 @@
- branch_label = s_('ChangeTypeActionLabel|Revert in branch')
- revert_merge_request = _('Revert this merge request')
- revert_commit = _('Revert this commit')
+ - description = s_('ChangeTypeAction|This will create a new commit in order to revert the existing changes.')
- title = commit.merged_merge_request(current_user) ? revert_merge_request : revert_commit
- when 'cherry-pick'
- label = s_('ChangeTypeAction|Cherry-pick')
@@ -17,6 +18,8 @@
%a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title= title
.modal-body
+ - if description
+ %p.append-bottom-20= description
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
.form-group.branch
= label_tag 'start_branch', branch_label, class: 'control-label'
diff --git a/app/views/projects/commit/_other_user_signature_badge.html.haml b/app/views/projects/commit/_other_user_signature_badge.html.haml
index 80eca96f7ce..d7bf2dc0cb6 100644
--- a/app/views/projects/commit/_other_user_signature_badge.html.haml
+++ b/app/views/projects/commit/_other_user_signature_badge.html.haml
@@ -1,6 +1,6 @@
- title = capture do
This commit was signed with a different user's verified signature.
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'icon_status_notfound_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml b/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
index e737de48e22..22ffd66ff8e 100644
--- a/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
+++ b/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
@@ -2,6 +2,6 @@
This commit was signed with a verified signature, but the committer email
is <strong>not verified</strong> to belong to the same user.
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: ['invalid'], icon: 'icon_status_notfound_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: 'Unverified', css_class: ['invalid'], icon: 'status_notfound_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml
index 44aa8002f12..aac020b42c5 100644
--- a/app/views/projects/commit/_signature_badge.html.haml
+++ b/app/views/projects/commit/_signature_badge.html.haml
@@ -10,7 +10,7 @@
- title = capture do
.gpg-popover-status
.gpg-popover-icon{ class: css_class }
- = render "shared/icons/#{icon}.svg"
+ = sprite_icon(icon)
%div
= title
diff --git a/app/views/projects/commit/_unverified_signature_badge.html.haml b/app/views/projects/commit/_unverified_signature_badge.html.haml
index 1af58027b83..00e1efe0582 100644
--- a/app/views/projects/commit/_unverified_signature_badge.html.haml
+++ b/app/views/projects/commit/_unverified_signature_badge.html.haml
@@ -1,6 +1,6 @@
- title = capture do
This commit was signed with an <strong>unverified</strong> signature.
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'icon_status_notfound_borderless' }
+- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'status_notfound_borderless' }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_verified_signature_badge.html.haml b/app/views/projects/commit/_verified_signature_badge.html.haml
index 423beba2120..31408806be7 100644
--- a/app/views/projects/commit/_verified_signature_badge.html.haml
+++ b/app/views/projects/commit/_verified_signature_badge.html.haml
@@ -2,6 +2,6 @@
This commit was signed with a <strong>verified</strong> signature and the
committer email is verified to belong to the same user.
-- locals = { signature: signature, title: title, label: 'Verified', css_class: 'valid', icon: 'icon_status_success_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: 'Verified', css_class: 'valid', icon: 'status_success_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder
index d806acdda13..04914888763 100644
--- a/app/views/projects/commits/_commit.atom.builder
+++ b/app/views/projects/commits/_commit.atom.builder
@@ -1,7 +1,7 @@
xml.entry do
xml.id project_commit_url(@project, id: commit.id)
xml.link href: project_commit_url(@project, id: commit.id)
- xml.title truncate(commit.title, length: 80)
+ xml.title truncate(commit.title, length: 80, escape: false)
xml.updated commit.committed_date.xmlschema
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
@@ -10,5 +10,5 @@ xml.entry do
xml.email commit.author_email
end
- xml.summary markdown(commit.description, pipeline: :single_line)
+ xml.summary markdown(commit.description, pipeline: :single_line), type: 'html'
end
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index edaa3a1119e..c363180d0db 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -10,13 +10,15 @@
%p.light.append-bottom-0
Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh/README")
- .form-group
- .checkbox
- = f.label :can_push do
- = f.check_box :can_push
- %strong Write access allowed
- .form-group
- %p.light.append-bottom-0
- Allow this key to push to repository as well? (Default only allows pull access.)
+
+ = f.fields_for :deploy_keys_projects do |deploy_keys_project_form|
+ .form-group
+ .checkbox
+ = deploy_keys_project_form.label :can_push do
+ = deploy_keys_project_form.check_box :can_push
+ %strong Write access allowed
+ .form-group
+ %p.light.append-bottom-0
+ Allow this key to push to repository as well? (Default only allows pull access.)
= f.submit "Add key", class: "btn-create btn"
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 58e89a481a9..ab225796b12 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -6,8 +6,9 @@
= render "home_panel"
.row-content-block.second-block.center
- %h3.page-title
+ %h4
The repository for this project is empty
+
- if can?(current_user, :push_code, @project)
%p
If you already have files you can push them using command line instructions below.
@@ -28,8 +29,8 @@
%p
- link = link_to(s_('AutoDevOps|Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'))
= s_('AutoDevOps|You can activate %{link_to_settings} for this project.').html_safe % { link_to_settings: link }
- %p
- = s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
+ %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
+ %p= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master'), class: 'btn btn-new'
- if can?(current_user, :push_code, @project)
%div{ class: container_class }
@@ -79,4 +80,4 @@
- if can? current_user, :remove_project, @project
.prepend-top-20
- = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
+ = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove pull-right"
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index ad94113fffd..5257b42548e 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -3,7 +3,6 @@
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'common_d3'
- = webpack_bundle_tag 'monitoring'
.prometheus-container{ class: container_class }
.top-area
diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml
index b1219f019d7..dcc1f0e3fbe 100644
--- a/app/views/projects/hooks/edit.html.haml
+++ b/app/views/projects/hooks/edit.html.haml
@@ -12,7 +12,7 @@
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Save changes', class: 'btn btn-create'
- = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: @hook
+ = render 'shared/web_hooks/test_button', triggers: ProjectHook.triggers, hook: @hook
= link_to 'Remove', project_hook_path(@project, @hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr
diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml
index 311934d9c33..c66313bdbf3 100644
--- a/app/views/projects/jobs/_empty_state.html.haml
+++ b/app/views/projects/jobs/_empty_state.html.haml
@@ -1,7 +1,7 @@
- illustration = local_assigns.fetch(:illustration)
- illustration_size = local_assigns.fetch(:illustration_size)
- title = local_assigns.fetch(:title)
-- content = local_assigns.fetch(:content, nil)
+- content = local_assigns.fetch(:content)
- action = local_assigns.fetch(:action, nil)
.row.empty-state
@@ -11,8 +11,7 @@
.col-xs-12
.text-content
%h4.text-center= title
- - if content
- %p= content
+ %p= content
- if action
.text-center
= action
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index 1e6d6f67e66..93efa7e8e86 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -95,12 +95,18 @@
title: _('This job requires a manual action'),
content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'),
action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') )
- - else
+ - elsif @build.created?
= render 'empty_state',
illustration: 'illustrations/job_not_triggered.svg',
illustration_size: 'svg-306',
- title: _('This job has not been triggered yet')
-
+ title: _('This job has not been triggered yet'),
+ content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered')
+ - else
+ = render 'empty_state',
+ illustration: 'illustrations/pending_job_empty.svg',
+ illustration_size: 'svg-430',
+ title: _('This job has not started yet'),
+ content: _('This job is in pending state and is waiting to be picked by a runner')
= render "sidebar"
.js-build-options{ data: javascript_build_options }
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 2ded7484151..640d2791dc1 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -10,7 +10,8 @@
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'filtered_search'
-= render 'projects/last_push'
+%div{ class: container_class }
+ = render 'projects/last_push'
- if @project.merge_requests.exists?
%div{ class: container_class }
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index fcbf7cb802b..6a7bc4b1888 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -12,6 +12,8 @@
New milestone
.milestones
+ #delete-milestone-modal
+
%ul.content-list
= render @milestones
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 5dd4d2c949c..623c42ba88e 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -35,8 +35,18 @@
- else
= link_to 'Reopen milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
- = link_to project_milestone_path(@project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
- Delete
+ %button.js-delete-milestone-button.btn.btn-grouped.btn-danger{ data: { toggle: 'modal',
+ target: '#delete-milestone-modal',
+ milestone_id: @milestone.id,
+ milestone_title: markdown_field(@milestone, :title),
+ milestone_url: project_milestone_path(@project, @milestone),
+ milestone_issue_count: @milestone.issues.count,
+ milestone_merge_request_count: @milestone.merge_requests.count },
+ disabled: true }
+ = _('Delete')
+ = icon('spin spinner', class: 'js-loading-icon hidden' )
+
+ #delete-milestone-modal
%a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" }
= icon('angle-double-left')
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 2f56630c22e..61ae0ebbce6 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -4,8 +4,6 @@
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'project_new'
.project-edit-container
.project-edit-errors
diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml
index 82516cb4bcf..cd003107d66 100644
--- a/app/views/projects/settings/integrations/_project_hook.html.haml
+++ b/app/views/projects/settings/integrations/_project_hook.html.haml
@@ -3,14 +3,14 @@
.col-md-8.col-lg-7
%strong.light-header= hook.url
%div
- - ProjectHook::TRIGGERS.each_value do |event|
+ - ProjectHook.triggers.each_value do |event|
- if hook.public_send(event)
%span.label.label-gray.deploy-project-label= event.to_s.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5
%span.append-right-10.inline
SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
= link_to 'Edit', edit_project_hook_path(@project, hook), class: 'btn btn-sm'
- = render 'shared/web_hooks/test_button', triggers: ProjectHook::TRIGGERS, hook: hook, button_class: 'btn-sm'
+ = render 'shared/web_hooks/test_button', triggers: ProjectHook.triggers, hook: hook, button_class: 'btn-sm'
= link_to project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-transparent' do
%span.sr-only Remove
= icon('trash')
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 7a68aa16aa4..d3e867e124c 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -7,7 +7,9 @@
= render partial: 'flash_messages', locals: { project: @project }
-= render "projects/last_push"
+%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
+ = render "projects/last_push"
+
= render "home_panel"
- if can?(current_user, :download_code, @project)
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index d1ecef39475..05539dfed7c 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -24,6 +24,8 @@
.add-to-tree-dropdown
%ul.dropdown-menu
- if can_edit_tree?
+ %li.dropdown-header
+ #{ _('This directory') }
%li
= link_to project_new_blob_path(@project, @id) do
#{ _('New file') }
@@ -60,6 +62,8 @@
#{ _('New directory') }
%li.divider
+ %li.dropdown-header
+ #{ _('This repository') }
%li
= link_to new_project_branch_path(@project) do
#{ _('New branch') }
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 709be20e00f..3b4057e56d0 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -6,7 +6,6 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
-= render 'projects/last_push'
-
%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] }
+ = render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index 314d8e9cb25..915e648a5d3 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -57,25 +57,24 @@
Titles and Filenames
%span.badge
= @search_results.snippet_titles_count
-
- else
%li{ class: active_when(@scope == 'projects') }
= link_to search_filter_path(scope: 'projects') do
Projects
%span.badge
- = @search_results.projects_count
+ = limited_count(@search_results.limited_projects_count)
%li{ class: active_when(@scope == 'issues') }
= link_to search_filter_path(scope: 'issues') do
Issues
%span.badge
- = @search_results.issues_count
+ = limited_count(@search_results.limited_issues_count)
%li{ class: active_when(@scope == 'merge_requests') }
= link_to search_filter_path(scope: 'merge_requests') do
Merge requests
%span.badge
- = @search_results.merge_requests_count
+ = limited_count(@search_results.limited_merge_requests_count)
%li{ class: active_when(@scope == 'milestones') }
= link_to search_filter_path(scope: 'milestones') do
Milestones
%span.badge
- = @search_results.milestones_count
+ = limited_count(@search_results.limited_milestones_count)
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 02133d09cdf..60ef44482f0 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -2,7 +2,8 @@
= render partial: "search/results/empty"
- else
.row-content-block
- = search_entries_info(@search_objects, @scope, @search_term)
+ - unless @search_objects.is_a?(Kaminari::PaginatableWithoutCount)
+ = search_entries_info(@search_objects, @scope, @search_term)
- unless @show_snippets
- if @project
in project #{link_to @project.name_with_namespace, [@project.namespace.becomes(Namespace), @project]}
@@ -22,4 +23,4 @@
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects
- if @scope != 'projects'
- = paginate(@search_objects, theme: 'gitlab')
+ = paginate_collection(@search_objects)
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index d0b9e891b82..cb21f90696f 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -1,5 +1,3 @@
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('group')
- parent = @group.parent
- group_path = root_url
- group_path << parent.full_path + '/' if parent
diff --git a/app/views/shared/deploy_keys/_form.html.haml b/app/views/shared/deploy_keys/_form.html.haml
index e6075c3ae3a..87c2965bb21 100644
--- a/app/views/shared/deploy_keys/_form.html.haml
+++ b/app/views/shared/deploy_keys/_form.html.haml
@@ -1,5 +1,6 @@
- form = local_assigns.fetch(:form)
- deploy_key = local_assigns.fetch(:deploy_key)
+- deploy_keys_project = deploy_key.deploy_keys_project_for(@project)
= form_errors(deploy_key)
@@ -20,11 +21,13 @@
.col-sm-10
= form.text_field :fingerprint, class: 'form-control', readonly: 'readonly'
-.form-group
- .control-label
- .col-sm-10
- = form.label :can_push do
- = form.check_box :can_push
- %strong Write access allowed
- %p.light.append-bottom-0
- Allow this key to push to repository as well? (Default only allows pull access.)
+- if deploy_keys_project.present?
+ = form.fields_for :deploy_keys_projects, deploy_keys_project do |deploy_keys_project_form|
+ .form-group
+ .control-label
+ .col-sm-10
+ = deploy_keys_project_form.label :can_push do
+ = deploy_keys_project_form.check_box :can_push
+ %strong Write access allowed
+ %p.light.append-bottom-0
+ Allow this key to push to repository as well? (Default only allows pull access.)
diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml
index f65bb6a29e6..38e9899ca4b 100644
--- a/app/views/shared/form_elements/_description.html.haml
+++ b/app/views/shared/form_elements/_description.html.haml
@@ -15,7 +15,7 @@
= render layout: 'projects/md_preview', locals: { url: preview_url, referenced_users: true } do
= render 'projects/zen', f: form, attr: :description,
- classes: 'note-textarea',
+ classes: 'note-textarea qa-issuable-form-description',
placeholder: "Write a comment or drag your files here...",
supports_quick_actions: supports_quick_actions
= render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
diff --git a/app/views/shared/icons/_icon_status_notfound_borderless.svg b/app/views/shared/icons/_icon_status_notfound_borderless.svg
deleted file mode 100644
index e58bd264ef8..00000000000
--- a/app/views/shared/icons/_icon_status_notfound_borderless.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="22" height="22" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0V11.78a5.9 5.9 0 0 0 .827-.492z" fill-rule="nonzero"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></svg>
diff --git a/app/views/shared/icons/_icon_status_success_borderless.svg b/app/views/shared/icons/_icon_status_success_borderless.svg
deleted file mode 100644
index 8ee5be7ab78..00000000000
--- a/app/views/shared/icons/_icon_status_success_borderless.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M11.4583333,12.375 L8.70008808,12.375 C8.45889044,12.375 8.25,12.5826293 8.25,12.8387529 L8.25,14.2029137 C8.25,14.4551799 8.4515113,14.6666667 8.70008808,14.6666667 L12.9619841,14.6666667 C13.3891296,14.6666667 13.75,14.3193051 13.75,13.8908129 L13.75,13.2899463 L13.75,6.42552703 C13.75,6.16226705 13.5423707,5.95833333 13.2862471,5.95833333 L11.9220863,5.95833333 C11.6698201,5.95833333 11.4583333,6.16750307 11.4583333,6.42552703 L11.4583333,12.375 Z" id="Combined-Shape" transform="translate(11.000000, 10.312500) rotate(-315.000000) translate(-11.000000, -10.312500) "></path></svg>
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 8442d7ff4a2..7704c88905b 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -22,7 +22,7 @@
= render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, show_started: true
.filter-item.inline.labels-filter
- = render "shared/issuable/label_dropdown", selected: finder.labels.select(:title).uniq, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
+ = render "shared/issuable/label_dropdown", selected: selected_labels, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
- if issuable_filter_present?
.filter-item.inline.reset-filters
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index bb02dfa0d3a..79021a08719 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -65,7 +65,7 @@
%span.append-right-10
- if issuable.new_record?
- = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
+ = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create qa-issuable-create-button'
- else
= form.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index e0009a35b9f..cc00c3c0bfd 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -34,7 +34,7 @@
Milestone
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
- = link_to 'Edit', '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
+ = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
.value.hide-collapsed
- if issuable.milestone
= link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_tooltip_title(issuable.milestone), data: { container: "body", html: 1 }
@@ -60,7 +60,7 @@
Due date
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- = link_to 'Edit', '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
+ = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
.value.hide-collapsed
%span.value-content
- if issuable.due_date
@@ -95,7 +95,7 @@
Labels
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
- = link_to 'Edit', '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
+ = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
.value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label|
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index 58782fa5f58..0fca4162ec9 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -13,7 +13,7 @@
Assignee
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
- = link_to 'Edit', '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
+ = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link pull-right'
- if !signed_in
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 64826d41d60..e81639f35ea 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -6,7 +6,7 @@
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
- autocomplete: 'off', class: 'form-control pad'
+ autocomplete: 'off', class: 'form-control pad qa-issuable-form-title'
- if issuable.respond_to?(:work_in_progress?)
%p.help-block
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 50f4901a2dd..e08a49b4e59 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -56,6 +56,13 @@
= link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped"
- = link_to project_milestone_path(milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove btn-grouped" do
- Delete
-
+ %button.js-delete-milestone-button.btn.btn-xs.btn-grouped.btn-danger{ data: { toggle: 'modal',
+ target: '#delete-milestone-modal',
+ milestone_id: milestone.id,
+ milestone_title: markdown_field(milestone, :title),
+ milestone_url: project_milestone_path(milestone.project, milestone),
+ milestone_issue_count: milestone.issues.count,
+ milestone_merge_request_count: milestone.merge_requests.count },
+ disabled: true }
+ = _('Delete')
+ = icon('spin spinner', class: 'js-loading-icon hidden' )
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index 1f0e7629fb4..ad4d39b4aa1 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -50,7 +50,7 @@
= form.check_box :merge_requests_events, class: 'pull-left'
.prepend-left-20
= form.label :merge_requests_events, class: 'list-label' do
- %strong Merge Request events
+ %strong Merge request events
%p.light
This URL will be triggered when a merge request is created/updated/merged
%li
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 4f4e81c705f..90aa1be30ac 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -58,15 +58,15 @@
= icon('skype')
- unless @user.linkedin.blank?
.profile-link-holder.middle-dot-divider
- = link_to linkedin_url(@user), title: "LinkedIn" do
+ = link_to linkedin_url(@user), title: "LinkedIn", target: '_blank', rel: 'noopener noreferrer nofollow' do
= icon('linkedin-square')
- unless @user.twitter.blank?
.profile-link-holder.middle-dot-divider
- = link_to twitter_url(@user), title: "Twitter" do
+ = link_to twitter_url(@user), title: "Twitter", target: '_blank', rel: 'noopener noreferrer nofollow' do
= icon('twitter-square')
- unless @user.website_url.blank?
.profile-link-holder.middle-dot-divider
- = link_to @user.short_website_url, @user.full_website_url, class: 'text-link'
+ = link_to @user.short_website_url, @user.full_website_url, class: 'text-link', target: '_blank', rel: 'noopener noreferrer nofollow'
- unless @user.location.blank?
.profile-link-holder.middle-dot-divider
= icon('map-marker')
diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb
index 4e3c691e8da..116bc185b38 100644
--- a/app/workers/repository_check/single_repository_worker.rb
+++ b/app/workers/repository_check/single_repository_worker.rb
@@ -20,10 +20,7 @@ module RepositoryCheck
# Historically some projects never had their wiki repos initialized;
# this happens on project creation now. Let's initialize an empty repo
# if it is not already there.
- begin
- project.create_wiki
- rescue Rugged::RepositoryError
- end
+ project.create_wiki
git_fsck(project.wiki.repository)
else
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 31e2798c36b..d79b5ee5346 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -20,7 +20,11 @@ class RepositoryImportWorker
# to those importers to mark the import process as complete.
return if service.async?
- raise result[:message] if result[:status] == :error
+ if result[:status] == :error
+ fail_import(project, result[:message]) if project.gitlab_project_import?
+
+ raise result[:message]
+ end
project.after_import
end
@@ -33,4 +37,8 @@ class RepositoryImportWorker
Rails.logger.info("Project #{project.full_path} was in inconsistent state (#{project.import_status}) while importing.")
false
end
+
+ def fail_import(project, message)
+ project.mark_import_as_failed(message)
+ end
end
diff --git a/bin/profile-url b/bin/profile-url
new file mode 100755
index 00000000000..d8d09641624
--- /dev/null
+++ b/bin/profile-url
@@ -0,0 +1,57 @@
+#!/usr/bin/env ruby
+require 'optparse'
+
+options = {}
+
+opt_parser = OptionParser.new do |opt|
+ opt.banner = <<DOCSTRING
+Profile a URL on this GitLab instance.
+
+Usage:
+ #{__FILE__} url --output=<profile-html> --sql=<sql-log> [--user=<user>] [--post=<post-data>]
+
+Example:
+ #{__FILE__} /dashboard/issues --output=dashboard-profile.html --sql=dashboard.log --user=root
+DOCSTRING
+ opt.separator ''
+ opt.separator 'Options:'
+
+ opt.on('-o', '--output=/tmp/profile.html', 'profile output filename') do |output|
+ options[:profile_output] = output
+ end
+
+ opt.on('-s', '--sql=/tmp/profile_sql.txt', 'SQL output filename') do |sql|
+ options[:sql_output] = sql
+ end
+
+ opt.on('-u', '--user=root', 'User to authenticate as') do |username|
+ options[:username] = username
+ end
+
+ opt.on('-p', "--post='user=john&pass=test'", 'Send HTTP POST data') do |post_data|
+ options[:post_data] = post_data
+ end
+end
+
+opt_parser.parse!
+options[:url] = ARGV[0]
+
+if options[:url].nil? ||
+ options[:profile_output].nil? ||
+ options[:sql_output].nil?
+ puts opt_parser
+ exit
+end
+
+require File.expand_path('../config/environment', File.dirname(__FILE__))
+
+result = Gitlab::Profiler.profile(options[:url],
+ logger: Logger.new(options[:sql_output]),
+ post_data: options[:post_data],
+ user: User.find_by_username(options[:username]),
+ private_token: ENV['PRIVATE_TOKEN'])
+
+printer = RubyProf::CallStackPrinter.new(result)
+file = File.open(options[:profile_output], 'w')
+printer.print(file)
+file.close
diff --git a/changelogs/archive.md b/changelogs/archive.md
index c68ab694d39..fe461a6ac5e 100644
--- a/changelogs/archive.md
+++ b/changelogs/archive.md
@@ -1,3 +1,3251 @@
+## 8.15.8 (2017-03-19)
+
+- Only show public emails in atom feeds.
+- 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.
+
+## 8.15.7 (2017-02-15)
+
+- No changes.
+
+## 8.15.6 (2017-02-14)
+
+- Patch Asciidocs rendering to block XSS.
+- Fix XSS vulnerability in SVG attachments.
+- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
+- Patch XSS vulnerability in RDOC support.
+
+## 8.15.5 (2017-01-20)
+
+- Ensure export files are removed after a namespace is deleted.
+- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
+- Prevent users from creating notes on resources they can't access.
+- Prevent users from deleting system deploy keys via the project deploy key API.
+- Upgrade omniauth gem to 1.3.2.
+
+## 8.15.4 (2017-01-09)
+
+- Make successful pipeline emails off for watchers. !8176
+- Speed up group milestone index by passing group_id to IssuesFinder. !8363
+- Don't instrument 405 Grape calls. !8445
+- Update the gitlab-markup gem to the version 1.5.1. !8509
+- Updated Turbolinks to mitigate potential XSS attacks.
+- Re-order update steps in the 8.14 -> 8.15 upgrade guide.
+- Re-add Google Cloud Storage as a backup strategy.
+
+## 8.15.3 (2017-01-06)
+
+- Rename wiki_events to wiki_page_events in project hooks API to avoid errors. !8425
+- Rename projects wth reserved names. !8234
+- Cache project authorizations even when user has access to zero projects. !8327
+- Fix a minor grammar error in merge request widget. !8337
+- Fix unclear closing issue behaviour on Merge Request show page. !8345 (Gabriel Gizotti)
+- fix border in login session tabs. !8346
+- Copy, don't move uploaded avatar files. !8396
+- Increases width of mini-pipeline-graph dropdown to prevent wrong position on chrome on ubuntu. !8399
+- Removes invalid html and unneed CSS to prevent shaking in the pipelines tab. !8411
+- Gitlab::LDAP::Person uses LDAP attributes configuration. !8418
+- Fix 500 errors when creating a user with identity via API. !8442
+- Whitelist next project names: assets, profile, public. !8470
+- Fixed regression of note-headline-light where it was always placed on 2 lines, even on wide viewports.
+- Fix 500 error when visit group from admin area if group name contains dot.
+- Fix cross-project references copy to include the project reference.
+- Fix 500 error renaming group.
+- Fixed GFM dropdown not showing on new lines.
+
+## 8.15.2 (2016-12-27)
+
+- Fix finding the latest pipeline. !8301
+- Fix mr list timestamp alignment. !8271
+- Fix discussion overlap text in regular screens. !8273
+- Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282
+- Fix line breaking in nodes of the pipeline graph in firefox. !8292
+- Fixes confendential warning text alignment. !8293
+- Hide Scroll Top button for failed build page. !8295
+- Fix finding the latest pipeline. !8301
+- Disable PostgreSQL statement timeouts when removing unneeded services. !8322
+- Fix timeout when MR contains large files marked as binary by .gitattributes.
+- Rename "autodeploy" to "auto deploy".
+- Fixed GFM autocomplete error when no data exists.
+- Fixed resolve discussion note button color.
+
+## 8.15.1 (2016-12-23)
+
+- Push payloads schedule at most 100 commits, instead of all commits.
+- Fix Mattermost command creation by specifying username.
+- Do not override incoming webhook for mattermost and slack.
+- Adds background color for disabled state to merge when succeeds dropdown. !8222
+- Standardises font-size for titles in Issues, Merge Requests and Merge Request widget. !8235
+- Fix Pipeline builds list blank on MR. !8255
+- Do not show retried builds in pipeline stage dropdown. !8260
+
+## 8.15.0 (2016-12-22)
+
+- Whitelist next project names: notes, services.
+- Use Grape's new Route methods.
+- Fixed issue boards scrolling with a lot of lists & issues.
+- Remove unnecessary sentences for status codes in the API documentation. (Luis Alonso Chavez Armendariz)
+- Allow unauthenticated access to Repositories Files API GET endpoints.
+- Add note to the invite page when the logged in user email is not the same as the invitation.
+- Don't accidentally mark unsafe diff lines as HTML safe.
+- Add git diff context to notifications of new notes on merge requests. (Heidi Hoopes)
+- Shows group members in project members list.
+- Gem update: Update grape to 0.18.0. (Robert Schilling)
+- API: Expose merge status for branch API. (Robert Schilling)
+- Displays milestone remaining days only when it's present.
+- API: Expose committer details for commits. (Robert Schilling)
+- API: Ability to set 'should_remove_source_branch' on merge requests. (Robert Schilling)
+- Fix project import label priorities error.
+- Fix Import/Export merge requests error while importing.
+- Refactor Bitbucket importer to use BitBucket API Version 2.
+- Fix Import/Export duplicated builds error.
+- Ci::Builds have same ref as Ci::Pipeline in dev fixtures. (twonegatives)
+- For single line git commit messages, the close quote should be on the same line as the open quote.
+- Use authorized projects in ProjectTeam.
+- Destroy a user's session when they delete their own account.
+- Edit help text to clarify annotated tag creation. (Liz Lam)
+- Fixed file template dropdown for the "New File" editor for smaller/zoomed screens.
+- Fix Route#rename_children behavior.
+- Add nested groups support on data level.
+- Allow projects with 'dashboard' as path.
+- Disabled emoji buttons when user is not logged in.
+- Remove unused and void services from the database.
+- Add issue search slash command.
+- Accept issue new as command to create an issue.
+- Non members cannot create labels through the API.
+- API: expose pipeline coverage.
+- Validate state param when filtering issuables.
+- Username exists check respects relative root path.
+- Bump Git version requirement to 2.8.4.
+- Updates the font weight of button styles because of the change to system fonts.
+- Update API spec files to describe the correct class. (Livier)
+- Fixed timeago re-rendering every timeago.
+- Enable ColorVariable in scss-lint. (Sam Rose)
+- Various small emoji positioning adjustments.
+- Add shortcuts for adding users to a project team with a specific role. (Nikolay Ponomarev and Dino M)
+- Additional rounded label fixes.
+- Remove unnecessary database indices.
+- 24726 Remove Across GitLab from side navigation.
+- Changed cursor icon to pointer when mousing over stages on the Cycle Analytics pages. (Ryan Harris)
+- Add focus state to dropdown items.
+- Fixes Environments displaying incorrect date since 8.14 upgrade.
+- Improve bulk assignment for issuables.
+- Stop supporting Google and Azure as backup strategies.
+- Fix broken README.md UX guide link.
+- Allow public access to some Tag API endpoints.
+- Encode input when migrating ProcessCommitWorker jobs to prevent migration errors.
+- Adjust the width of project avatars to fix alignment within their container. (Ryan Harris)
+- Sentence cased the nav tab headers on the project dashboard page. (Ryan Harris)
+- Adds hoverstates for collapsed Issue/Merge Request sidebar.
+- Make CI badge hitboxes match parent.
+- Add a starting date to milestones.
+- Adjusted margins for Build Status and Coverage Report rows to match those of the CI/CD Pipeline row. (Ryan Harris)
+- Updated members dropdowns.
+- Move all action buttons to project header.
+- Replace issue access checks with use of IssuableFinder.
+- Fix missing Note access checks by moving Note#search to updated NoteFinder.
+- Centered Accept Merge Request button within MR widget and added padding for viewports smaller than 768px. (Ryan Harris)
+- Fix missing access checks on issue lookup using IssuableFinder.
+- Added top margin to Build status page header for mobile views. (Ryan Harris)
+- Fixes "ActionView::Template::Error: undefined method `text?` for nil:NilClass" on MR pages.
+- Issue#visible_to_user moved to IssuesFinder to prevent accidental use.
+- Replace MR access checks with use of MergeRequestsFinder.
+- Fix information disclosure in `Projects::BlobController#update`.
+- Allow branch names with dots on API endpoint.
+- Changed Housekeeping button on project settings page to default styling. (Ryan Harris)
+- Ensure issuable state changes only fire webhooks once.
+- Fix bad selection on dropdown menu for tags filter. (Luis Alonso Chavez Armendariz)
+- Fix title case to sentence case. (Luis Alonso Chavez Armendariz)
+- Fix appearance in error pages. (Luis Alonso Chavez Armendariz)
+- Create mattermost service.
+- 25617 Fix placeholder color of todo filters.
+- Made the padding on the plus button in the breadcrumb menu even. (Ryan Harris)
+- Allow to delete tag release note.
+- Ensure nil User-Agent doesn't break the CI API.
+- Replace Rack::Multipart with GitLab-Workhorse based solution. !5867
+- Add scopes for personal access tokens and OAuth tokens. !5951
+- API: Endpoint to expose personal snippets as /snippets. !6373 (Bernard Guyzmo Pratz)
+- New `gitlab:workhorse:install` rake task. !6574
+- Filter protocol-relative URLs in ExternalLinkFilter. Fixes issue #22742. !6635 (Makoto Scott-Hinkle)
+- Add support for setting the GitLab Runners Registration Token during initial database seeding. !6642
+- Guests can read builds when public. !6842
+- Made comment autocomplete more performant and removed some loading bugs. !6856
+- Add GitLab host to 2FA QR code and manual info. !6941
+- Add sorting functionality for group/project members. !7032
+- Rename Merge When Build Succeeds to Merge When Pipeline Succeeds. !7135
+- Resolve all discussions in a merge request by creating an issue collecting them. !7180 (Bob Van Landuyt)
+- Add Human Readable format for rake backup. !7188 (David Gerő)
+- post_receive: accept any user email from last commit. !7225 (Elan Ruusamäe)
+- Add support for Dockerfile templates. !7247
+- Add shorthand support to gitlab markdown references. !7255 (Oswaldo Ferreira)
+- Display error code for U2F errors. !7305 (winniehell)
+- Fix wrong tab selected when loggin fails and multiple login tabs exists. !7314 (Jacopo Beschi @jacopo-beschi)
+- Clean up common_utils.js. !7318 (winniehell)
+- Show commit status from latest pipeline. !7333
+- Remove the help text under the sidebar subscribe button and style it inline. !7389
+- Update wiki page design. !7429
+- Add nested groups support to the routing. !7459
+- Changed eslint airbnb config to the base airbnb config and corrected eslintrc plugins and envs. !7470 (Luke "Jared" Bennett)
+- Fix cancelling created or external pipelines. !7508
+- Allow admins to stop impersonating users without e-mail addresses. !7550 (Oren Kanner)
+- Remove unnecessary self from user model. !7551 (Semyon Pupkov)
+- Homogenize filter and sort dropdown look'n'feel. !7583 (David Wagner)
+- Create dynamic fixture for build_spec. !7589 (winniehell)
+- Moved Leave Project and Leave Group buttons to access_request_buttons from the settings dropdown. !7600
+- Remove unnecessary require_relative calls from service classes. !7601 (Semyon Pupkov)
+- Simplify copy on "Create a new list" dropdown in Issue Boards. !7605 (Victor Rodrigues)
+- Refactor create service spec. !7609 (Semyon Pupkov)
+- Shows unconfirmed email status in profile. !7611
+- The admin user projects view now has a clickable group link. !7620 (James Gregory)
+- Prevent DOM ID collisions resulting from user-generated content anchors. !7631
+- Replace static fixture for abuse_reports_spec. !7644 (winniehell)
+- Define common helper for describe pagination params in api. !7646 (Semyon Pupkov)
+- Move abuse report spinach test to rspec. !7659 (Semyon Pupkov)
+- Replace static fixture for awards_handler_spec. !7661 (winniehell)
+- API: Add ability to unshare a project from a group. !7662 (Robert Schilling)
+- Replace references to MergeRequestDiff#commits with st_commits when we care only about the number of commits. !7668
+- Add issue events filter and make all really show all events. !7673 (Oxan van Leeuwen)
+- Replace static fixture for notes_spec. !7683 (winniehell)
+- Replace static fixture for shortcuts_issuable_spec. !7685 (winniehell)
+- Replace static fixture for zen_mode_spec. !7686 (winniehell)
+- Replace static fixture for right_sidebar_spec. !7687 (winniehell)
+- Add online terminal support for Kubernetes. !7690
+- Move admin abuse report spinach test to rspec. !7691 (Semyon Pupkov)
+- Move admin spam spinach test to Rspec. !7708 (Semyon Pupkov)
+- Make API::Helpers find a project with only one query. !7714
+- Create builds in transaction to avoid empty pipelines. !7742
+- Render SVG images in diffs and notes. !7747 (andrebsguedes)
+- Add setting to enable/disable HTML emails. !7749
+- Use SmartInterval for MR widget and improve visibilitychange functionality. !7762
+- Resolve "Remove Builds tab from Merge Requests and Commits". !7763
+- Moved new projects button below new group button on the welcome screen. !7770
+- fix display hook error message. !7775 (basyura)
+- Refactor issuable_filters_present to reduce duplications. !7776 (Semyon Pupkov)
+- Redirect to sign-in page when unauthenticated user tries to create a snippet. !7786
+- Fix Archived project merge requests add to group's Merge Requests. !7790 (Jacopo Beschi @jacopo-beschi)
+- Update generic/external build status to match normal build status template. !7811
+- Enable AsciiDoctor admonition icons. !7812 (Horacio Sanson)
+- Do not raise error in AutocompleteController#users when not authorized. !7817 (Semyon Pupkov)
+- fix: 24982- Remove'Signed in successfully' message After this change the sign-in-success flash message will not be shown. !7837 (jnoortheen)
+- Fix Latest deployment link is broken. !7839
+- Don't display prompt to add SSH keys if SSH protocol is disabled. !7840 (Andrew Smith (EspadaV8))
+- Allow unauthenticated access to some Project API GET endpoints. !7843
+- Refactor presenters ChatCommands. !7846
+- Improve help message for issue create slash command. !7850
+- change text around timestamps to make it clear which timestamp is displayed. !7860 (BM5k)
+- Improve Build Log scrolling experience. !7895
+- Change ref property to commitRef in vue commit component. !7901
+- Prevent user creating issue or MR without signing in for a group. !7902
+- Provides a sensible default message when adding a README to a project. !7903
+- Bump ruby version to 2.3.3. !7904
+- Fix comments activity tab visibility condition. !7913 (Rydkin Maxim)
+- Remove unnecessary target branch link from MR page in case of deleted target branch. !7916 (Rydkin Maxim)
+- Add image controls to MR diffs. !7919
+- Remove wrong '.builds-feature' class from the MR settings fieldset. !7930
+- Resolve "Manual actions on pipeline graph". !7931
+- Avoid escaping relative links in Markdown twice. !7940 (winniehell)
+- Move admin hooks spinach to rspec. !7942 (Semyon Pupkov)
+- Move admin logs spinach test to rspec. !7945 (Semyon Pupkov)
+- fix: removed signed_out notification. !7958 (jnoortheen)
+- Accept environment variables from the `pre-receive` script. !7967
+- Do not reload diff for merge request made from fork when target branch in fork is updated. !7973
+- Fixes left align issue for long system notes. !7982
+- Add a slug to environments. !7983
+- Fix lookup of project by unknown ref when caching is enabled. !7988
+- Resolve "Provide SVG as a prop instead of hiding and copy them in environments table". !7992
+- Introduce deployment services, starting with a KubernetesService. !7994
+- Adds tests for custom event polyfill. !7996
+- Allow all alphanumeric characters in file names. !8002 (winniehell)
+- Added support for math rendering, using KaTeX, in Markdown and asciidoc. !8003 (Munken)
+- Remove unnecessary commits order message. !8004
+- API: Memoize the current_user so that sudo can work properly. !8017
+- group authors in contribution graph with case insensitive email handle comparison. !8021
+- Move admin active tab spinach tests to rspec. !8037 (Semyon Pupkov)
+- Add Authentiq as Oauth provider. !8038 (Alexandros Keramidas)
+- API: Ability to cherry pick a commit. !8047 (Robert Schilling)
+- Fix Slack pipeline message from pipelines made by API. !8059
+- API: Simple representation of group's projects. !8060 (Robert Schilling)
+- Prevent overflow with vertical scroll when we have space to show content. !8061
+- Allow to auto-configure Mattermost. !8070
+- Introduce $CI_BUILD_REF_SLUG. !8072
+- Added go back anchor on error pages. !8087
+- Convert CI YAML variables keys into strings. !8088
+- Adds Direct link from pipeline list to builds. !8097
+- Cache last commit id for path. !8098 (Hiroyuki Sato)
+- Pass variables from deployment project services to CI runner. !8107
+- New Gitea importer. !8116
+- Introduce "Set up autodeploy" button to help configure GitLab CI for deployment. !8135
+- Prevent enviroment table to overflow when name has underscores. !8142
+- Fix missing service error importing from EE to CE. !8144
+- Milestoneish SQL performance partially improved and memoized. !8146
+- Allow unauthenticated access to Repositories API GET endpoints. !8148
+- fix colors and margins for adjacent alert banners. !8151
+- Hides new issue button for non loggedin user. !8175
+- Fix N+1 queries on milestone show pages. !8185
+- Rename groups with .git in the end of the path. !8199
+- Whitelist next project names: help, ci, admin, search. !8227
+- Adds back CSS for progress-bars. !8237
+
+## 8.14.10 (2017-02-15)
+
+- No changes.
+
+## 8.14.9 (2017-02-14)
+
+- Patch Asciidocs rendering to block XSS.
+- Fix XSS vulnerability in SVG attachments.
+- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
+- Patch XSS vulnerability in RDOC support.
+
+## 8.14.8 (2017-01-25)
+
+- Accept environment variables from the `pre-receive` script. !7967
+- Milestoneish SQL performance partially improved and memoized. !8146
+- Fix N+1 queries on milestone show pages. !8185
+- Speed up group milestone index by passing group_id to IssuesFinder. !8363
+- Ensure issuable state changes only fire webhooks once.
+
+## 8.14.7 (2017-01-21)
+
+- Ensure export files are removed after a namespace is deleted.
+- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
+- Prevent users from creating notes on resources they can't access.
+- Prevent users from deleting system deploy keys via the project deploy key API.
+- Upgrade omniauth gem to 1.3.2.
+
+## 8.14.6 (2017-01-10)
+
+- Update the gitlab-markup gem to the version 1.5.1. !8509
+- Updated Turbolinks to mitigate potential XSS attacks.
+
+## 8.14.5 (2016-12-14)
+
+- Moved Leave Project and Leave Group buttons to access_request_buttons from the settings dropdown. !7600
+- fix display hook error message. !7775 (basyura)
+- Remove wrong '.builds-feature' class from the MR settings fieldset. !7930
+- Avoid escaping relative links in Markdown twice. !7940 (winniehell)
+- API: Memoize the current_user so that sudo can work properly. !8017
+- Displays milestone remaining days only when it's present.
+- Allow branch names with dots on API endpoint.
+- Issue#visible_to_user moved to IssuesFinder to prevent accidental use.
+- Shows group members in project members list.
+- Encode input when migrating ProcessCommitWorker jobs to prevent migration errors.
+- Fixed timeago re-rendering every timeago.
+- Fix missing Note access checks by moving Note#search to updated NoteFinder.
+
+## 8.14.4 (2016-12-08)
+
+- Fix diff view permalink highlighting. !7090
+- Fix pipeline author for Slack and use pipeline id for pipeline link. !7506
+- Fix compatibility with Internet Explorer 11 for merge requests. !7525 (Steffen Rauh)
+- Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615
+- Fix Cicking on tabs on pipeline page should set URL. !7709
+- Authorize users into imported GitLab project.
+- Destroy a user's session when they delete their own account.
+- Don't accidentally mark unsafe diff lines as HTML safe.
+- Replace MR access checks with use of MergeRequestsFinder.
+- Remove visible content caching.
+
+## 8.14.3 (2016-12-02)
+
+- Pass commit data to ProcessCommitWorker to reduce Git overhead. !7744
+- Speed up issuable dashboards.
+- Don't change relative URLs to absolute URLs in the Help page.
+- Fixes "ActionView::Template::Error: undefined method `text?` for nil:NilClass" on MR pages.
+- Fix branch validation for GitHub PR where repo/fork was renamed/deleted.
+- Validate state param when filtering issuables.
+
+## 8.14.2 (2016-12-01)
+
+- Remove caching of events data. !6578
+- Rephrase some system notes to be compatible with new system note style. !7692
+- Pass tag SHA to post-receive hook when tag is created via UI. !7700
+- Prevent error when submitting a merge request and pipeline is not defined. !7707
+- Fixes system note style in commit discussion. !7721
+- Use a Redis lease for updating authorized projects. !7733
+- Refactor JiraService by moving code out of JiraService#execute method. !7756
+- Update GitLab Workhorse to v1.0.1. !7759
+- Fix pipelines info being hidden in merge request widget. !7808
+- Fixed commit timeago not rendering after initial page.
+- Fix for error thrown in cycle analytics events if build has not started.
+- Fixed issue boards issue sorting when dragging issue into list.
+- Allow access to the wiki with git when repository feature disabled.
+- Fixed timeago not rendering when resolving a discussion.
+- Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1.
+- Timeout creating and viewing merge request for binary file.
+- Gracefully recover from Redis connection failures in Sidekiq initializer.
+
+## 8.14.1 (2016-11-28)
+
+- Fix deselecting calendar days on contribution graph. !6453 (ClemMakesApps)
+- Update grape entity to 0.6.0. !7491
+- If Build running change accept merge request when build succeeds button from orange to blue. !7577
+- Changed import sources buttons to checkboxes. !7598 (Luke "Jared" Bennett)
+- Last minute CI Style tweaks for 8.14. !7643
+- Fix exceptions when loading build trace. !7658
+- Fix wrong template rendered when CI/CD settings aren't update successfully. !7665
+- fixes last_deployment call environment is nil. !7671
+- Sort builds by name within pipeline graph. !7681
+- Correctly determine mergeability of MR with no discussions.
+- Sidekiq stats in the admin area will now show correctly on different platforms. (blackst0ne)
+- Fixed issue boards dragging card removing random issues.
+- Fix information disclosure in `Projects::BlobController#update`.
+- Fix missing access checks on issue lookup using IssuableFinder.
+- Replace issue access checks with use of IssuableFinder.
+- Non members cannot create labels through the API.
+- Fix cycle analytics plan stage when commits are missing.
+
+## 8.14.0 (2016-11-22)
+
+- Use separate email-token for incoming email and revert back the inactive feature. !5914
+- API: allow recursive tree request. !6088 (Rebeca Mendez)
+- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
+- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
+- Add button to delete all merged branches. !6449 (Toon Claes)
+- Finer-grained Git gargage collection. !6588
+- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
+- Centralize LDAP config/filter logic. !6606
+- Make system notes less intrusive. !6755
+- Process commits using a dedicated Sidekiq worker. !6802
+- Show random messages when the To Do list is empty. !6818 (Josep Llaneras)
+- Precalculate user's authorized projects in database. !6839
+- Fix record not found error on NewNoteWorker processing. !6863 (Oswaldo Ferreira)
+- Show avatars in mention dropdown. !6865
+- Fix expanding a collapsed diff when converting a symlink to a regular file. !6953
+- Defer saving project services to the database if there are no user changes. !6958
+- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
+- Display "folders" for environments. !7015
+- Make it possible to trigger builds from webhooks. !7022 (Dmitry Poray)
+- Fix showing pipeline status for a given commit from correct branch. !7034
+- Add link to build pipeline within individual build pages. !7082
+- Add api endpoint `/groups/owned`. !7103 (Borja Aparicio)
+- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
+- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
+- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
+- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
+- Fix trace patching feature - update the updated_at value. !7146
+- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
+- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
+- Add api endpoint for creating a pipeline. !7209 (Ido Leibovich)
+- Allow users to subscribe to group labels. !7215
+- Reduce API calls needed when importing issues and pull requests from GitHub. !7241 (Andrew Smith (EspadaV8))
+- Only skip group when it's actually a group in the "Share with group" select. !7262
+- Introduce round-robin project creation to spread load over multiple shards. !7266
+- Ensure merge request's "remove branch" accessors return booleans. !7267
+- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
+- Expose label IDs in API. !7275 (Rares Sfirlogea)
+- Fix invalid filename validation on eslint. !7281
+- API: Ability to retrieve version information. !7286 (Robert Schilling)
+- Added ability to throttle Sidekiq Jobs. !7292
+- Set default Sidekiq retries to 3. !7294
+- Fix double event and ajax request call on MR page. !7298 (YarNayar)
+- Unify anchor link format for MR diff files. !7298 (YarNayar)
+- Require projects before creating milestone. !7301 (gfyoung)
+- Fix error when using invalid branch name when creating a new pipeline. !7324
+- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
+- Auto-close environment when branch is deleted. !7355
+- Rework cache invalidation so only changed data is refreshed. !7360
+- Navigation bar issuables counters reflects dashboard issuables counters. !7368 (Lucas Deschamps)
+- Fix cache for commit status in commits list to respect branches. !7372
+- fixes 500 error on project show when user is not logged in and project is still empty. !7376
+- Removed gray button styling from todo buttons in sidebars. !7387
+- Fix project records with invalid visibility_level values. !7391
+- Use 'Forking in progress' title when appropriate. !7394 (Philip Karpiak)
+- Fix error links in help index page. !7396 (Fu Xu)
+- Add support for reply-by-email when the email only contains HTML. !7397
+- [Fix] Extra divider issue in dropdown. !7398
+- Project download buttons always show. !7405 (Philip Karpiak)
+- Give search-input correct padding-right value. !7407 (Philip Karpiak)
+- Remove additional padding on right-aligned items in MR widget. !7411 (Didem Acet)
+- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
+- Allow mail_room idle_timeout option to be configurable. !7423
+- Fix misaligned buttons on admin builds page. !7424 (Didem Acet)
+- Disable "Request Access" functionality by default for new projects and groups. !7425
+- fix shibboleth misconfigurations resulting in authentication bypass. !7428
+- Added Mattermost slash command. !7438
+- Allow to connect Chat account with GitLab. !7450
+- Make New Group form respect default visibility application setting. !7454 (Jacopo Beschi @jacopo-beschi)
+- Fix Error 500 when creating a merge request that contains an image that was deleted and added. !7457
+- Fix labels API by adding missing current_user parameter. !7458 (Francesco Coda Zabetta)
+- Changed restricted visibility admin buttons to checkboxes. !7463
+- Send credentials (currently for registry only) with build data to GitLab Runner. !7474
+- Fix POST /internal/allowed to cope with gitlab-shell v4.0.0 project paths. !7480
+- Adds es6-promise Polyfill. !7482
+- Added colored labels to related MR list. !7486 (Didem Acet)
+- Use setter for key instead AR callback. !7488 (Semyon Pupkov)
+- Limit labels returned for a specific project as an administrator. !7496
+- Change slack notification comment link. !7498 (Herbert Kagumba)
+- Allow registering users whose username contains dots. !7500 (Timothy Andrew)
+- Fix race condition during group deletion and remove stale records present due to this bug. !7528 (Timothy Andrew)
+- Check all namespaces on validation of new username. !7537
+- Pass correct tag target to post-receive hook when creating tag via UI. !7556
+- Add help message for configuring Mattermost slash commands. !7558
+- Fix typo in Build page JavaScript. !7563 (winniehell)
+- Make job script a required configuration entry. !7566
+- Fix errors happening when source branch of merge request is removed and then restored. !7568
+- Fix a wrong "The build for this merge request failed" message. !7579
+- Fix Margins look weird in Project page with pinned sidebar in project stats bar. !7580
+- Fix regression causing bad error message to appear on Merge Request form. !7599 (Alex Sanford)
+- Fix activity page endless scroll on large viewports. !7608
+- Fix 404 on some group pages when name contains dot. !7614
+- Do not create a new TODO when failed build is allowed to fail. !7618
+- Add deployment command to ChatOps. !7619
+- Fix 500 error when group name ends with git. !7630
+- Fix undefined error in CI linter. !7650
+- Show events per stage on Cycle Analytics page. !23449
+- Add JIRA remotelinks and prevent duplicated closing messages.
+- Fixed issue boards counter border when unauthorized.
+- Add placeholder for the example text for custom hex color on label creation popup. (Luis Alonso Chavez Armendariz)
+- Add an index for project_id in project_import_data to improve performance.
+- Fix broken commits search.
+- Assignee dropdown now searches author of issue or merge request.
+- Clicking "force remove source branch" label now toggles the checkbox again.
+- More aggressively preload on merge request and issue index pages.
+- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
+- Fixing the issue of the project fork url giving 500 when not signed instead of being redirected to sign in page. (Cagdas Gerede)
+- Fix: Guest sees some repository details and gets 404.
+- Add logging for rack attack events to production.log.
+- Add environment info to builds page.
+- Allow commit note to be visible if repo is visible.
+- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
+- Redesign pipelines page.
+- Faster search inside Project.
+- Search for a filename in a project.
+- Allow sorting groups in the API.
+- Fix: Todos Filter Shows All Users.
+- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
+- Fixed multiple requests sent when opening dropdowns.
+- Added permissions per stage to cycle analytics endpoint.
+- Fix project Visibility Level selector not using default values.
+- Add events per stage to cycle analytics.
+- Allow to test JIRA service settings without having a repository.
+- Fix JIRA references for project snippets.
+- Allow enabling and disabling commit and MR events for JIRA.
+- simplify url generation. (Jarka Kadlecova)
+- Show correct environment log in admin/logs (@duk3luk3 !7191)
+- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
+- Diff collapse won't shift when collapsing.
+- Backups do not fail anymore when using tar on annex and custom_hooks only. !5814
+- Adds user project membership expired event to clarify why user was removed (Callum Dryden)
+- Trim leading and trailing whitespace on project_path (Linus Thiel)
+- Prevent award emoji via notes for issues/MRs authored by user (barthc)
+- Adds support for the `token` attribute in project hooks API (Gauvain Pocentek)
+- Change auto selection behaviour of emoji and slash commands to be more UX/Type friendly (Yann Gravrand)
+- Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
+- Fix Markdown styling inside reference links (Jan Zdráhal)
+- Create new issue board list after creating a new label
+- Fix extra space on Build sidebar on Firefox !7060
+- Fail gracefully when creating merge request with non-existing branch (alexsanford)
+- Fix mobile layout issues in admin user overview page !7087
+- Fix HipChat notifications rendering (airatshigapov, eisnerd)
+- Removed unneeded "Builds" and "Environments" link from project titles
+- Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato)
+- Cleaned up global namespace JS !19661 (Jose Ivan Vargas)
+- Refactor Jira service to use jira-ruby gem
+- Improved todos empty state
+- Add hover to trash icon in notes !7008 (blackst0ne)
+- Hides project activity tabs when features are disabled
+- Only show one error message for an invalid email !5905 (lycoperdon)
+- Added guide describing how to upgrade PostgreSQL using Slony
+- Fix sidekiq stats in admin area (blackst0ne)
+- Added label description as tooltip to issue board list title
+- Created cycle analytics bundle JavaScript file
+- Make the milestone page more responsive (yury-n)
+- Hides container registry when repository is disabled
+- API: Fix booleans not recognized as such when using the `to_boolean` helper
+- Removed delete branch tooltip !6954
+- Stop unauthorized users dragging on milestone page (blackst0ne)
+- Restore issue boards welcome message when a project is created !6899
+- Check that JavaScript file names match convention !7238 (winniehell)
+- Do not show tooltip for active element !7105 (winniehell)
+- Escape ref and path for relative links !6050 (winniehell)
+- Fixed link typo on /help/ui to Alerts section. !6915 (Sam Rose)
+- Fix broken issue/merge request links in JIRA comments. !6143 (Brian Kintz)
+- Fix filtering of milestones with quotes in title (airatshigapov)
+- Fix issue boards dragging bug in Safari
+- Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
+- Update mail_room and enable sentinel support to Reply By Email (!7101)
+- Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
+- Simpler arguments passed to named_route on toggle_award_url helper method
+- Fix typo in framework css class. !7086 (Daniel Voogsgerd)
+- New issue board list dropdown stays open after adding a new list
+- Fix: Backup restore doesn't clear cache
+- Optimize Event queries by removing default order
+- Add new icon for skipped builds
+- Show created icon in pipeline mini-graph
+- Remove duplicate links from sidebar
+- API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh)
+- Add Rake task to create/repair GitLab Shell hooks symlinks !5634
+- Add job for removal of unreferenced LFS objects from both the database and the filesystem (Frank Groeneveld)
+- Replace jquery.cookie plugin with js.cookie !7085
+- Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
+- Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens
+- Show full status link on MR & commit pipelines
+- Fix documents and comments on Build API `scope`
+- Initialize Sidekiq with the list of queues used by GitLab
+- Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
+- Shortened merge request modal to let clipboard button not overlap
+- Adds JavaScript validation for group path editing field
+- In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo)
+- Improve search query parameter naming in /admin/users !7115 (YarNayar)
+- Fix table pagination to be responsive
+- Fix applying GitHub-imported labels when importing job is interrupted
+- Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar)
+- Updated commit SHA styling on the branches page.
+- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
+- Fix 404 when visit /projects page
+
+## 8.13.12 (2017-01-21)
+
+- Ensure export files are removed after a namespace is deleted.
+- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
+- Prevent users from creating notes on resources they can't access.
+- Prevent users from deleting system deploy keys via the project deploy key API.
+- Upgrade omniauth gem to 1.3.2.
+
+## 8.13.11 (2017-01-10)
+
+- Update the gitlab-markup gem to the version 1.5.1. !8509
+- Updated Turbolinks to mitigate potential XSS attacks.
+
+## 8.13.10 (2016-12-14)
+
+- API: Memoize the current_user so that sudo can work properly. !8017
+- Filter `authentication_token`, `incoming_email_token` and `runners_token` parameters.
+- Issue#visible_to_user moved to IssuesFinder to prevent accidental use.
+- Fix missing Note access checks by moving Note#search to updated NoteFinder.
+
+## 8.13.9 (2016-12-08)
+
+- Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615
+- Replace MR access checks with use of MergeRequestsFinder.
+
+## 8.13.8 (2016-12-02)
+
+- Pass tag SHA to post-receive hook when tag is created via UI. !7700
+- Validate state param when filtering issuables.
+
+## 8.13.7 (2016-11-28)
+
+- fixes 500 error on project show when user is not logged in and project is still empty. !7376
+- Update grape entity to 0.6.0. !7491
+- Fix information disclosure in `Projects::BlobController#update`.
+- Fix missing access checks on issue lookup using IssuableFinder.
+- Replace issue access checks with use of IssuableFinder.
+- Non members cannot create labels through the API.
+
+## 8.13.6 (2016-11-17)
+
+- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
+- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option. !7117
+- Fix relative links in Markdown wiki when displayed in "Project" tab. !7218
+- Fix no "Register" tab if ldap auth is enabled (#24038). !7274 (Luc Didry)
+- Fix cache for commit status in commits list to respect branches. !7372
+- Fix issue causing Labels not to appear in sidebar on MR page. !7416 (Alex Sanford)
+- Limit labels returned for a specific project as an administrator. !7496
+- Clicking "force remove source branch" label now toggles the checkbox again.
+- Allow commit note to be visible if repo is visible.
+- Fix project Visibility Level selector not using default values.
+
+## 8.13.5 (2016-11-08)
+
+- Restore unauthenticated access to public container registries
+- Fix showing pipeline status for a given commit from correct branch. !7034
+- Only skip group when it's actually a group in the "Share with group" select. !7262
+- Introduce round-robin project creation to spread load over multiple shards. !7266
+- Ensure merge request's "remove branch" accessors return booleans. !7267
+- Ensure external users are not able to clone disabled repositories.
+- Fix XSS issue in Markdown autolinker.
+- Respect event visibility in Gitlab::ContributionsCalendar.
+- Honour issue and merge request visibility in their respective finders.
+- Disable reference Markdown for unavailable features.
+- Fix lightweight tags not processed correctly by GitTagPushService. !6532
+- Allow owners to fetch source code in CI builds. !6943
+- Return conflict error in label API when title is taken by group label. !7014
+- Reduce the overhead to calculate number of open/closed issues and merge requests within the group or project. !7123
+- Fix builds tab visibility. !7178
+- Fix project features default values. !7181
+
+## 8.13.4
+
+- Pulled due to packaging error.
+
+## 8.13.3 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+- Fixed Import/Export foreign key issue to do with project members.
+- Changed build dropdown list length to be 6,5 builds long in the pipeline graph
+
+## 8.13.2 (2016-10-31)
+
+- Fix encoding issues on pipeline commits. !6832
+- Use Hash rocket syntax to fix cycle analytics under Ruby 2.1. !6977
+- Modify GitHub importer to be retryable. !7003
+- Fix refs dropdown selection with special characters. !7061
+- Fix horizontal padding for highlight blocks. !7062
+- Pass user instance to `Labels::FindOrCreateService` or `skip_authorization: true`. !7093
+- Fix builds dropdown overlapping bug. !7124
+- Fix applying labels for GitHub-imported MRs. !7139
+- Fix importing MR comments from GitHub. !7139
+- Fix project member access for group links. !7144
+- API: Fix booleans not recognized as such when using the `to_boolean` helper. !7149
+- Fix and improve `Sortable.highest_label_priority`. !7165
+- Fixed sticky merge request tabs when sidebar is pinned. !7167
+- Only remove right connector of first build of last stage. !7179
+
+## 8.13.1 (2016-10-25)
+
+- Fix branch protection API. !6215
+- Fix hidden pipeline graph on commit and MR page. !6895
+- Fix Cycle analytics not showing correct data when filtering by date. !6906
+- Ensure custom provider tab labels don't break layout. !6993
+- Fix issue boards user link when in subdirectory. !7018
+- Refactor and add new environment functionality to CI yaml reference. !7026
+- Fix typo in project settings that prevents users from enabling container registry. !7037
+- Fix events order in `users/:id/events` endpoint. !7039
+- Remove extra line for empty issue description. !7045
+- Don't append issue/MR templates to any existing text. !7050
+- Fix error in generating labels. !7055
+- Stop clearing the database cache on `rake cache:clear`. !7056
+- Only show register tab if signup enabled. !7058
+- Fix lightweight tags not processed correctly by GitTagPushService
+- Expire and build repository cache after project import. !7064
+- Fix bug where labels would be assigned to issues that were moved. !7065
+- Fix reply-by-email not working due to queue name mismatch. !7068
+- Fix 404 for group pages when GitLab setup uses relative url. !7071
+- Fix `User#to_reference`. !7088
+- Reduce overhead of `LabelFinder` by avoiding `#presence` call. !7094
+- Fix unauthorized users dragging on issue boards. !7096
+- Only schedule `ProjectCacheWorker` jobs when needed. !7099
+
+## 8.13.0 (2016-10-22)
+
+- Fix save button on project pipeline settings page. (!6955)
+- All Sidekiq workers now use their own queue
+- Avoid race condition when asynchronously removing expired artifacts. (!6881)
+- Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
+- Respond with 404 Not Found for non-existent tags (Linus Thiel)
+- Truncate long labels with ellipsis in labels page
+- Improve tabbing usability for sign in page (ClemMakesApps)
+- Enforce TrailingSemicolon and EmptyLineBetweenBlocks in scss-lint
+- Adding members no longer silently fails when there is extra whitespace
+- Update runner version only when updating contacted_at
+- Add link from system note to compare with previous version
+- Use gitlab-shell v3.6.6
+- Ignore references to internal issues when using external issues tracker
+- Ability to resolve merge request conflicts with editor !6374
+- Add `/projects/visible` API endpoint (Ben Boeckel)
+- Fix centering of custom header logos (Ashley Dumaine)
+- Keep around commits only pipeline creation as pipeline data doesn't change over time
+- Update duration at the end of pipeline
+- ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup
+- Add group level labels. (!6425)
+- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
+- Cancelled pipelines could be retried. !6927
+- Updating verbiage on git basics to be more intuitive
+- Fix project_feature record not generated on project creation
+- Clarify documentation for Runners API (Gennady Trafimenkov)
+- Use optimistic locking for pipelines and builds
+- The instrumentation for Banzai::Renderer has been restored
+- Change user & group landing page routing from /u/:username to /:username
+- Added documentation for .gitattributes files
+- Move Pipeline Metrics to separate worker
+- AbstractReferenceFilter caches project_refs on RequestStore when active
+- Replaced the check sign to arrow in the show build view. !6501
+- Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar)
+- ProjectCacheWorker updates caches at most once per 15 minutes per project
+- Fix Error 500 when viewing old merge requests with bad diff data
+- Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar)
+- Fix viewing merged MRs when the source project has been removed !6991
+- Speed-up group milestones show page
+- Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps)
+- Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService
+- Fix discussion thread from emails for merge requests. !7010
+- Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs)
+- Add tag shortcut from the Commit page. !6543
+- Keep refs for each deployment
+- Close open tooltips on page navigation (Linus Thiel)
+- Allow browsing branches that end with '.atom'
+- Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller)
+- Replace unique keyframes mixin with keyframe mixin with specific names (ClemMakesApps)
+- Add more tests for calendar contribution (ClemMakesApps)
+- Update Gitlab Shell to fix some problems with moving projects between storages
+- Cache rendered markdown in the database, rather than Redis
+- Add todo toggle event (ClemMakesApps)
+- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
+- Simplify Mentionable concern instance methods
+- API: Ability to retrieve version information (Robert Schilling)
+- Fix permission for setting an issue's due date
+- API: Multi-file commit !6096 (mahcsig)
+- Unicode emoji are now converted to images
+- Revert "Label list shows all issues (opened or closed) with that label"
+- Expose expires_at field when sharing project on API
+- Fix VueJS template tags being rendered in code comments
+- Added copy file path button to merge request diff files
+- Fix issue with page scrolling to top when closing or pinning sidebar (lukehowell)
+- Add Issue Board API support (andrebsguedes)
+- Allow the Koding integration to be configured through the API
+- Add new issue button to each list on Issues Board
+- Execute specific named route method from toggle_award_url helper method
+- Added soft wrap button to repository file/blob editor
+- Update namespace validation to forbid reserved names (.git and .atom) (Will Starms)
+- Show the time ago a merge request was deployed to an environment
+- Add RTL support to markdown renderer (Ebrahim Byagowi)
+- Add word-wrap to issue title on issue and milestone boards (ClemMakesApps)
+- Fix todos page mobile viewport layout (ClemMakesApps)
+- Make issues search less finicky
+- Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps)
+- Remove redundant mixins (ClemMakesApps)
+- Added 'Download' button to the Snippets page (Justin DiPierro)
+- Add visibility level to project repository
+- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
+- Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska)
+- Fix showing commits from source project for merge request !6658
+- Fix that manual jobs would no longer block jobs in the next stage. !6604
+- Add configurable email subject suffix (Fu Xu)
+- Use defined colour for a language when available !6748 (nilsding)
+- Added tooltip to fork count on project show page. (Justin DiPierro)
+- Use a ConnectionPool for Rails.cache on Sidekiq servers
+- Replace `alias_method_chain` with `Module#prepend`
+- Enable GitLab Import/Export for non-admin users.
+- Preserve label filters when sorting !6136 (Joseph Frazier)
+- MergeRequest#new form load diff asynchronously
+- Only update issuable labels if they have been changed
+- Take filters in account in issuable counters. !6496
+- Use custom Ruby images to test builds (registry.dev.gitlab.org/gitlab/gitlab-build-images:*)
+- Replace static issue fixtures by script !6059 (winniehell)
+- Append issue template to existing description !6149 (Joseph Frazier)
+- Trending projects now only show public projects and the list of projects is cached for a day
+- Memoize Gitlab Shell's secret token (!6599, Justin DiPierro)
+- Revoke button in Applications Settings underlines on hover.
+- Use higher size on Gitlab::Redis connection pool on Sidekiq servers
+- Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska)
+- Revert avoid touching file system on Build#artifacts?
+- Stop using a Redis lease when updating the project activity timestamp whenever a new event is created
+- Add disabled delete button to protected branches (ClemMakesApps)
+- Add broadcast messages and alerts below sub-nav
+- Better empty state for Groups view
+- API: New /users/:id/events endpoint
+- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
+- Replace bootstrap caret with fontawesome caret (ClemMakesApps)
+- Fix unnecessary escaping of reserved HTML characters in milestone title. !6533
+- Add organization field to user profile
+- Change user pages routing from /u/:username/PATH to /users/:username/PATH. Old routes will redirect to the new ones for the time being.
+- Fix enter key when navigating search site search dropdown. !6643 (Brennan Roberts)
+- Fix deploy status responsiveness error !6633
+- Make searching for commits case insensitive
+- Fix resolved discussion display in side-by-side diff view !6575
+- Optimize GitHub importing for speed and memory
+- API: expose pipeline data in builds API (!6502, Guilherme Salazar)
+- Notify the Merger about merge after successful build (Dimitris Karakasilis)
+- Reduce queries needed to find users using their SSH keys when pushing commits
+- Prevent rendering the link to all when the author has no access (Katarzyna Kobierska Ula Budziszewska)
+- Fix broken repository 500 errors in project list
+- Fix the diff in the merge request view when converting a symlink to a regular file
+- Fix Pipeline list commit column width should be adjusted
+- Close todos when accepting merge requests via the API !6486 (tonygambone)
+- Ability to batch assign issues relating to a merge request to the author. !5725 (jamedjo)
+- Changed Slack service user referencing from full name to username (Sebastian Poxhofer)
+- Retouch environments list and deployments list
+- Add multiple command support for all label related slash commands !6780 (barthc)
+- Add Container Registry on/off status to Admin Area !6638 (the-undefined)
+- Add Nofollow for uppercased scheme in external urls !6820 (the-undefined)
+- Allow empty merge requests !6384 (Artem Sidorenko)
+- Grouped pipeline dropdown is a scrollable container
+- Cleanup Ci::ApplicationController. !6757 (Takuya Noguchi)
+- Fixes padding in all clipboard icons that have .btn class
+- Fix a typo in doc/api/labels.md
+- Fix double-escaping in activities tab (Alexandre Maia)
+- API: all unknown routing will be handled with 404 Not Found
+- Add docs for request profiling
+- Delete dynamic environments
+- Fix buggy iOS tooltip layering behavior.
+- Make guests unable to view MRs on private projects
+- Fix broken Project API docs (Takuya Noguchi)
+- Migrate invalid project members (owner -> master)
+
+## 8.12.12 (2016-12-08)
+
+- Replace MR access checks with use of MergeRequestsFinder
+- Reenables /user API request to return private-token if user is admin and request is made with sudo
+
+## 8.12.11 (2016-12-02)
+
+- No changes
+
+## 8.12.10 (2016-11-28)
+
+- Fix information disclosure in `Projects::BlobController#update`
+- Fix missing access checks on issue lookup using IssuableFinder
+- Replace issue access checks with use of IssuableFinder
+
+## 8.12.9 (2016-11-07)
+
+- Fix XSS issue in Markdown autolinker
+
+## 8.12.8 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+- Fixed Import/Export foreign key issue to do with project members.
+
+## 8.12.7
+
+ - Prevent running `GfmAutocomplete` setup for each diff note. !6569
+ - Fix long commit messages overflow viewport in file tree. !6573
+ - Use `gitlab-markup` gem instead of `github-markup` to fix `.rst` file rendering. !6659
+ - Prevent flash alert text from being obscured when container is fluid. !6694
+ - Fix due date being displayed as `NaN` in Safari. !6797
+ - Fix JS bug with select2 because of missing `data-field` attribute in select box. !6812
+ - Do not alter `force_remove_source_branch` options on MergeRequest unless specified. !6817
+ - Fix GFM autocomplete setup being called several times. !6840
+ - Handle case where deployment ref no longer exists. !6855
+
+## 8.12.6
+
+ - Update mailroom to 0.8.1 in Gemfile.lock !6814
+
+## 8.12.5
+
+ - Switch from request to env in ::API::Helpers. !6615
+ - Update the mail_room gem to 0.8.1 to fix a race condition with the mailbox watching thread. !6714
+ - Improve issue load time performance by avoiding ORDER BY in find_by call. !6724
+ - Add a new gitlab:users:clear_all_authentication_tokens task. !6745
+ - Don't send Private-Token (API authentication) headers to Sentry
+ - Share projects via the API only with groups the authenticated user can access
+
+## 8.12.4
+
+ - Fix "Copy to clipboard" tooltip to say "Copied!" when clipboard button is clicked. !6294 (lukehowell)
+ - Fix padding in build sidebar. !6506
+ - Changed compare dropdowns to dropdowns with isolated search input. !6550
+ - Fix race condition on LFS Token. !6592
+ - Fix type mismatch bug when closing Jira issue. !6619
+ - Fix lint-doc error. !6623
+ - Skip wiki creation when GitHub project has wiki enabled. !6665
+ - Fix issues importing services via Import/Export. !6667
+ - Restrict failed login attempts for users with 2FA enabled. !6668
+ - Fix failed project deletion when feature visibility set to private. !6688
+ - Prevent claiming associated model IDs via import.
+ - Set GitLab project exported file permissions to owner only
+ - Improve the way merge request versions are compared with each other
+
+## 8.12.3
+
+ - Update Gitlab Shell to support low IO priority for storage moves
+
+## 8.12.2
+
+ - Fix Import/Export not recognising correctly the imported services.
+ - Fix snippets pagination
+ - Fix "Create project" button layout when visibility options are restricted
+ - Fix List-Unsubscribe header in emails
+ - Fix IssuesController#show degradation including project on loaded notes
+ - Fix an issue with the "Commits" section of the cycle analytics summary. !6513
+ - Fix errors importing project feature and milestone models using GitLab project import
+ - Make JWT messages Docker-compatible
+ - Fix duplicate branch entry in the merge request version compare dropdown
+ - Respect the fork_project permission when forking projects
+ - Only update issuable labels if they have been changed
+ - Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv)
+ - Fix resolve discussion buttons endpoint path
+ - Refactor remnants of CoffeeScript destructured opts and super !6261
+
+## 8.12.1
+
+ - Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
+ - Fix issue with search filter labels not displaying
+
+## 8.12.0 (2016-09-22)
+
+ - Removes inconsistency regarding tagging immediatelly as merged once you create a new branch. !6408
+ - Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
+ - Only check :can_resolve permission if the note is resolvable
+ - Bump fog-aws to v0.11.0 to support ap-south-1 region
+ - Add ability to fork to a specific namespace using API. (ritave)
+ - Allow to set request_access_enabled for groups and projects
+ - Cleanup misalignments in Issue list view !6206
+ - Only create a protected branch upon a push to a new branch if a rule for that branch doesn't exist
+ - Add Pipelines for Commit
+ - Prune events older than 12 months. (ritave)
+ - Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
+ - Fix issues/merge-request templates dropdown for forked projects
+ - Filter tags by name !6121
+ - Update gitlab shell secret file also when it is empty. !3774 (glensc)
+ - Give project selection dropdowns responsive width, make non-wrapping.
+ - Fix note form hint showing slash commands supported for commits.
+ - Make push events have equal vertical spacing.
+ - API: Ensure invitees are not returned in Members API.
+ - Preserve applied filters on issues search.
+ - Add two-factor recovery endpoint to internal API !5510
+ - Pass the "Remember me" value to the U2F authentication form
+ - Display stages in valid order in stages dropdown on build page
+ - Only update projects.last_activity_at once per hour when creating a new event
+ - Cycle analytics (first iteration) !5986
+ - Remove vendor prefixes for linear-gradient CSS (ClemMakesApps)
+ - Move pushes_since_gc from the database to Redis
+ - Limit number of shown environments on Merge Request: show only environments for target_branch, source_branch and tags
+ - Add font color contrast to external label in admin area (ClemMakesApps)
+ - Fix find file navigation links (ClemMakesApps)
+ - Change logo animation to CSS (ClemMakesApps)
+ - Instructions for enabling Git packfile bitmaps !6104
+ - Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint
+ - Fix long comments in diffs messing with table width
+ - Add spec covering 'Gitlab::Git::committer_hash' !6433 (dandunckelman)
+ - Fix pagination on user snippets page
+ - Honor "fixed layout" preference in more places !6422
+ - Run CI builds with the permissions of users !5735
+ - Fix sorting of issues in API
+ - Fix download artifacts button links !6407
+ - Sort project variables by key. !6275 (Diego Souza)
+ - Ensure specs on sorting of issues in API are deterministic on MySQL
+ - Added ability to use predefined CI variables for environment name
+ - Added ability to specify URL in environment configuration in gitlab-ci.yml
+ - Escape search term before passing it to Regexp.new !6241 (winniehell)
+ - Fix pinned sidebar behavior in smaller viewports !6169
+ - Fix file permissions change when updating a file on the Gitlab UI !5979
+ - Added horizontal padding on build page sidebar on code coverage block. !6196 (Vitaly Baev)
+ - Change merge_error column from string to text type
+ - Fix issue with search filter labels not displaying
+ - Reduce contributions calendar data payload (ClemMakesApps)
+ - Show all pipelines for merge requests even from discarded commits !6414
+ - Replace contributions calendar timezone payload with dates (ClemMakesApps)
+ - Changed MR widget build status to pipeline status !6335
+ - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
+ - Enable pipeline events by default !6278
+ - Add pipeline email service !6019
+ - Move parsing of sidekiq ps into helper !6245 (pascalbetz)
+ - Added go to issue boards keyboard shortcut
+ - Expose `sha` and `merge_commit_sha` in merge request API (Ben Boeckel)
+ - Emoji can be awarded on Snippets !4456
+ - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
+ - Fix blame table layout width
+ - Spec testing if issue authors can read issues on private projects
+ - Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps)
+ - Request only the LDAP attributes we need !6187
+ - Center build stage columns in pipeline overview (ClemMakesApps)
+ - Fix bug with tooltip not hiding on discussion toggle button
+ - Rename behaviour to behavior in bug issue template for consistency (ClemMakesApps)
+ - Fix bug stopping issue description being scrollable after selecting issue template
+ - Remove suggested colors hover underline (ClemMakesApps)
+ - Fix jump to discussion button being displayed on commit notes
+ - Shorten task status phrase (ClemMakesApps)
+ - Fix project visibility level fields on settings
+ - Add hover color to emoji icon (ClemMakesApps)
+ - Increase ci_builds artifacts_size column to 8-byte integer to allow larger files
+ - Add textarea autoresize after comment (ClemMakesApps)
+ - Do not write SSH public key 'comments' to authorized_keys !6381
+ - Add due date to issue todos
+ - Refresh todos count cache when an Issue/MR is deleted
+ - Fix branches page dropdown sort alignment (ClemMakesApps)
+ - Hides merge request button on branches page is user doesn't have permissions
+ - Add white background for no readme container (ClemMakesApps)
+ - API: Expose issue confidentiality flag. (Robert Schilling)
+ - Fix markdown anchor icon interaction (ClemMakesApps)
+ - Test migration paths from 8.5 until current release !4874
+ - Replace animateEmoji timeout with eventListener (ClemMakesApps)
+ - Show badges in Milestone tabs. !5946 (Dan Rowden)
+ - Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
+ - Require confirmation when not logged in for unsubscribe links !6223 (Maximiliano Perez Coto)
+ - Add `wiki_page_events` to project hook APIs (Ben Boeckel)
+ - Remove Gitorious import
+ - Loads GFM autocomplete source only when required
+ - Fix issue with slash commands not loading on new issue page
+ - Fix inconsistent background color for filter input field (ClemMakesApps)
+ - Remove prefixes from transition CSS property (ClemMakesApps)
+ - Add Sentry logging to API calls
+ - Add BroadcastMessage API
+ - Merge request tabs are fixed when scrolling page
+ - Use 'git update-ref' for safer web commits !6130
+ - Sort pipelines requested through the API
+ - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
+ - Fix issue boards loading on large screens
+ - Change pipeline duration to be jobs running time instead of simple wall time from start to end !6084
+ - Show queued time when showing a pipeline !6084
+ - Remove unused mixins (ClemMakesApps)
+ - Fix issue board label filtering appending already filtered labels
+ - Add search to all issue board lists
+ - Scroll active tab into view on mobile
+ - Fix groups sort dropdown alignment (ClemMakesApps)
+ - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps)
+ - Use JavaScript tooltips for mentions !5301 (winniehell)
+ - Add hover state to todos !5361 (winniehell)
+ - Fix icon alignment of star and fork buttons !5451 (winniehell)
+ - Fix alignment of icon buttons !5887 (winniehell)
+ - Added Ubuntu 16.04 support for packager.io (JonTheNiceGuy)
+ - Fix markdown help references (ClemMakesApps)
+ - Add last commit time to repo view (ClemMakesApps)
+ - Fix accessibility and visibility of project list dropdown button !6140
+ - Fix missing flash messages on service edit page (airatshigapov)
+ - Added project-specific enable/disable setting for LFS !5997
+ - Added group-specific enable/disable setting for LFS !6164
+ - Add optional 'author' param when making commits. !5822 (dandunckelman)
+ - Don't expose a user's token in the `/api/v3/user` API (!6047)
+ - Remove redundant js-timeago-pending from user activity log (ClemMakesApps)
+ - Ability to manage project issues, snippets, wiki, merge requests and builds access level
+ - Remove inconsistent font weight for sidebar's labels (ClemMakesApps)
+ - Align add button on repository view (ClemMakesApps)
+ - Fix contributions calendar month label truncation (ClemMakesApps)
+ - Import release note descriptions from GitHub (EspadaV8)
+ - Added tests for diff notes
+ - Add pipeline events to Slack integration !5525
+ - Add a button to download latest successful artifacts for branches and tags !5142
+ - Remove redundant pipeline tooltips (ClemMakesApps)
+ - Expire commit info views after one day, instead of two weeks, to allow for user email updates
+ - Add delimiter to project stars and forks count (ClemMakesApps)
+ - Fix badge count alignment (ClemMakesApps)
+ - Remove green outline from `New branch unavailable` button on issue page !5858 (winniehell)
+ - Fix repo title alignment (ClemMakesApps)
+ - Change update interval of contacted_at
+ - Add LFS support to SSH !6043
+ - Fix branch title trailing space on hover (ClemMakesApps)
+ - Don't include 'Created By' tag line when importing from GitHub if there is a linked GitLab account (EspadaV8)
+ - Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
+ - Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison)
+ - Order award emoji tooltips in order they were added (EspadaV8)
+ - Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
+ - Update merge_requests.md with a simpler way to check out a merge request. !5944
+ - Fix button missing type (ClemMakesApps)
+ - Gitlab::Checks is now instrumented
+ - Move to project dropdown with infinite scroll for better performance
+ - Fix leaking of submit buttons outside the width of a main container !18731 (originally by @pavelloz)
+ - Load branches asynchronously in Cherry Pick and Revert dialogs.
+ - Convert datetime coffeescript spec to ES6 (ClemMakesApps)
+ - Add merge request versions !5467
+ - Change using size to use count and caching it for number of group members. !5935
+ - Replace play icon font with svg (ClemMakesApps)
+ - Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck)
+ - Reduce number of database queries on builds tab
+ - Wrap text in commit message containers
+ - Capitalize mentioned issue timeline notes (ClemMakesApps)
+ - Fix inconsistent checkbox alignment (ClemMakesApps)
+ - Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger)
+ - Adds response mime type to transaction metric action when it's not HTML
+ - Fix hover leading space bug in pipeline graph !5980
+ - Avoid conflict with admin labels when importing GitHub labels
+ - User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
+ - Fix repository page ui issues
+ - Avoid protected branches checks when verifying access without branch name
+ - Add information about user and manual build start to runner as variables !6201 (Sergey Gnuskov)
+ - Fixed invisible scroll controls on build page on iPhone
+ - Fix error on raw build trace download for old builds stored in database !4822
+ - Refactor the triggers page and documentation !6217
+ - Show values of CI trigger variables only when clicked (Katarzyna Kobierska Ula Budziszewska)
+ - Use default clone protocol on "check out, review, and merge locally" help page URL
+ - Let the user choose a namespace and name on GitHub imports
+ - API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
+ - Allow bulk update merge requests from merge requests index page
+ - Ensure validation messages are shown within the milestone form
+ - Add notification_settings API calls !5632 (mahcsig)
+ - Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska)
+ - Fix URLs with anchors in wiki !6300 (houqp)
+ - Deleting source project with existing fork link will close all related merge requests !6177 (Katarzyna Kobierska Ula Budziszeska)
+ - Return 204 instead of 404 for /ci/api/v1/builds/register.json if no builds are scheduled for a runner !6225
+ - Fix Gitlab::Popen.popen thread-safety issue
+ - Add specs to removing project (Katarzyna Kobierska Ula Budziszewska)
+ - Clean environment variables when running git hooks
+ - Fix Import/Export issues importing protected branches and some specific models
+ - Fix non-master branch readme display in tree view
+ - Add UX improvements for merge request version diffs
+
+## 8.11.11 (2016-11-07)
+
+- Fix XSS issue in Markdown autolinker
+
+## 8.11.10 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+
+## 8.11.9
+
+ - Don't send Private-Token (API authentication) headers to Sentry
+ - Share projects via the API only with groups the authenticated user can access
+
+## 8.11.8
+
+ - Respect the fork_project permission when forking projects
+ - Set a restrictive CORS policy on the API for credentialed requests
+ - API: disable rails session auth for non-GET/HEAD requests
+ - Escape HTML nodes in builds commands in CI linter
+
+## 8.11.7
+
+ - Avoid conflict with admin labels when importing GitHub labels. !6158
+ - Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234
+ - Allow the Rails cookie to be used for API authentication.
+ - Login/Register UX upgrade !6328
+
+## 8.11.6
+
+ - Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
+ - Make merge conflict file size limit 200 KB, to match the docs. !6052
+ - Fix an error where we were unable to create a CommitStatus for running state. !6107
+ - Optimize discussion notes resolving and unresolving. !6141
+ - Fix GitLab import button. !6167
+ - Restore SSH Key title auto-population behavior. !6186
+ - Fix DB schema to match latest migration. !6256
+ - Exclude some pending or inactivated rows in Member scopes.
+
+## 8.11.5
+
+ - Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
+ - Fix member expiration date picker after update. !6184
+ - Fix suggested colors options for new labels in the admin area. !6138
+ - Optimize discussion notes resolving and unresolving
+ - Fix GitLab import button
+ - Fix confidential issues being exposed as public using gitlab.com export
+ - Remove gitorious from import_sources. !6180
+ - Scope webhooks/services that will run for confidential issues
+ - Remove gitorious from import_sources
+ - Fix confidential issues being exposed as public using gitlab.com export
+ - Use oj gem for faster JSON processing
+
+## 8.11.4
+
+ - Fix resolving conflicts on forks. !6082
+ - Fix diff commenting on merge requests created prior to 8.10. !6029
+ - Fix pipelines tab layout regression. !5952
+ - Fix "Wiki" link not appearing in navigation for projects with external wiki. !6057
+ - Do not enforce using hash with hidden key in CI configuration. !6079
+ - Fix hover leading space bug in pipeline graph !5980
+ - Fix sorting issues by "last updated" doesn't work after import from GitHub
+ - GitHub importer use default project visibility for non-private projects
+ - Creating an issue through our API now emails label subscribers !5720
+ - Block concurrent updates for Pipeline
+ - Don't create groups for unallowed users when importing projects
+ - Fix issue boards leak private label names and descriptions
+ - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner)
+ - Remove gitorious. !5866
+ - Allow compare merge request versions
+
+## 8.11.3
+
+ - Allow system info page to handle case where info is unavailable
+ - Label list shows all issues (opened or closed) with that label
+ - Don't show resolve conflicts link before MR status is updated
+ - Fix IE11 fork button bug !5982
+ - Don't prevent viewing the MR when git refs for conflicts can't be found on disk
+ - Fix external issue tracker "Issues" link leading to 404s
+ - Don't try to show merge conflict resolution info if a merge conflict contains non-UTF-8 characters
+ - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
+ - Issues filters reset button
+
+## 8.11.2
+
+ - Show "Create Merge Request" widget for push events to fork projects on the source project. !5978
+ - Use gitlab-workhorse 0.7.11 !5983
+ - Does not halt the GitHub import process when an error occurs. !5763
+ - Fix file links on project page when default view is Files !5933
+ - Fixed enter key in search input not working !5888
+
+## 8.11.1
+
+ - Pulled due to packaging error.
+
+## 8.11.0 (2016-08-22)
+
+ - Use test coverage value from the latest successful pipeline in badge. !5862
+ - Add test coverage report badge. !5708
+ - Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
+ - Add Koding (online IDE) integration
+ - Ability to specify branches for Pivotal Tracker integration (Egor Lynko)
+ - Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
+ - Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
+ - Fix adding line comments on the initial commit to a repo !5900
+ - Fix the title of the toggle dropdown button. !5515 (herminiotorres)
+ - Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz)
+ - Update to Ruby 2.3.1. !4948
+ - Add Issues Board !5548
+ - Allow resolving merge conflicts in the UI !5479
+ - Improve diff performance by eliminating redundant checks for text blobs
+ - Ensure that branch names containing escapable characters (e.g. %20) aren't unescaped indiscriminately. !5770 (ewiltshi)
+ - Convert switch icon into icon font (ClemMakesApps)
+ - API: Endpoints for enabling and disabling deploy keys
+ - API: List access requests, request access, approve, and deny access requests to a project or a group. !4833
+ - Use long options for curl examples in documentation !5703 (winniehell)
+ - Added tooltip listing label names to the labels value in the collapsed issuable sidebar
+ - Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
+ - GitLab Performance Monitoring can now track custom events such as the number of tags pushed to a repository
+ - Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
+ - Allow naming U2F devices !5833
+ - Ignore URLs starting with // in Markdown links !5677 (winniehell)
+ - Fix CI status icon link underline (ClemMakesApps)
+ - The Repository class is now instrumented
+ - Fix commit mention font inconsistency (ClemMakesApps)
+ - Do not escape URI when extracting path !5878 (winniehell)
+ - Fix filter label tooltip HTML rendering (ClemMakesApps)
+ - Cache the commit author in RequestStore to avoid extra lookups in PostReceive
+ - Expand commit message width in repo view (ClemMakesApps)
+ - Cache highlighted diff lines for merge requests
+ - Pre-create all builds for a Pipeline when the new Pipeline is created !5295
+ - Allow merge request diff notes and discussions to be explicitly marked as resolved
+ - API: Add deployment endpoints
+ - API: Add Play endpoint on Builds
+ - Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
+ - Show wall clock time when showing a pipeline. !5734
+ - Show member roles to all users on members page
+ - Project.visible_to_user is instrumented again
+ - Fix awardable button mutuality loading spinners (ClemMakesApps)
+ - Sort todos by date and priority
+ - Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
+ - Optimize maximum user access level lookup in loading of notes
+ - Send notification emails to users newly mentioned in issue and MR edits !5800
+ - Add "No one can push" as an option for protected branches. !5081
+ - Improve performance of AutolinkFilter#text_parse by using XPath
+ - Add experimental Redis Sentinel support !1877
+ - Rendering of SVGs as blobs is now limited to SVGs with a size smaller or equal to 2MB
+ - Fix branches page dropdown sort initial state (ClemMakesApps)
+ - Environments have an url to link to
+ - Various redundant database indexes have been removed
+ - Update `timeago` plugin to use multiple string/locale settings
+ - Remove unused images (ClemMakesApps)
+ - Get issue and merge request description templates from repositories
+ - Enforce 2FA restrictions on API authentication endpoints !5820
+ - Limit git rev-list output count to one in forced push check
+ - Show deployment status on merge requests with external URLs
+ - Clean up unused routes (Josef Strzibny)
+ - Fix issue on empty project to allow developers to only push to protected branches if given permission
+ - API: Add enpoints for pipelines
+ - Add green outline to New Branch button. !5447 (winniehell)
+ - Optimize generating of cache keys for issues and notes
+ - Fix repository push email formatting in Outlook
+ - Improve performance of syntax highlighting Markdown code blocks
+ - Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
+ - Remove delay when hitting "Reply..." button on page with a lot of discussions
+ - Retrieve rendered HTML from cache in one request
+ - Fix renaming repository when name contains invalid chararacters under project settings
+ - Upgrade Grape from 0.13.0 to 0.15.0. !4601
+ - Trigram indexes for the "ci_runners" table have been removed to speed up UPDATE queries
+ - Fix devise deprecation warnings.
+ - Check for 2FA when using Git over HTTP and only allow PersonalAccessTokens as password in that case !5764
+ - Update version_sorter and use new interface for faster tag sorting
+ - Optimize checking if a user has read access to a list of issues !5370
+ - Store all DB secrets in secrets.yml, under descriptive names !5274
+ - Fix syntax highlighting in file editor
+ - Support slash commands in issue and merge request descriptions as well as comments. !5021
+ - Nokogiri's various parsing methods are now instrumented
+ - Add archived badge to project list !5798
+ - Add simple identifier to public SSH keys (muteor)
+ - Admin page now references docs instead of a specific file !5600 (AnAverageHuman)
+ - Fix filter input alignment (ClemMakesApps)
+ - Include old revision in merge request update hooks (Ben Boeckel)
+ - Add build event color in HipChat messages (David Eisner)
+ - Make fork counter always clickable. !5463 (winniehell)
+ - Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
+ - Gitlab::Highlight is now instrumented
+ - All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
+ - Allow users to import cross-repository pull requests from GitHub
+ - The overhead of instrumented method calls has been reduced
+ - Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
+ - Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
+ - Add pipeline events hook
+ - Bump gitlab_git to speedup DiffCollection iterations
+ - Rewrite description of a blocked user in admin settings. (Elias Werberich)
+ - Make branches sortable without push permission !5462 (winniehell)
+ - Check for Ci::Build artifacts at database level on pipeline partial
+ - Convert image diff background image to CSS (ClemMakesApps)
+ - Remove unnecessary index_projects_on_builds_enabled index from the projects table
+ - Make "New issue" button in Issue page less obtrusive !5457 (winniehell)
+ - Gitlab::Metrics.current_transaction needs to be public for RailsQueueDuration
+ - Fix search for notes which belongs to deleted objects
+ - Allow Akismet to be trained by submitting issues as spam or ham !5538
+ - Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
+ - Allow branch names ending with .json for graph and network page !5579 (winniehell)
+ - Add the `sprockets-es6` gem
+ - Improve OAuth2 client documentation (muteor)
+ - Fix diff comments inverted toggle bug (ClemMakesApps)
+ - Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
+ - Profile requests when a header is passed
+ - Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
+ - Speedup DiffNote#active? on discussions, preloading noteables and avoid touching git repository to return diff_refs when possible
+ - Add commit stats in commit api. !5517 (dixpac)
+ - Add CI configuration button on project page
+ - Fix merge request new view not changing code view rendering style
+ - edit_blob_link will use blob passed onto the options parameter
+ - Make error pages responsive (Takuya Noguchi)
+ - The performance of the project dropdown used for moving issues has been improved
+ - Fix skip_repo parameter being ignored when destroying a namespace
+ - Add all builds into stage/job dropdowns on builds page
+ - Change requests_profiles resource constraint to catch virtually any file
+ - Bump gitlab_git to lazy load compare commits
+ - Reduce number of queries made for merge_requests/:id/diffs
+ - Add the option to set the expiration date for the project membership when giving a user access to a project. !5599 (Adam Niedzielski)
+ - Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
+ - Fix bug where destroying a namespace would not always destroy projects
+ - Fix RequestProfiler::Middleware error when code is reloaded in development
+ - Allow horizontal scrolling of code blocks in issue body
+ - Catch what warden might throw when profiling requests to re-throw it
+ - Avoid commit lookup on diff_helper passing existing local variable to the helper method
+ - Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
+ - Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
+ - Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
+ - Adds support for pending invitation project members importing projects
+ - Add pipeline visualization/graph on pipeline page
+ - Update devise initializer to turn on changed password notification emails. !5648 (tombell)
+ - Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
+ - Fix importing GitLab projects with an invalid MR source project
+ - Sort folders with submodules in Files view !5521
+ - Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
+ - Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
+ - Add pipelines tab to merge requests
+ - Fix notification_service argument error of declined invitation emails
+ - Fix a memory leak caused by Banzai::Filter::SanitizationFilter
+ - Speed up todos queries by limiting the projects set we join with
+ - Ensure file editing in UI does not overwrite commited changes without warning user
+ - Eliminate unneeded calls to Repository#blob_at when listing commits with no path
+ - Update gitlab_git gem to 10.4.7
+ - Simplify SQL queries of marking a todo as done
+
+## 8.10.13 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+
+## 8.10.12
+
+ - Don't send Private-Token (API authentication) headers to Sentry
+ - Share projects via the API only with groups the authenticated user can access
+
+## 8.10.11
+
+ - Respect the fork_project permission when forking projects
+ - Set a restrictive CORS policy on the API for credentialed requests
+ - API: disable rails session auth for non-GET/HEAD requests
+ - Escape HTML nodes in builds commands in CI linter
+
+## 8.10.10
+
+ - Allow the Rails cookie to be used for API authentication.
+
+## 8.10.9
+
+ - Exclude some pending or inactivated rows in Member scopes
+
+## 8.10.8
+
+ - Fix information disclosure in issue boards.
+ - Fix privilege escalation in project import.
+
+## 8.10.7
+
+ - Upgrade Hamlit to 2.6.1. !5873
+ - Upgrade Doorkeeper to 4.2.0. !5881
+
+## 8.10.6
+
+ - Upgrade Rails to 4.2.7.1 for security fixes. !5781
+ - Restore "Largest repository" sort option on Admin > Projects page. !5797
+ - Fix privilege escalation via project export.
+ - Require administrator privileges to perform a project import.
+
+## 8.10.5
+
+ - Add a data migration to fix some missing timestamps in the members table. !5670
+ - Revert the "Defend against 'Host' header injection" change in the source NGINX templates. !5706
+ - Cache project count for 5 minutes to reduce DB load. !5746 & !5754
+
+## 8.10.4
+
+ - Don't close referenced upstream issues from a forked project.
+ - Fixes issue with dropdowns `enter` key not working correctly. !5544
+ - Fix Import/Export project import not working in HA mode. !5618
+ - Fix Import/Export error checking versions. !5638
+
+## 8.10.3
+
+ - Fix Import/Export issue importing milestones and labels not associated properly. !5426
+ - Fix timing problems running imports on production. !5523
+ - Add a log message when a project is scheduled for destruction for debugging. !5540
+ - Fix hooks missing on imported GitLab projects. !5549
+ - Properly abort a merge when merge conflicts occur. !5569
+ - Fix importer for GitHub Pull Requests when a branch was removed. !5573
+ - Ignore invalid IPs in X-Forwarded-For when trusted proxies are configured. !5584
+ - Trim extra displayed carriage returns in diffs and files with CRLFs. !5588
+ - Fix label already exist error message in the right sidebar.
+
+## 8.10.2
+
+ - User can now search branches by name. !5144
+ - Page is now properly rendered after committing the first file and creating the first branch. !5399
+ - Add branch or tag icon to ref in builds page. !5434
+ - Fix backup restore. !5459
+ - Use project ID in repository cache to prevent stale data from persisting across projects. !5460
+ - Fix issue with autocomplete search not working with enter key. !5466
+ - Add iid to MR API response. !5468
+ - Disable MySQL foreign key checks before dropping all tables. !5472
+ - Ensure relative paths for video are rewritten as we do for images. !5474
+ - Ensure current user can retry a build before showing the 'Retry' button. !5476
+ - Add ENV variable to skip repository storages validations. !5478
+ - Added `*.js.es6 gitlab-language=javascript` to `.gitattributes`. !5486
+ - Don't show comment button in gutter of diffs on MR discussion tab. !5493
+ - Rescue Rugged::OSError (lock exists) when creating references. !5497
+ - Fix expand all diffs button in compare view. !5500
+ - Show release notes in tags list. !5503
+ - Fix a bug where forking a project from a repository storage to another would fail. !5509
+ - Fix missing schema update for `20160722221922`. !5512
+ - Update `gitlab-shell` version to 3.2.1 in the 8.9->8.10 update guide. !5516
+
+## 8.10.1
+
+ - Refactor repository storages documentation. !5428
+ - Gracefully handle case when keep-around references are corrupted or exist already. !5430
+ - Add detailed info on storage path mountpoints. !5437
+ - Fix Error 500 when creating Wiki pages with hyphens or spaces. !5444
+ - Fix bug where replies to commit notes displayed in the MR discussion tab wouldn't show up on the commit page. !5446
+ - Ignore invalid trusted proxies in X-Forwarded-For header. !5454
+ - Add links to the real markdown.md file for all GFM examples. !5458
+
+## 8.10.0 (2016-07-22)
+
+ - Fix profile activity heatmap to show correct day name (eanplatter)
+ - Speed up ExternalWikiHelper#get_project_wiki_path
+ - Expose {should,force}_remove_source_branch (Ben Boeckel)
+ - Add the functionality to be able to rename a file. !5049
+ - Disable PostgreSQL statement timeout during migrations
+ - Fix projects dropdown loading performance with a simplified api cal. !5113
+ - Fix commit builds API, return all builds for all pipelines for given commit. !4849
+ - Replace Haml with Hamlit to make view rendering faster. !3666
+ - Refresh the branch cache after `git gc` runs
+ - Allow to disable request access button on projects/groups
+ - Refactor repository paths handling to allow multiple git mount points
+ - Optimize system note visibility checking by memoizing the visible reference count. !5070
+ - Add Application Setting to configure default Repository Path for new projects
+ - Delete award emoji when deleting a user
+ - Remove pinTo from Flash and make inline flash messages look nicer. !4854 (winniehell)
+ - Add an API for downloading latest successful build from a particular branch or tag. !5347
+ - Avoid data-integrity issue when cleaning up repository archive cache.
+ - Add link to profile to commit avatar. !5163 (winniehell)
+ - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
+ - Align flash messages with left side of page content. !4959 (winniehell)
+ - Display tooltip for "Copy to Clipboard" button. !5164 (winniehell)
+ - Use default cursor for table header of project files. !5165 (winniehell)
+ - Store when and yaml variables in builds table
+ - Display last commit of deleted branch in push events. !4699 (winniehell)
+ - Escape file extension when parsing search results. !5141 (winniehell)
+ - Add "passing with warnings" to the merge request pipeline possible statuses, this happens when builds that allow failures have failed. !5004
+ - Add image border in Markdown preview. !5162 (winniehell)
+ - Apply the trusted_proxies config to the rack request object for use with rack_attack
+ - Added the ability to block sign ups using a domain blacklist. !5259
+ - Upgrade to Rails 4.2.7. !5236
+ - Extend exposed environment variables for CI builds
+ - Deprecate APIs "projects/:id/keys/...". Use "projects/:id/deploy_keys/..." instead
+ - Add API "deploy_keys" for admins to get all deploy keys
+ - Allow to pull code with deploy key from public projects
+ - Use limit parameter rather than hardcoded value in `ldap:check` rake task (Mike Ricketts)
+ - Add Sidekiq queue duration to transaction metrics.
+ - Add a new column `artifacts_size` to table `ci_builds`. !4964
+ - Let Workhorse serve format-patch diffs
+ - Display tooltip for mentioned users and groups. !5261 (winniehell)
+ - Allow build email service to be tested
+ - Added day name to contribution calendar tooltips
+ - Refactor user authorization check for a single project to avoid querying all user projects
+ - Make images fit to the size of the viewport. !4810
+ - Fix check for New Branch button on Issue page. !4630 (winniehell)
+ - Fix GFM autocomplete not working on wiki pages
+ - Fixed enter key not triggering click on first row when searching in a dropdown
+ - Updated dropdowns in issuable form to use new GitLab dropdown style
+ - Make images fit to the size of the viewport !4810
+ - Fix check for New Branch button on Issue page !4630 (winniehell)
+ - Fix MR-auto-close text added to description. !4836
+ - Support U2F devices in Firefox. !5177
+ - Fix issue, preventing users w/o push access to sort tags. !5105 (redetection)
+ - Add Spring EmojiOne updates.
+ - Added Rake task for tracking deployments. !5320
+ - Fix fetching LFS objects for private CI projects
+ - Add the new 2016 Emoji! Adds 72 new emoji including bacon, facepalm, and selfie. !5237
+ - Add syntax for multiline blockquote using `>>>` fence. !3954
+ - Fix viewing notification settings when a project is pending deletion
+ - Updated compare dropdown menus to use GL dropdown
+ - Redirects back to issue after clicking login link
+ - Eager load award emoji on notes
+ - Allow to define manual actions/builds on Pipelines and Environments
+ - Fix pagination when sorting by columns with lots of ties (like priority)
+ - The Markdown reference parsers now re-use query results to prevent running the same queries multiple times. !5020
+ - Updated project header design
+ - Issuable collapsed assignee tooltip is now the users name
+ - Fix compare view not changing code view rendering style
+ - Exclude email check from the standard health check
+ - Updated layout for Projects, Groups, Users on Admin area. !4424
+ - Fix changing issue state columns in milestone view
+ - Update health_check gem to version 2.1.0
+ - Add notification settings dropdown for groups
+ - Render inline diffs for multiple changed lines following eachother
+ - Wildcards for protected branches. !4665
+ - Allow importing from Github using Personal Access Tokens. (Eric K Idema)
+ - API: Expose `due_date` for issues (Robert Schilling)
+ - API: Todos. !3188 (Robert Schilling)
+ - API: Expose shared groups for projects and shared projects for groups. !5050 (Robert Schilling)
+ - API: Expose `developers_can_push` and `developers_can_merge` for branches. !5208 (Robert Schilling)
+ - Add "Enabled Git access protocols" to Application Settings
+ - Diffs will create button/diff form on demand no on server side
+ - Reduce size of HTML used by diff comment forms
+ - Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
+ - Fix user creation with stronger minimum password requirements. !4054 (nathan-pmt)
+ - Only show New Snippet button to users that can create snippets.
+ - PipelinesFinder uses git cache data
+ - Track a user who created a pipeline
+ - Actually render old and new sections of parallel diff next to each other
+ - Throttle the update of `project.pushes_since_gc` to 1 minute.
+ - Allow expanding and collapsing files in diff view. !4990
+ - Collapse large diffs by default (!4990)
+ - Fix mentioned users list on diff notes
+ - Add support for inline videos in GitLab Flavored Markdown. !5215 (original implementation by Eric Hayes)
+ - Fix creation of deployment on build that is retried, redeployed or rollback
+ - Don't parse Rinku returned value to DocFragment when it didn't change the original html string.
+ - Check for conflicts with existing Project's wiki path when creating a new project.
+ - Show last push widget in upstream after push to fork
+ - Fix stage status shown for pipelines
+ - Cache todos pending/done dashboard query counts.
+ - Don't instantiate a git tree on Projects show default view
+ - Bump Rinku to 2.0.0
+ - Remove unused front-end variable -> default_issues_tracker
+ - ObjectRenderer retrieve renderer content using Rails.cache.read_multi
+ - Better caching of git calls on ProjectsController#show.
+ - Avoid to retrieve MR closes_issues as much as possible.
+ - Hide project name in project activities. !5068 (winniehell)
+ - Add API endpoint for a group issues. !4520 (mahcsig)
+ - Add Bugzilla integration. !4930 (iamtjg)
+ - Fix new snippet style bug (elliotec)
+ - Instrument Rinku usage
+ - Be explicit to define merge request discussion variables
+ - Use cache for todos counter calling TodoService
+ - Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
+ - RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
+ - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
+ - Made project list visibility icon fixed width
+ - Set import_url validation to be more strict
+ - Memoize MR merged/closed events retrieval
+ - Don't render discussion notes when requesting diff tab through AJAX
+ - Add basic system information like memory and disk usage to the admin panel
+ - Don't garbage collect commits that have related DB records like comments
+ - Allow to setup event by channel on slack service
+ - More descriptive message for git hooks and file locks
+ - Aliases of award emoji should be stored as original name. !5060 (dixpac)
+ - Handle custom Git hook result in GitLab UI
+ - Allow to access Container Registry for Public and Internal projects
+ - Allow '?', or '&' for label names
+ - Support redirected blobs for Container Registry integration
+ - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
+ - Add date when user joined the team on the member page
+ - Fix 404 redirect after validation fails importing a GitLab project
+ - Added setting to set new users by default as external. !4545 (Dravere)
+ - Add min value for project limit field on user's form. !3622 (jastkand)
+ - Reset project pushes_since_gc when we enqueue the git gc call
+ - Add reminder to not paste private SSH keys. !4399 (Ingo Blechschmidt)
+ - Collapsed diffs lines/size don't acumulate to overflow diffs.
+ - Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
+ - Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
+ - Fix GitHub client requests when rate limit is disabled
+ - Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
+ - Redesign Builds and Pipelines pages
+ - Change status color and icon for running builds
+ - Fix commenting issue in side by side diff view for unchanged lines
+ - Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
+ - Project export filename now includes the project and namespace path
+ - Fix last update timestamp on issues not preserved on gitlab.com and project imports
+ - Fix issues importing projects from EE to CE
+ - Fix creating group with space in group path
+ - Improve cron_jobs loading error messages. !5318 / !5360
+ - Prevent toggling sidebar when clipboard icon clicked
+ - Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
+ - Limit the number of retries on error to 3 for exporting projects
+ - Allow empty repositories on project import/export
+ - Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska)
+ - Allow bulk (un)subscription from issues in issue index
+ - Fix MR diff encoding issues exporting GitLab projects
+ - Move builds settings out of project settings and rename Pipelines
+ - Add builds badge to Pipelines settings page
+ - Export and import avatar as part of project import/export
+ - Fix migration corrupting import data for old version upgrades
+ - Show tooltip on GitLab export link in new project page
+ - Fix import_data wrongly saved as a result of an invalid import_url !5206
+
+## 8.9.11
+
+ - Respect the fork_project permission when forking projects
+ - Set a restrictive CORS policy on the API for credentialed requests
+ - API: disable rails session auth for non-GET/HEAD requests
+ - Escape HTML nodes in builds commands in CI linter
+
+## 8.9.10
+
+ - Allow the Rails cookie to be used for API authentication.
+
+## 8.9.9
+
+ - Exclude some pending or inactivated rows in Member scopes
+
+## 8.9.8
+
+ - Upgrade Doorkeeper to 4.2.0. !5881
+
+## 8.9.7
+
+ - Upgrade Rails to 4.2.7.1 for security fixes. !5781
+ - Require administrator privileges to perform a project import.
+
+## 8.9.6
+
+ - Fix importing of events under notes for GitLab projects. !5154
+ - Fix log statements in import/export. !5129
+ - Fix commit avatar alignment in compare view. !5128
+ - Fix broken migration in MySQL. !5005
+ - Overwrite Host and X-Forwarded-Host headers in NGINX !5213
+ - Keeps issue number when importing from Gitlab.com
+ - Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
+
+## 8.9.5
+
+ - Add more debug info to import/export and memory killer. !5108
+ - Fixed avatar alignment in new MR view. !5095
+ - Fix diff comments not showing up in activity feed. !5069
+ - Add index on both Award Emoji user and name. !5061
+ - Downgrade to Redis 3.2.2 due to massive memory leak with Sidekiq. !5056
+ - Re-enable import button when import process fails due to namespace already being taken. !5053
+ - Fix snippets comments not displayed. !5045
+ - Fix emoji paths in relative root configurations. !5027
+ - Fix issues importing events in Import/Export. !4987
+ - Fixed 'use shortcuts' button on docs. !4979
+ - Admin should be able to turn shared runners into specific ones. !4961
+ - Update RedCloth to 4.3.2 for CVE-2012-6684. !4929 (Takuya Noguchi)
+ - Improve the request / withdraw access button. !4860
+
+## 8.9.4
+
+ - Fix privilege escalation issue with OAuth external users.
+ - Ensure references to private repos aren't shown to logged-out users.
+ - Fixed search field blur not removing focus. !4704
+ - Resolve "Sub nav isn't showing on file view". !4890
+ - Fixes middle click and double request when navigating through the file browser. !4891
+ - Fixed URL on label button when filtering. !4897
+ - Fixed commit avatar alignment. !4933
+ - Do not show build retry link when build is active. !4967
+ - Fix restore Rake task warning message output. !4980
+ - Handle external issues in IssueReferenceFilter. !4988
+ - Expiry date on pinned nav cookie. !5009
+ - Updated breakpoint for sidebar pinning. !5019
+
+## 8.9.3
+
+ - Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !4963
+ - Fix rendering of commit notes. !4953
+ - Resolve "Pin should show up at 1280px min". !4947
+ - Switched mobile button icons to ellipsis and angle. !4944
+ - Correctly returns todo ID after creating todo. !4941
+ - Better debugging for memory killer middleware. !4936
+ - Remove duplicate new page btn from edit wiki. !4904
+ - Use clock_gettime for all performance timestamps. !4899
+ - Use memorized tags array when searching tags by name. !4859
+ - Fixed avatar alignment in new MR view. !4901
+ - Removed fade when filtering results. !4932
+ - Fix missing avatar on system notes. !4954
+ - Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973
+ - Use update_columns to bypass all the dirty code on active_record. !4985
+ - Fix restore Rake task warning message output !4980
+
+## 8.9.2
+
+ - Fix visibility of snippets when searching.
+ - Fix an information disclosure when requesting access to a group containing private projects.
+ - Update omniauth-saml to 1.6.0 !4951
+
+## 8.9.1
+
+ - Refactor labels documentation. !3347
+ - Eager load award emoji on notes. !4628
+ - Fix some CI wording in documentation. !4660
+ - Document `GIT_STRATEGY` and `GIT_DEPTH`. !4720
+ - Add documentation for the export & import features. !4732
+ - Add some docs for Docker Registry configuration. !4738
+ - Ensure we don't send the "access request declined" email to access requesters on project deletion. !4744
+ - Display group/project access requesters separately in the admin area. !4798
+ - Add documentation and examples for configuring cloud storage for registry images. !4812
+ - Clarifies documentation about artifact expiry. !4831
+ - Fix the Network graph links. !4832
+ - Fix MR-auto-close text added to description. !4836
+ - Add documentation for award emoji now that comments can be awarded with emojis. !4839
+ - Fix typo in export failure email. !4847
+ - Fix header vertical centering. !4170
+ - Fix subsequent SAML sign ins. !4718
+ - Set button label when picking an option from status dropdown. !4771
+ - Prevent invalid URLs from raising exceptions in WikiLink Filter. !4775
+ - Handle external issues in IssueReferenceFilter. !4789
+ - Support for rendering/redacting multiple documents. !4828
+ - Update Todos documentation and screenshots to include new functionality. !4840
+ - Hide nav arrows by default. !4843
+ - Added bottom padding to label color suggestion link. !4845
+ - Use jQuery objects in ref dropdown. !4850
+ - Fix GitLab project import issues related to notes and builds. !4855
+ - Restrict header logo to 36px so it doesn't overflow. !4861
+ - Fix unwanted label unassignment. !4863
+ - Fix mobile Safari bug where horizontal nav arrows would flicker on scroll. !4869
+ - Restore old behavior around diff notes to outdated discussions. !4870
+ - Fix merge requests project settings help link anchor. !4873
+ - Fix 404 when accessing pipelines as guest user on public projects. !4881
+ - Remove width restriction for logo on sign-in page. !4888
+ - Bump gitlab_git to 10.2.3 to fix false truncated warnings with ISO-8559 files. !4884
+ - Apply selected value as label. !4886
+ - Change Retry to Re-deploy on Deployments page
+ - Fix temp file being deleted after the request while importing a GitLab project. !4894
+ - Fix pagination when sorting by columns with lots of ties (like priority)
+ - Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise.
+ - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
+ - Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912
+ - Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915
+ - Remove duplicate 'New Page' button on edit wiki page
+
+## 8.9.0 (2016-06-22)
+
+ - Fix group visibility form layout in application settings
+ - Fix builds API response not including commit data
+ - Fix error when CI job variables key specified but not defined
+ - Fix pipeline status when there are no builds in pipeline
+ - Fix Error 500 when using closes_issues API with an external issue tracker
+ - Add more information into RSS feed for issues (Alexander Matyushentsev)
+ - Bulk assign/unassign labels to issues.
+ - Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
+ - Show Star and Fork buttons on mobile.
+ - Performance improvements on RelativeLinkFilter
+ - Fix endless redirections when accessing user OAuth applications when they are disabled
+ - Allow enabling wiki page events from Webhook management UI
+ - Bump rouge to 1.11.0
+ - Fix issue with arrow keys not working in search autocomplete dropdown
+ - Fix an issue where note polling stopped working if a window was in the
+ background during a refresh.
+ - Pre-processing Markdown now only happens when needed
+ - Make EmailsOnPushWorker use Sidekiq mailers queue
+ - Redesign all Devise emails. !4297
+ - Don't show 'Leave Project' to group members
+ - Fix wiki page events' webhook to point to the wiki repository
+ - Add a border around images to differentiate them from the background.
+ - Don't show tags for revert and cherry-pick operations
+ - Show image ID on registry page
+ - Fix issue todo not remove when leave project !4150 (Long Nguyen)
+ - Allow customisable text on the 'nearly there' page after a user signs up
+ - Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
+ - Fix SVG sanitizer to allow more elements
+ - Allow forking projects with restricted visibility level
+ - Added descriptions to notification settings dropdown
+ - Improve note validation to prevent errors when creating invalid note via API
+ - Reduce number of fog gem dependencies
+ - Add number of merge requests for a given milestone to the milestones view.
+ - Implement a fair usage of shared runners
+ - Remove project notification settings associated with deleted projects
+ - Fix 404 page when viewing TODOs that contain milestones or labels in different projects
+ - Add a metric for the number of new Redis connections created by a transaction
+ - Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
+ - Redesign navigation for project pages
+ - Fix images in sign-up confirmation email
+ - Added shortcut 'y' for copying a files content hash URL #14470
+ - Fix groups API to list only user's accessible projects
+ - Fix horizontal scrollbar for long commit message.
+ - GitLab Performance Monitoring now tracks the total method execution time and call count per method
+ - Add Environments and Deployments
+ - Redesign account and email confirmation emails
+ - Don't fail builds for projects that are deleted
+ - Support Docker Registry manifest v1
+ - `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
+ - Bump nokogiri to 1.6.8
+ - Use gitlab-shell v3.0.0
+ - Fixed alignment of download dropdown in merge requests
+ - Upgrade to jQuery 2
+ - Adds selected branch name to the dropdown toggle
+ - Add API endpoint for Sidekiq Metrics !4653
+ - Refactoring Award Emoji with API support for Issues and MergeRequests
+ - Use Knapsack to evenly distribute tests across multiple nodes
+ - Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
+ - Don't allow MRs to be merged when commits were added since the last review / page load
+ - Add DB index on users.state
+ - Limit email on push diff size to 30 files / 150 KB
+ - Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
+ - Changed the Slack build message to use the singular duration if necessary (Aran Koning)
+ - Fix race condition on merge when build succeeds
+ - Added shortcut to focus filter search fields and added documentation #18120
+ - Links from a wiki page to other wiki pages should be rewritten as expected
+ - Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
+ - Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393
+ - Fix issues filter when ordering by milestone
+ - Disable SAML account unlink feature
+ - Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
+ - Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
+ - TeamCity Service: Fix URL handling when base URL contains a path
+ - Todos will display target state if issuable target is 'Closed' or 'Merged'
+ - Validate only and except regexp
+ - Fix bug when sorting issues by milestone due date and filtering by two or more labels
+ - POST to API /projects/:id/runners/:runner_id would give 409 if the runner was already enabled for this project
+ - Add support for using Yubikeys (U2F) for two-factor authentication
+ - Link to blank group icon doesn't throw a 404 anymore
+ - Remove 'main language' feature
+ - Toggle whitespace button now available for compare branches diffs #17881
+ - Pipelines can be canceled only when there are running builds
+ - Allow authentication using personal access tokens
+ - Use downcased path to container repository as this is expected path by Docker
+ - Allow to use CI token to fetch LFS objects
+ - Custom notification settings
+ - Projects pending deletion will render a 404 page
+ - Measure queue duration between gitlab-workhorse and Rails
+ - Added Gfm autocomplete for labels
+ - Added edit note 'up' shortcut documentation to the help panel and docs screenshot #18114
+ - Make Omniauth providers specs to not modify global configuration
+ - Remove unused JiraIssue class and replace references with ExternalIssue. !4659 (Ilan Shamir)
+ - Make authentication service for Container Registry to be compatible with < Docker 1.11
+ - Make it possible to lock a runner from being enabled for other projects
+ - Add Application Setting to configure Container Registry token expire delay (default 5min)
+ - Cache assigned issue and merge request counts in sidebar nav
+ - Use Knapsack only in CI environment
+ - Updated project creation page to match new UI #2542
+ - Cache project build count in sidebar nav
+ - Add milestone expire date to the right sidebar
+ - Manually mark a issue or merge request as a todo
+ - Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
+ - Reduce number of queries needed to render issue labels in the sidebar
+ - Improve error handling importing projects
+ - Remove duplicated notification settings
+ - Put project Files and Commits tabs under Code tab
+ - Decouple global notification level from user model
+ - Replace Colorize with Rainbow for coloring console output in Rake tasks.
+ - Add workhorse controller and API helpers
+ - An indicator is now displayed at the top of the comment field for confidential issues.
+ - Show categorised search queries in the search autocomplete
+ - RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
+ - Dropdown for `.gitlab-ci.yml` templates
+ - Improve issuables APIs performance when accessing notes !4471
+ - Add sorting dropdown to tags page !4423
+ - External links now open in a new tab
+ - Prevent default actions of disabled buttons and links
+ - Markdown editor now correctly resets the input value on edit cancellation !4175
+ - Toggling a task list item in a issue/mr description does not creates a Todo for mentions
+ - Improved UX of date pickers on issue & milestone forms
+ - Cache on the database if a project has an active external issue tracker.
+ - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
+ - GitLab project import and export functionality
+ - All classes in the Banzai::ReferenceParser namespace are now instrumented
+ - Remove deprecated issues_tracker and issues_tracker_id from project model
+ - Allow users to create confidential issues in private projects
+ - Measure CPU time for instrumented methods
+ - Instrument private methods and private instance methods by default instead just public methods
+ - Only show notes through JSON on confidential issues that the user has access to
+ - Updated the allocations Gem to version 1.0.5
+ - The background sampler now ignores classes without names
+ - Update design for `Close` buttons
+ - New custom icons for navigation
+ - Horizontally scrolling navigation on project, group, and profile settings pages
+ - Hide global side navigation by default
+ - Fix project Star/Unstar project button tooltip
+ - Remove tanuki logo from side navigation; center on top nav
+ - Include user relationships when retrieving award_emoji
+ - Various associations are now eager loaded when parsing issue references to reduce the number of queries executed
+ - Set inverse_of for Project/Service association to reduce the number of queries
+ - Update tanuki logo highlight/loading colors
+ - Remove explicit Gitlab::Metrics.action assignments, are already automatic.
+ - Use Git cached counters for branches and tags on project page
+ - Cache participable participants in an instance variable.
+ - Filter parameters for request_uri value on instrumented transactions.
+ - Remove duplicated keys add UNIQUE index to keys fingerprint column
+ - ExtractsPath get ref_names from repository cache, if not there access git.
+ - Show a flash warning about the error detail of XHR requests which failed with status code 404 and 500
+ - Cache user todo counts from TodoService
+ - Ensure Todos counters doesn't count Todos for projects pending delete
+ - Add left/right arrows horizontal navigation
+ - Add tooltip to pin/unpin navbar
+ - Add new sub nav style to Wiki and Graphs sub navigation
+
+## 8.8.9
+
+ - Upgrade Doorkeeper to 4.2.0. !5881
+
+## 8.8.8
+
+ - Upgrade Rails to 4.2.7.1 for security fixes. !5781
+
+## 8.8.7
+
+ - Fix privilege escalation issue with OAuth external users.
+ - Ensure references to private repos aren't shown to logged-out users.
+
+## 8.8.6
+
+ - Fix visibility of snippets when searching.
+ - Update omniauth-saml to 1.6.0 !4951
+
+## 8.8.5
+
+ - Import GitHub repositories respecting the API rate limit !4166
+ - Fix todos page throwing errors when you have a project pending deletion !4300
+ - Disable Webhooks before proceeding with the GitHub import !4470
+ - Fix importer for GitHub comments on diff !4488
+ - Adjust the SAML control flow to allow LDAP identities to be added to an existing SAML user !4498
+ - Fix incremental trace upload API when using multi-byte UTF-8 chars in trace !4541
+ - Prevent unauthorized access for projects build traces
+ - Forbid scripting for wiki files
+ - Only show notes through JSON on confidential issues that the user has access to
+ - Banzai::Filter::UploadLinkFilter use XPath instead CSS expressions
+ - Banzai::Filter::ExternalLinkFilter use XPath instead CSS expressions
+
+## 8.8.4
+
+ - Fix LDAP-based login for users with 2FA enabled. !4493
+ - Added descriptions to notification settings dropdown
+ - Due date can be removed from milestones
+
+## 8.8.3
+
+ - Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
+ - Fixed JS error when trying to remove discussion form. !4303
+ - Fixed issue with button color when no CI enabled. !4287
+ - Fixed potential issue with 2 CI status polling events happening. !3869
+ - Improve design of Pipeline view. !4230
+ - Fix gitlab importer failing to import new projects due to missing credentials. !4301
+ - Fix import URL migration not rescuing with the correct Error. !4321
+ - Fix health check access token changing due to old application settings being used. !4332
+ - Make authentication service for Container Registry to be compatible with Docker versions before 1.11. !4363
+ - Add Application Setting to configure Container Registry token expire delay (default 5 min). !4364
+ - Pass the "Remember me" value to the 2FA token form. !4369
+ - Fix incorrect links on pipeline page when merge request created from fork. !4376
+ - Use downcased path to container repository as this is expected path by Docker. !4420
+ - Fix wiki project clone address error (chujinjin). !4429
+ - Fix serious performance bug with rendering Markdown with InlineDiffFilter. !4392
+ - Fix missing number on generated ordered list element. !4437
+ - Prevent disclosure of notes on confidential issues in search results.
+
+## 8.8.2
+
+ - Added remove due date button. !4209
+ - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242
+ - Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245
+ - Fix table UI on CI builds page. !4249
+ - Fix backups if registry is disabled. !4263
+ - Fixed issue with merge button color. !4211
+ - Fixed issue with enter key selecting wrong option in dropdown. !4210
+ - When creating a .gitignore file a dropdown with templates will be provided. !4075
+ - Fix concurrent request when updating build log in browser. !4183
+
+## 8.8.1
+
+ - Add documentation for the "Health Check" feature
+ - Allow anonymous users to access a public project's pipelines !4233
+ - Fix MySQL compatibility in zero downtime migrations helpers
+ - Fix the CI login to Container Registry (the gitlab-ci-token user)
+
+## 8.8.0 (2016-05-22)
+
+ - Implement GFM references for milestones (Alejandro Rodríguez)
+ - Snippets tab under user profile. !4001 (Long Nguyen)
+ - Fix error when using link to uploads in global snippets
+ - Fix Error 500 when attempting to retrieve project license when HEAD points to non-existent ref
+ - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
+ - Use a case-insensitive comparison in sanitizing URI schemes
+ - Toggle sign-up confirmation emails in application settings
+ - Make it possible to prevent tagged runner from picking untagged jobs
+ - Added `InlineDiffFilter` to the markdown parser. (Adam Butler)
+ - Added inline diff styling for `change_title` system notes. (Adam Butler)
+ - Project#open_branches has been cleaned up and no longer loads entire records into memory.
+ - Escape HTML in commit titles in system note messages
+ - Improve design of Pipeline View
+ - Fix scope used when accessing container registry
+ - Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios
+ - Improve multiple branch push performance by memoizing permission checking
+ - Log to application.log when an admin starts and stops impersonating a user
+ - Changing the confidentiality of an issue now creates a new system note (Alex Moore-Niemi)
+ - Updated gitlab_git to 10.1.0
+ - GitAccess#protected_tag? no longer loads all tags just to check if a single one exists
+ - Reduce delay in destroying a project from 1-minute to immediately
+ - Make build status canceled if any of the jobs was canceled and none failed
+ - Upgrade Sidekiq to 4.1.2
+ - Added /health_check endpoint for checking service status
+ - Make 'upcoming' filter for milestones work better across projects
+ - Sanitize repo paths in new project error message
+ - Bump mail_room to 0.7.0 to fix stuck IDLE connections
+ - Remove future dates from contribution calendar graph.
+ - Support e-mail notifications for comments on project snippets
+ - Fix API leak of notes of unauthorized issues, snippets and merge requests
+ - Use ActionDispatch Remote IP for Akismet checking
+ - Fix error when visiting commit builds page before build was updated
+ - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
+ - Update SVG sanitizer to conform to SVG 1.1
+ - Speed up push emails with multiple recipients by only generating the email once
+ - Updated search UI
+ - Added authentication service for Container Registry
+ - Display informative message when new milestone is created
+ - Sanitize milestones and labels titles
+ - Support multi-line tag messages. !3833 (Calin Seciu)
+ - Force users to reset their password after an admin changes it
+ - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
+ - Added button to toggle whitespaces changes on diff view
+ - Backport GitHub Enterprise import support from EE
+ - Create tags using Rugged for performance reasons. !3745
+ - Allow guests to set notification level in projects
+ - API: Expose Issue#user_notes_count. !3126 (Anton Popov)
+ - Don't show forks button when user can't view forks
+ - Fix atom feed links and rendering
+ - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718
+ - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes)
+ - Add eager load paths to help prevent dependency load issues in Sidekiq workers. !3724
+ - Added multiple colors for labels in dropdowns when dups happen.
+ - Show commits in the same order as `git log`
+ - Improve description for the Two-factor Authentication sign-in screen. (Connor Shea)
+ - API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
+ - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
+ - Expire repository exists? and has_visible_content? caches after a push if necessary
+ - Fix unintentional filtering bug in Issue/MR sorted by milestone due (Takuya Noguchi)
+ - Fix adding a todo for private group members (Ahmad Sherif)
+ - Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
+ - Total method execution timings are no longer tracked
+ - Allow Admins to remove the Login with buttons for OAuth services and still be able to import !4034. (Andrei Gliga)
+ - Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif)
+ - Hide left sidebar on phone screens to give more space for content
+ - Redesign navigation for profile and group pages
+ - Add counter metrics for rails cache
+ - Import pull requests from GitHub where the source or target branches were removed
+ - All Grape API helpers are now instrumented
+ - Improve Issue formatting for the Slack Service (Jeroen van Baarsen)
+ - Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine)
+ - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs)
+ - When creating a .gitignore file a dropdown with templates will be provided
+ - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
+
+## 8.7.9
+
+ - Fix privilege escalation issue with OAuth external users.
+ - Ensure references to private repos aren't shown to logged-out users.
+
+## 8.7.8
+
+ - Fix visibility of snippets when searching.
+ - Update omniauth-saml to 1.6.0 !4951
+
+## 8.7.7
+
+ - Fix import by `Any Git URL` broken if the URL contains a space
+ - Prevent unauthorized access to other projects build traces
+ - Forbid scripting for wiki files
+ - Only show notes through JSON on confidential issues that the user has access to
+
+## 8.7.6
+
+ - Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
+ - Fix import from GitLab.com to a private instance failure. !4181
+ - Fix external imports not finding the import data. !4106
+ - Fix notification delay when changing status of an issue
+ - Bump Workhorse to 0.7.5 so it can serve raw diffs
+
+## 8.7.5
+
+ - Fix relative links in wiki pages. !4050
+ - Fix always showing build notification message when switching between merge requests !4086
+ - Fix an issue when filtering merge requests with more than one label. !3886
+ - Fix short note for the default scope on build page (Takuya Noguchi)
+
+## 8.7.4
+
+ - Links for Redmine issue references are generated correctly again !4048 (Benedikt Huss)
+ - Fix setting trusted proxies !3970
+ - Fix BitBucket importer bug when throwing exceptions !3941
+ - Use sign out path only if not empty !3989
+ - Running rake gitlab:db:drop_tables now drops tables with cascade !4020
+ - Running rake gitlab:db:drop_tables uses "IF EXISTS" as a precaution !4100
+ - Use a case-insensitive comparison in sanitizing URI schemes
+
+## 8.7.3
+
+ - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented
+ - Merge request widget displays TeamCity build state and code coverage correctly again.
+ - Fix the line code when importing PR review comments from GitHub. !4010
+ - Wikis are now initialized on legacy projects when checking repositories
+ - Remove animate.css in favor of a smaller subset of animations. !3937 (Connor Shea)
+
+## 8.7.2
+
+ - The "New Branch" button is now loaded asynchronously
+ - Fix error 500 when trying to create a wiki page
+ - Updated spacing between notification label and button
+ - Label titles in filters are now escaped properly
+
+## 8.7.1
+
+ - Throttle the update of `project.last_activity_at` to 1 minute. !3848
+ - Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849
+ - Fix license detection to detect all license files, not only known licenses. !3878
+ - Use the `can?` helper instead of `current_user.can?`. !3882
+ - Prevent users from deleting Webhooks via API they do not own
+ - Fix Error 500 due to stale cache when projects are renamed or transferred
+ - Update width of search box to fix Safari bug. !3900 (Jedidiah)
+ - Use the `can?` helper instead of `current_user.can?`
+
+## 8.7.0 (2016-04-22)
+
+ - Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
+ - Fix vulnerability that made it possible to gain access to private labels and milestones
+ - The number of InfluxDB points stored per UDP packet can now be configured
+ - Fix error when cross-project label reference used with non-existent project
+ - Transactions for /internal/allowed now have an "action" tag set
+ - Method instrumentation now uses Module#prepend instead of aliasing methods
+ - Repository.clean_old_archives is now instrumented
+ - Add support for environment variables on a job level in CI configuration file
+ - SQL query counts are now tracked per transaction
+ - The Projects::HousekeepingService class has extra instrumentation
+ - All service classes (those residing in app/services) are now instrumented
+ - Developers can now add custom tags to transactions
+ - Loading of an issue's referenced merge requests and related branches is now done asynchronously
+ - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
+ - Add support to cherry-pick any commit into any branch in the web interface (Minqi Pan)
+ - Project switcher uses new dropdown styling
+ - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
+ - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
+ - Restrict user profiles when public visibility level is restricted.
+ - Add ability set due date to issues, sort and filter issues by due date (Mehmet Beydogan)
+ - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
+ - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
+ - Add setting for customizing the list of trusted proxies !3524
+ - Allow projects to be transfered to a lower visibility level group
+ - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
+ - Improved Markdown rendering performance !3389
+ - Make shared runners text in box configurable
+ - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
+ - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
+ - Expose project badges in project settings
+ - Make /profile/keys/new redirect to /profile/keys for back-compat. !3717
+ - Preserve time notes/comments have been updated at when moving issue
+ - Make HTTP(s) label consistent on clone bar (Stan Hu)
+ - Add support for `after_script`, requires Runner 1.2 (Kamil Trzciński)
+ - Expose label description in API (Mariusz Jachimowicz)
+ - API: Ability to update a group (Robert Schilling)
+ - API: Ability to move issues (Robert Schilling)
+ - Fix Error 500 after renaming a project path (Stan Hu)
+ - Fix a bug whith trailing slash in teamcity_url (Charles May)
+ - Allow back dating on issues when created or updated through the API
+ - Allow back dating on issue notes when created through the API
+ - Propose license template when creating a new LICENSE file
+ - API: Expose /licenses and /licenses/:key
+ - Fix avatar stretching by providing a cropping feature
+ - API: Expose `subscribed` for issues and merge requests (Robert Schilling)
+ - Allow SAML to handle external users based on user's information !3530
+ - Allow Omniauth providers to be marked as `external` !3657
+ - Add endpoints to archive or unarchive a project !3372
+ - Fix a bug whith trailing slash in bamboo_url
+ - Add links to CI setup documentation from project settings and builds pages
+ - Display project members page to all members
+ - Handle nil descriptions in Slack issue messages (Stan Hu)
+ - Add automated repository integrity checks (OFF by default)
+ - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
+ - API: Ability to star and unstar a project (Robert Schilling)
+ - Add default scope to projects to exclude projects pending deletion
+ - Allow to close merge requests which source projects(forks) are deleted.
+ - Ensure empty recipients are rejected in BuildsEmailService
+ - Use rugged to change HEAD in Project#change_head (P.S.V.R)
+ - API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
+ - API: Fix milestone filtering by `iid` (Robert Schilling)
+ - Make before_script and after_script overridable on per-job (Kamil Trzciński)
+ - API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
+ - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
+ - Better errors handling when creating milestones inside groups
+ - Fix high CPU usage when PostReceive receives refs/merge-requests/<id>
+ - Hide `Create a group` help block when creating a new project in a group
+ - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
+ - Allow issues and merge requests to be assigned to the author !2765
+ - Make Ci::Commit to group only similar builds and make it stateful (ref, tag)
+ - Gracefully handle notes on deleted commits in merge requests (Stan Hu)
+ - Decouple membership and notifications
+ - Fix creation of merge requests for orphaned branches (Stan Hu)
+ - API: Ability to retrieve a single tag (Robert Schilling)
+ - While signing up, don't persist the user password across form redisplays
+ - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
+ - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
+ - Fix admin/projects when using visibility levels on search (PotHix)
+ - Build status notifications
+ - Update email confirmation interface
+ - API: Expose user location (Robert Schilling)
+ - API: Do not leak group existence via return code (Robert Schilling)
+ - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
+ - Update number of Todos in the sidebar when it's marked as "Done". !3600
+ - Sanitize branch names created for confidential issues
+ - API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
+ - API: User can leave a project through the API when not master or owner. !3613
+ - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
+ - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
+ - Improved markdown forms
+ - Diff design updates (colors, button styles, etc)
+ - Copying and pasting a diff no longer pastes the line numbers or +/-
+ - Add null check to formData when updating profile content to fix Firefox bug
+ - Disable spellcheck and autocorrect for username field in admin page
+ - Delete tags using Rugged for performance reasons (Robert Schilling)
+ - Add Slack notifications when Wiki is edited (Sebastian Klier)
+ - Diffs load at the correct point when linking from from number
+ - Selected diff rows highlight
+ - Fix emoji categories in the emoji picker
+ - API: Properly display annotated tags for GET /projects/:id/repository/tags (Robert Schilling)
+ - Add encrypted credentials for imported projects and migrate old ones
+ - Properly format all merge request references with ! rather than # !3740 (Ben Bodenmiller)
+ - Author and participants are displayed first on users autocompletion
+ - Show number sign on external issue reference text (Florent Baldino)
+ - Updated print style for issues
+ - Use GitHub Issue/PR number as iid to keep references
+ - Import GitHub labels
+ - Add option to filter by "Owned projects" on dashboard page
+ - Import GitHub milestones
+ - Execute system web hooks on push to the project
+ - Allow enable/disable push events for system hooks
+ - Fix GitHub project's link in the import page when provider has a custom URL
+ - Add RAW build trace output and button on build page
+ - Add incremental build trace update into CI API
+
+## 8.6.9
+
+ - Prevent unauthorized access to other projects build traces
+ - Forbid scripting for wiki files
+ - Only show notes through JSON on confidential issues that the user has access to
+
+## 8.6.8
+
+ - Prevent privilege escalation via "impersonate" feature
+ - Prevent privilege escalation via notes API
+ - Prevent privilege escalation via project webhook API
+ - Prevent XSS via Git branch and tag names
+ - Prevent XSS via custom issue tracker URL
+ - Prevent XSS via `window.opener`
+ - Prevent XSS via label drop-down
+ - Prevent information disclosure via milestone API
+ - Prevent information disclosure via snippet API
+ - Prevent information disclosure via project labels
+ - Prevent information disclosure via new merge request page
+
+## 8.6.7
+
+ - Fix persistent XSS vulnerability in `commit_person_link` helper
+ - Fix persistent XSS vulnerability in Label and Milestone dropdowns
+ - Fix vulnerability that made it possible to enumerate private projects belonging to group
+
+## 8.6.6
+
+ - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
+ - Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
+ - Fix revoking of authorized OAuth applications (Connor Shea). !3690
+ - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
+ - Issuable header is consistent between issues and merge requests
+ - Improved spacing in issuable header on mobile
+
+## 8.6.5
+
+ - Fix importing from GitHub Enterprise. !3529
+ - Perform the language detection after updating merge requests in `GitPushService`, leading to faster visual feedback for the end-user. !3533
+ - Check permissions when user attempts to import members from another project. !3535
+ - Only update repository language if it is not set to improve performance. !3556
+ - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu). !3583
+ - Unblock user when active_directory is disabled and it can be found !3550
+ - Fix a 2FA authentication spoofing vulnerability.
+
+## 8.6.4
+
+ - Don't attempt to fetch any tags from a forked repo (Stan Hu)
+ - Redesign the Labels page
+
+## 8.6.3
+
+ - Mentions on confidential issues doesn't create todos for non-members. !3374
+ - Destroy related todos when an Issue/MR is deleted. !3376
+ - Fix error 500 when target is nil on todo list. !3376
+ - Fix copying uploads when moving issue to another project. !3382
+ - Ensuring Merge Request API returns boolean values for work_in_progress (Abhi Rao). !3432
+ - Fix raw/rendered diff producing different results on merge requests. !3450
+ - Fix commit comment alignment (Stan Hu). !3466
+ - Fix Error 500 when searching for a comment in a project snippet. !3468
+ - Allow temporary email as notification email. !3477
+ - Fix issue with dropdowns not selecting values. !3478
+ - Update gitlab-shell version and doc to 2.6.12. gitlab-org/gitlab-ee!280
+
+## 8.6.2
+
+ - Fix dropdown alignment. !3298
+ - Fix issuable sidebar overlaps on tablet. !3299
+ - Make dropdowns pixel perfect. !3337
+ - Fix order of steps to prevent PostgreSQL errors when running migration. !3355
+ - Fix bold text in issuable sidebar. !3358
+ - Fix error with anonymous token in applications settings. !3362
+ - Fix the milestone 'upcoming' filter. !3364 + !3368
+ - Fix comments on confidential issues showing up in activity feed to non-members. !3375
+ - Fix `NoMethodError` when visiting CI root path at `/ci`. !3377
+ - Add a tooltip to new branch button in issue page. !3380
+ - Fix an issue hiding the password form when signed-in with a linked account. !3381
+ - Add links to CI setup documentation from project settings and builds pages. !3384
+ - Fix an issue with width of project select dropdown. !3386
+ - Remove redundant `require`s from Banzai files. !3391
+ - Fix error 500 with cancel button on issuable edit form. !3392 + !3417
+ - Fix background when editing a highlighted note. !3423
+ - Remove tabstop from the WIP toggle links. !3426
+ - Ensure private project snippets are not viewable by unauthorized people.
+ - Gracefully handle notes on deleted commits in merge requests (Stan Hu). !3402
+ - Fixed issue with notification settings not saving. !3452
+
+## 8.6.1
+
+ - Add option to reload the schema before restoring a database backup. !2807
+ - Display navigation controls on mobile. !3214
+ - Fixed bug where participants would not work correctly on merge requests. !3329
+ - Fix sorting issues by votes on the groups issues page results in SQL errors. !3333
+ - Restrict notifications for confidential issues. !3334
+ - Do not allow to move issue if it has not been persisted. !3340
+ - Add a confirmation step before deleting an issuable. !3341
+ - Fixes issue with signin button overflowing on mobile. !3342
+ - Auto collapses the navigation sidebar when resizing. !3343
+ - Fix build dependencies, when the dependency is a string. !3344
+ - Shows error messages when trying to create label in dropdown menu. !3345
+ - Fixes issue with assign milestone not loading milestone list. !3346
+ - Fix an issue causing the Dashboard/Milestones page to be blank. !3348
+
+## 8.6.0 (2016-03-22)
+
+ - Add ability to move issue to another project
+ - Prevent tokens in the import URL to be showed by the UI
+ - Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
+ - Add confidential issues
+ - Bump gitlab_git to 9.0.3 (Stan Hu)
+ - Fix diff image view modes (2-up, swipe, onion skin) not working (Stan Hu)
+ - Support Golang subpackage fetching (Stan Hu)
+ - Bump Capybara gem to 2.6.2 (Stan Hu)
+ - New branch button appears on issues where applicable
+ - Contributions to forked projects are included in calendar
+ - Improve the formatting for the user page bio (Connor Shea)
+ - Easily (un)mark merge request as WIP using link
+ - Use specialized system notes when MR is (un)marked as WIP
+ - Removed the default password from the initial admin account created during
+ setup. A password can be provided during setup (see installation docs), or
+ GitLab will ask the user to create a new one upon first visit.
+ - Fix issue when pushing to projects ending in .wiki
+ - Properly display YAML front matter in Markdown
+ - Add support for wiki with UTF-8 page names (Hiroyuki Sato)
+ - Fix wiki search results point to raw source (Hiroyuki Sato)
+ - Don't load all of GitLab in mail_room
+ - Add information about `image` and `services` field at `job` level in the `.gitlab-ci.yml` documentation (Pat Turner)
+ - HTTP error pages work independently from location and config (Artem Sidorenko)
+ - Update `omniauth-saml` to 1.5.0 to allow for custom response attributes to be set
+ - Memoize @group in Admin::GroupsController (Yatish Mehta)
+ - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie)
+ - Added omniauth-auth0 Gem (Daniel Carraro)
+ - Add label description in tooltip to labels in issue index and sidebar
+ - Strip leading and trailing spaces in URL validator (evuez)
+ - Add "last_sign_in_at" and "confirmed_at" to GET /users/* API endpoints for admins (evuez)
+ - Return empty array instead of 404 when commit has no statuses in commit status API
+ - Decrease the font size and the padding of the `.anchor` icons used in the README (Roberto Dip)
+ - Rewrite logo to simplify SVG code (Sean Lang)
+ - Allow to use YAML anchors when parsing the `.gitlab-ci.yml` (Pascal Bach)
+ - Ignore jobs that start with `.` (hidden jobs)
+ - Hide builds from project's settings when the feature is disabled
+ - Allow to pass name of created artifacts archive in `.gitlab-ci.yml`
+ - Refactor and greatly improve search performance
+ - Add support for cross-project label references
+ - Ensure "new SSH key" email do not ends up as dead Sidekiq jobs
+ - Update documentation to reflect Guest role not being enforced on internal projects
+ - Allow search for logged out users
+ - Allow to define on which builds the current one depends on
+ - Allow user subscription to a label: get notified for issues/merge requests related to that label (Timothy Andrew)
+ - Fix bug where Bitbucket `closed` issues were imported as `opened` (Iuri de Silvio)
+ - Don't show Issues/MRs from archived projects in Groups view
+ - Fix wrong "iid of max iid" in Issuable sidebar for some merged MRs
+ - Fix empty source_sha on Merge Request when there is no diff (Pierre de La Morinerie)
+ - Increase the notes polling timeout over time (Roberto Dip)
+ - Add shortcut to toggle markdown preview (Florent Baldino)
+ - Show labels in dashboard and group milestone views
+ - Fix an issue when the target branch of a MR had been deleted
+ - Add main language of a project in the list of projects (Tiago Botelho)
+ - Add #upcoming filter to Milestone filter (Tiago Botelho)
+ - Add ability to show archived projects on dashboard, explore and group pages
+ - Remove fork link closes all merge requests opened on source project (Florent Baldino)
+ - Move group activity to separate page
+ - Create external users which are excluded of internal and private projects unless access was explicitly granted
+ - Continue parameters are checked to ensure redirection goes to the same instance
+ - User deletion is now done in the background so the request can not time out
+ - Canceled builds are now ignored in compound build status if marked as `allowed to fail`
+ - Trigger a todo for mentions on commits page
+ - Let project owners and admins soft delete issues and merge requests
+
+## 8.5.13
+
+ - Prevent unauthorized access to other projects build traces
+ - Forbid scripting for wiki files
+
+## 8.5.12
+
+ - Prevent privilege escalation via "impersonate" feature
+ - Prevent privilege escalation via notes API
+ - Prevent privilege escalation via project webhook API
+ - Prevent XSS via Git branch and tag names
+ - Prevent XSS via custom issue tracker URL
+ - Prevent XSS via `window.opener`
+ - Prevent information disclosure via snippet API
+ - Prevent information disclosure via project labels
+ - Prevent information disclosure via new merge request page
+
+## 8.5.11
+
+ - Fix persistent XSS vulnerability in `commit_person_link` helper
+
+## 8.5.10
+
+ - Fix a 2FA authentication spoofing vulnerability.
+
+## 8.5.9
+
+ - Don't attempt to fetch any tags from a forked repo (Stan Hu).
+
+## 8.5.8
+
+ - Bump Git version requirement to 2.7.4
+
+## 8.5.7
+
+ - Bump Git version requirement to 2.7.3
+
+## 8.5.6
+
+ - Obtain a lease before querying LDAP
+
+## 8.5.5
+
+ - Ensure removing a project removes associated Todo entries
+ - Prevent a 500 error in Todos when author was removed
+ - Fix pagination for filtered dashboard and explore pages
+ - Fix "Show all" link behavior
+
+## 8.5.4
+
+ - Do not cache requests for badges (including builds badge)
+
+## 8.5.3
+
+ - Flush repository caches before renaming projects
+ - Sort starred projects on dashboard based on last activity by default
+ - Show commit message in JIRA mention comment
+ - Makes issue page and merge request page usable on mobile browsers.
+ - Improved UI for profile settings
+
+## 8.5.2
+
+ - Fix sidebar overlapping content when screen width was below 1200px
+ - Don't repeat labels listed on Labels tab
+ - Bring the "branded appearance" feature from EE to CE
+ - Fix error 500 when commenting on a commit
+ - Show days remaining instead of elapsed time for Milestone
+ - Fix broken icons on installations with relative URL (Artem Sidorenko)
+ - Fix issue where tag list wasn't refreshed after deleting a tag
+ - Fix import from gitlab.com (KazSawada)
+ - Improve implementation to check read access to forks and add pagination
+ - Don't show any "2FA required" message if it's not actually required
+ - Fix help keyboard shortcut on relative URL setups (Artem Sidorenko)
+ - Update Rails to 4.2.5.2
+ - Fix permissions for deprecated CI build status badge
+ - Don't show "Welcome to GitLab" when the search didn't return any projects
+ - Add Todos documentation
+
+## 8.5.1
+
+ - Fix group projects styles
+ - Show Crowd login tab when sign in is disabled and Crowd is enabled (Peter Hudec)
+ - Fix a set of small UI glitches in project, profile, and wiki pages
+ - Restrict permissions on public/uploads
+ - Fix the merge request side-by-side view after loading diff results
+ - Fix the look of tooltip for the "Revert" button
+ - Add when the Builds & Runners API changes got introduced
+ - Fix error 500 on some merged merge requests
+ - Fix an issue causing the content of the issuable sidebar to disappear
+ - Fix error 500 when trying to mark an already done todo as "done"
+ - Fix an issue where MRs weren't sortable
+ - Issues can now be dragged & dropped into empty milestone lists. This is also
+ possible with MRs
+ - Changed padding & background color for highlighted notes
+ - Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu)
+ - Update sentry-raven gem to 0.15.6
+ - Add build coverage in project's builds page (Steffen Köhler)
+ - Changed # to ! for merge requests in activity view
+
+## 8.5.0 (2016-02-22)
+
+ - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
+ - Cache various Repository methods to improve performance
+ - Fix duplicated branch creation/deletion Webhooks/service notifications when using Web UI (Stan Hu)
+ - Ensure rake tasks that don't need a DB connection can be run without one
+ - Update New Relic gem to 3.14.1.311 (Stan Hu)
+ - Add "visibility" flag to GET /projects api endpoint
+ - Add an option to supply root email through an environmental variable (Koichiro Mikami)
+ - Ignore binary files in code search to prevent Error 500 (Stan Hu)
+ - Render sanitized SVG images (Stan Hu)
+ - Support download access by PRIVATE-TOKEN header (Stan Hu)
+ - Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
+ - Add option to include the sender name in body of Notify email (Jason Lee)
+ - New UI for pagination
+ - Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
+ set it up
+ - API: Added "merge_requests/:merge_request_id/closes_issues" (Gal Schlezinger)
+ - Fix diff comments loaded by AJAX to load comment with diff in discussion tab
+ - Fix relative links in other markup formats (Ben Boeckel)
+ - Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
+ - Fix label links for a merge request pointing to issues list
+ - Don't vendor minified JS
+ - Increase project import timeout to 15 minutes
+ - Be more permissive with email address validation: it only has to contain a single '@'
+ - Display 404 error on group not found
+ - Track project import failure
+ - Support Two-factor Authentication for LDAP users
+ - Display database type and version in Administration dashboard
+ - Allow limited Markdown in Broadcast Messages
+ - Fix visibility level text in admin area (Zeger-Jan van de Weg)
+ - Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
+ - Update the ExternalIssue regex pattern (Blake Hitchcock)
+ - Remember user's inline/side-by-side diff view preference in a cookie (Kirill Katsnelson)
+ - Optimized performance of finding issues to be closed by a merge request
+ - Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`, `path_with_namespace`
+ and `default_branch` in `project` in push, issue, merge-request and note webhooks data (Kirill Zaitsev)
+ - Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in favor of `git_http_url`
+ in `project` for push, issue, merge-request and note webhooks data (Kirill Zaitsev)
+ - Deprecate the `repository` key in push, issue, merge-request and note webhooks data, use `project` instead (Kirill Zaitsev)
+ - API: Expose MergeRequest#merge_status (Andrei Dziahel)
+ - Revert "Add IP check against DNSBLs at account sign-up"
+ - Actually use the `skip_merges` option in Repository#commits (Tony Chu)
+ - Fix API to keep request parameters in Link header (Michael Potthoff)
+ - Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
+ - Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
+ - Prevent parse error when name of project ends with .atom and prevent path issues
+ - Discover branches for commit statuses ref-less when doing merge when succeeded
+ - Mark inline difference between old and new paths when a file is renamed
+ - Support Akismet spam checking for creation of issues via API (Stan Hu)
+ - API: Allow to set or update a merge-request's milestone (Kirill Skachkov)
+ - Improve UI consistency between projects and groups lists
+ - Add sort dropdown to dashboard projects page
+ - Fixed logo animation on Safari (Roman Rott)
+ - Fix Merge When Succeeded when multiple stages
+ - Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
+ - In seach autocomplete show only groups and projects you are member of
+ - Don't process cross-reference notes from forks
+ - Fix: init.d script not working on OS X
+ - Faster snippet search
+ - Added API to download build artifacts
+ - Title for milestones should be unique (Zeger-Jan van de Weg)
+ - Validate correctness of maximum attachment size application setting
+ - Replaces "Create merge request" link with one to the "Merge Request" when one exists
+ - Fix CI builds badge, add a new link to builds badge, deprecate the old one
+ - Fix broken link to project in build notification emails
+ - Ability to see and sort on vote count from Issues and MR lists
+ - Fix builds scheduler when first build in stage was allowed to fail
+ - User project limit is reached notice is hidden if the projects limit is zero
+ - Add API support for managing runners and project's runners
+ - Allow SAML users to login with no previous account without having to allow
+ all Omniauth providers to do so.
+ - Allow existing users to auto link their SAML credentials by logging in via SAML
+ - Make it possible to erase a build (trace, artifacts) using UI and API
+ - Ability to revert changes from a Merge Request or Commit
+ - Emoji comment on diffs are not award emoji
+ - Add label description (Nuttanart Pornprasitsakul)
+ - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
+ - Add Todos
+
+## 8.4.11
+
+ - Prevent unauthorized access to other projects build traces
+ - Forbid scripting for wiki files
+
+## 8.4.10
+
+ - Prevent privilege escalation via "impersonate" feature
+ - Prevent privilege escalation via notes API
+ - Prevent privilege escalation via project webhook API
+ - Prevent XSS via Git branch and tag names
+ - Prevent XSS via custom issue tracker URL
+ - Prevent XSS via `window.opener`
+ - Prevent information disclosure via snippet API
+ - Prevent information disclosure via project labels
+ - Prevent information disclosure via new merge request page
+
+## 8.4.9
+
+ - Fix persistent XSS vulnerability in `commit_person_link` helper
+
+## 8.4.8
+
+ - Fix a 2FA authentication spoofing vulnerability.
+
+## 8.4.7
+
+ - Don't attempt to fetch any tags from a forked repo (Stan Hu).
+
+## 8.4.6
+
+ - Bump Git version requirement to 2.7.4
+
+## 8.4.5
+
+ - No CE-specific changes
+
+## 8.4.4
+
+ - Update omniauth-saml gem to 1.4.2
+ - Prevent long-running backup tasks from timing out the database connection
+ - Add a Project setting to allow guests to view build logs (defaults to true)
+ - Sort project milestones by due date including issue editor (Oliver Rogers / Orih)
+
+## 8.4.3
+
+ - Increase lfs_objects size column to 8-byte integer to allow files larger
+ than 2.1GB
+ - Correctly highlight MR diff when MR has merge conflicts
+ - Fix highlighting in blame view
+ - Update sentry-raven gem to prevent "Not a git repository" console output
+ when running certain commands
+ - Add instrumentation to additional Gitlab::Git and Rugged methods for
+ performance monitoring
+ - Allow autosize textareas to also be manually resized
+
+## 8.4.2
+
+ - Bump required gitlab-workhorse version to bring in a fix for missing
+ artifacts in the build artifacts browser
+ - Get rid of those ugly borders on the file tree view
+ - Fix updating the runner information when asking for builds
+ - Bump gitlab_git version to 7.2.24 in order to bring in a performance
+ improvement when checking if a repository was empty
+ - Add instrumentation for Gitlab::Git::Repository instance methods so we can
+ track them in Performance Monitoring.
+ - Increase contrast between highlighted code comments and inline diff marker
+ - Fix method undefined when using external commit status in builds
+ - Fix highlighting in blame view.
+
+## 8.4.1
+
+ - Apply security updates for Rails (4.2.5.1), rails-html-sanitizer (1.0.3),
+ and Nokogiri (1.6.7.2)
+ - Fix redirect loop during import
+ - Fix diff highlighting for all syntax themes
+ - Delete project and associations in a background worker
+
+## 8.4.0 (2016-01-22)
+
+ - Allow LDAP users to change their email if it was not set by the LDAP server
+ - Ensure Gravatar host looks like an actual host
+ - Consider re-assign as a mention from a notification point of view
+ - Add pagination headers to already paginated API resources
+ - Properly generate diff of orphan commits, like the first commit in a repository
+ - Improve the consistency of commit titles, branch names, tag names, issue/MR titles, on their respective project pages
+ - Autocomplete data is now always loaded, instead of when focusing a comment text area
+ - Improved performance of finding issues for an entire group
+ - Added custom application performance measuring system powered by InfluxDB
+ - Add syntax highlighting to diffs
+ - Gracefully handle invalid UTF-8 sequences in Markdown links (Stan Hu)
+ - Bump fog to 1.36.0 (Stan Hu)
+ - Add user's last used IP addresses to admin page (Stan Hu)
+ - Add housekeeping function to project settings page
+ - The default GitLab logo now acts as a loading indicator
+ - Fix caching issue where build status was not updating in project dashboard (Stan Hu)
+ - Accept 2xx status codes for successful Webhook triggers (Stan Hu)
+ - Fix missing date of month in network graph when commits span a month (Stan Hu)
+ - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
+ - Don't notify users twice if they are both project watchers and subscribers (Stan Hu)
+ - Remove gray background from layout in UI
+ - Fix signup for OAuth providers that don't provide a name
+ - Implement new UI for group page
+ - Implement search inside emoji picker
+ - Let the CI runner know about builds that this build depends on
+ - Add API support for looking up a user by username (Stan Hu)
+ - Add project permissions to all project API endpoints (Stan Hu)
+ - Link to milestone in "Milestone changed" system note
+ - Only allow group/project members to mention `@all`
+ - Expose Git's version in the admin area (Trey Davis)
+ - Add "Frequently used" category to emoji picker
+ - Add CAS support (tduehr)
+ - Add link to merge request on build detail page
+ - Fix: Problem with projects ending with .keys (Jose Corcuera)
+ - Revert back upvote and downvote button to the issue and MR pages
+ - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg)
+ - Add system hook messages for project rename and transfer (Steve Norman)
+ - Fix version check image in Safari
+ - Show 'All' tab by default in the builds page
+ - Add Open Graph and Twitter Card data to all pages
+ - Fix API project lookups when querying with a namespace with dots (Stan Hu)
+ - Enable forcing Two-factor authentication sitewide, with optional grace period
+ - Import GitHub Pull Requests into GitLab
+ - Change single user API endpoint to return more detailed data (Michael Potthoff)
+ - Update version check images to use SVG
+ - Validate README format before displaying
+ - Enable Microsoft Azure OAuth2 support (Janis Meybohm)
+ - Properly set task-list class on single item task lists
+ - Add file finder feature in tree view (Kyungchul Shin)
+ - Ajax filter by message for commits page
+ - API: Add support for deleting a tag via the API (Robert Schilling)
+ - Allow subsequent validations in CI Linter
+ - Show referenced MRs & Issues only when the current viewer can access them
+ - Fix Encoding::CompatibilityError bug when markdown content has some complex URL (Jason Lee)
+ - Add API support for managing project's builds
+ - Add API support for managing project's build triggers
+ - Add API support for managing project's build variables
+ - Allow broadcast messages to be edited
+ - Autosize Markdown textareas
+ - Import GitHub wiki into GitLab
+ - Add reporters ability to download and browse build artifacts (Andrew Johnson)
+ - Autofill referring url in message box when reporting user abuse.
+ - Remove leading comma on award emoji when the user is the first to award the emoji (Zeger-Jan van de Weg)
+ - Add build artifacts browser
+ - Improve UX in builds artifacts browser
+ - Increase default size of `data` column in `events` table when using MySQL
+ - Expose button to CI Lint tool on project builds page
+ - Fix: Creator should be added as a master of the project on creation
+ - Added X-GitLab-... headers to emails from CI and Email On Push services (Anton Baklanov)
+ - Add IP check against DNSBLs at account sign-up
+ - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
+
+## 8.3.10
+
+ - Prevent unauthorized access to other projects build traces
+ - Forbid scripting for wiki files
+
+## 8.3.9
+
+ - Prevent privilege escalation via "impersonate" feature
+ - Prevent privilege escalation via notes API
+ - Prevent privilege escalation via project webhook API
+ - Prevent XSS via custom issue tracker URL
+ - Prevent XSS via `window.opener`
+ - Prevent information disclosure via project labels
+ - Prevent information disclosure via new merge request page
+
+## 8.3.8
+
+ - Fix persistent XSS vulnerability in `commit_person_link` helper
+
+## 8.3.7
+
+ - Fix a 2FA authentication spoofing vulnerability.
+
+## 8.3.6
+
+ - Don't attempt to fetch any tags from a forked repo (Stan Hu).
+
+## 8.3.5
+
+ - Bump Git version requirement to 2.7.4
+
+## 8.3.4
+
+ - Use gitlab-workhorse 0.5.4 (fixes API routing bug)
+
+## 8.3.3
+
+ - Preserve CE behavior with JIRA integration by only calling API if URL is set
+ - Fix duplicated branch creation/deletion events when using Web UI (Stan Hu)
+ - Add configurable LDAP server query timeout
+ - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running
+ - Suppress e-mails on failed builds if allow_failure is set (Stan Hu)
+ - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu)
+ - Better support for referencing and closing issues in Asana service (Mike Wyatt)
+ - Enable "Add key" button when user fills in a proper key (Stan Hu)
+ - Fix error in processing reply-by-email messages (Jason Lee)
+ - Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu)
+ - Use WOFF versions of SourceSansPro fonts
+ - Fix regression when builds were not generated for tags created through web/api interface
+ - Fix: maintain milestone filter between Open and Closed tabs (Greg Smethells)
+ - Fix missing artifacts and build traces for build created before 8.3
+
+## 8.3.2
+
+ - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
+ - Add support for Google reCAPTCHA in user registration
+
+## 8.3.1
+
+ - Fix Error 500 when global milestones have slashes (Stan Hu)
+ - Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
+ - Fix LDAP identity and user retrieval when special characters are used
+ - Move Sidekiq-cron configuration to gitlab.yml
+
+## 8.3.0 (2015-12-22)
+
+ - Bump rack-attack to 4.3.1 for security fix (Stan Hu)
+ - API support for starred projects for authorized user (Zeger-Jan van de Weg)
+ - Add open_issues_count to project API (Stan Hu)
+ - Expand character set of usernames created by Omniauth (Corey Hinshaw)
+ - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
+ - Add unsubscribe link in the email footer (Zeger-Jan van de Weg)
+ - Provide better diagnostic message upon project creation errors (Stan Hu)
+ - Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
+ - Remove api credentials from link to build_page
+ - Deprecate GitLabCiService making it to always be inactive
+ - Bump gollum-lib to 4.1.0 (Stan Hu)
+ - Fix broken group avatar upload under "New group" (Stan Hu)
+ - Update project repositorize size and commit count during import:repos task (Stan Hu)
+ - Fix API setting of 'public' attribute to false will make a project private (Stan Hu)
+ - Handle and report SSL errors in Webhook test (Stan Hu)
+ - Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu)
+ - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
+ - WIP identifier on merge requests no longer requires trailing space
+ - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
+ - Fix 500 error when update group member permission
+ - Fix: As an admin, cannot add oneself as a member to a group/project
+ - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
+ - Recognize issue/MR/snippet/commit links as references
+ - Backport JIRA features from EE to CE
+ - Add ignore whitespace change option to commit view
+ - Fire update hook from GitLab
+ - Allow account unlock via email
+ - Style warning about mentioning many people in a comment
+ - Fix: sort milestones by due date once again (Greg Smethells)
+ - Migrate all CI::Services and CI::WebHooks to Services and WebHooks
+ - Don't show project fork event as "imported"
+ - Add API endpoint to fetch merge request commits list
+ - Don't create CI status for refs that doesn't have .gitlab-ci.yml, even if the builds are enabled
+ - Expose events API with comment information and author info
+ - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
+ - Run custom Git hooks when branch is created or deleted.
+ - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch
+ - Add languages page to graphs
+ - Block LDAP user when they are no longer found in the LDAP server
+ - Improve wording on project visibility levels (Zeger-Jan van de Weg)
+ - Fix editing notes on a merge request diff
+ - Automatically select default clone protocol based on user preferences (Eirik Lygre)
+ - Make Network page as sub tab of Commits
+ - Add copy-to-clipboard button for Snippets
+ - Add indication to merge request list item that MR cannot be merged automatically
+ - Default target branch to patch-n when editing file in protected branch
+ - Add Builds tab to merge request detail page
+ - Allow milestones, issues and MRs to be created from dashboard and group indexes
+ - Use new style for wiki
+ - Use new style for milestone detail page
+ - Fix sidebar tooltips when collapsed
+ - Prevent possible XSS attack with award-emoji
+ - Upgraded Sidekiq to 4.x
+ - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg)
+ - Fix emoji aliases problem
+ - Fix award-emojis Flash alert's width
+ - Fix deleting notes on a merge request diff
+ - Display referenced merge request statuses in the issue description (Greg Smethells)
+ - Implement new sidebar for issue and merge request pages
+ - Emoji picker improvements
+ - Suppress warning about missing `.gitlab-ci.yml` if builds are disabled
+ - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
+ - Persist runners registration token in database
+ - Fix online editor should not remove newlines at the end of the file
+ - Expose Git's version in the admin area
+ - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
+
+## 8.2.6
+
+ - Prevent unauthorized access to other projects build traces
+ - Forbid scripting for wiki files
+
+## 8.2.5
+
+ - Prevent privilege escalation via "impersonate" feature
+ - Prevent privilege escalation via notes API
+ - Prevent privilege escalation via project webhook API
+ - Prevent XSS via `window.opener`
+ - Prevent information disclosure via project labels
+ - Prevent information disclosure via new merge request page
+
+## 8.2.4
+
+ - Bump Git version requirement to 2.7.4
+
+## 8.2.3
+
+ - Fix application settings cache not expiring after changes (Stan Hu)
+ - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
+ - Update documentation for "Guest" permissions
+ - Properly convert Emoji-only comments into Award Emojis
+ - Enable devise paranoid mode to prevent user enumeration attack
+ - Webhook payload has an added, modified and removed properties for each commit
+ - Fix 500 error when creating a merge request that removes a submodule
+
+## 8.2.2
+
+ - Fix 404 in redirection after removing a project (Stan Hu)
+ - Ensure cached application settings are refreshed at startup (Stan Hu)
+ - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
+ - Fix: Raw private snippets access workflow
+ - Prevent "413 Request entity too large" errors when pushing large files with LFS
+ - Fix invalid links within projects dashboard header
+ - Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
+ - Fix: duplicate email notifications on issue comments
+
+## 8.2.1
+
+ - Forcefully update builds that didn't want to update with state machine
+ - Fix: saving GitLabCiService as Admin Template
+
+## 8.2.0 (2015-11-22)
+
+ - Improved performance of finding projects and groups in various places
+ - Improved performance of rendering user profile pages and Atom feeds
+ - Expose build artifacts path as config option
+ - Fix grouping of contributors by email in graph.
+ - Improved performance of finding issues with/without labels
+ - Fix Drone CI service template not saving properly (Stan Hu)
+ - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu)
+ - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
+ - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
+ - Improved performance of finding users by one of their Email addresses
+ - Add allow_failure field to commit status API (Stan Hu)
+ - Commits without .gitlab-ci.yml are marked as skipped
+ - Save detailed error when YAML syntax is invalid
+ - Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml
+ - Added build artifacts
+ - Improved performance of replacing references in comments
+ - Show last project commit to default branch on project home page
+ - Highlight comment based on anchor in URL
+ - Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
+ - Improved performance of sorting milestone issues
+ - Allow users to select the Files view as default project view (Cristian Bica)
+ - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy)
+ - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
+ - Use git follow flag for commits page when retrieve history for file or directory
+ - Show merge request CI status on merge requests index page
+ - Send build name and stage in CI notification e-mail
+ - Extend yml syntax for only and except to support specifying repository path
+ - Enable shared runners to all new projects
+ - Bump GitLab-Workhorse to 0.4.1
+ - Allow to define cache in `.gitlab-ci.yml`
+ - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
+ - Remove deprecated CI events from project settings page
+ - Use issue editor as cross reference comment author when issue is edited with a new mention.
+ - Add graphs of commits ahead and behind default branch (Jeff Stubler)
+ - Improve personal snippet access workflow (Douglas Alexandre)
+ - [API] Add ability to fetch the commit ID of the last commit that actually touched a file
+ - Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
+ - Add "New file" link to dropdown on project page
+ - Include commit logs in project search
+ - Add "added", "modified" and "removed" properties to commit object in webhook
+ - Rename "Back to" links to "Go to" because its not always a case it point to place user come from
+ - Allow groups to appear in the search results if the group owner allows it
+ - Add email notification to former assignee upon unassignment (Adam Lieskovský)
+ - New design for project graphs page
+ - Remove deprecated dumped yaml file generated from previous job definitions
+ - Show specific runners from projects where user is master or owner
+ - MR target branch is now visible on a list view when it is different from project's default one
+ - Improve Continuous Integration graphs page
+ - Make color of "Accept Merge Request" button consistent with current build status
+ - Add ignore white space option in merge request diff and commit and compare view
+ - Ability to add release notes (markdown text and attachments) to git tags (aka Releases)
+ - Relative links from a repositories README.md now link to the default branch
+ - Fix trailing whitespace issue in merge request/issue title
+ - Fix bug when milestone/label filter was empty for dashboard issues page
+ - Add ability to create milestone in group projects from single form
+ - Add option to create merge request when editing/creating a file (Dirceu Tiegs)
+ - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
+ - Add Award Emoji to issue and merge request pages
+
+## 8.1.4
+
+ - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
+ - Prevent redirect loop when home_page_url is set to the root URL
+ - Fix incoming email config defaults
+ - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
+
+## 8.1.3
+
+ - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
+ - Spread out runner contacted_at updates
+ - Use issue editor as cross reference comment author when issue is edited with a new mention
+ - Add Facebook authentication
+
+## 8.1.2
+
+ - Fix cloning Wiki repositories via HTTP (Stan Hu)
+ - Add migration to remove satellites directory
+ - Fix specific runners visibility
+ - Fix 500 when editing CI service
+ - Require CI jobs to be named
+ - Fix CSS for runner status
+ - Fix CI badge
+ - Allow developer to manage builds
+
+## 8.1.1
+
+ - Removed, see 8.1.2
+
+## 8.1.0 (2015-10-22)
+
+ - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
+ - Fix duplicate repositories in GitHub import page (Stan Hu)
+ - Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
+ - Adds ability to create directories using the web editor (Ben Ford)
+ - Cleanup stuck CI builds
+ - Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
+ - Show notifications button when user is member of group rather than project (Grzegorz Bizon)
+ - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
+ - Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
+ - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu)
+ - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
+ - Speed up load times of issue detail pages by roughly 1.5x
+ - Fix CI rendering regressions
+ - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
+ - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
+ - Make diff file view easier to use on mobile screens (Stan Hu)
+ - Improved performance of finding users by username or Email address
+ - Fix bug where merge request comments created by API would not trigger notifications (Stan Hu)
+ - Add support for creating directories from Files page (Stan Hu)
+ - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
+ - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
+ - Improved performance of the trending projects page
+ - Remove CI migration task
+ - Improved performance of finding projects by their namespace
+ - Add assignee data to Issuables' hook_data (Bram Daams)
+ - Fix bug where transferring a project would result in stale commit links (Stan Hu)
+ - Fix build trace updating
+ - Include full path of source and target branch names in New Merge Request page (Stan Hu)
+ - Add user preference to view activities as default dashboard (Stan Hu)
+ - Add option to admin area to sign in as a specific user (Pavel Forkert)
+ - Show CI status on all pages where commits list is rendered
+ - Automatically enable CI when push .gitlab-ci.yml file to repository
+ - Move CI charts to project graphs area
+ - Fix cases where Markdown did not render links in activity feed (Stan Hu)
+ - Add first and last to pagination (Zeger-Jan van de Weg)
+ - Added Commit Status API
+ - Added Builds View
+ - Added when to .gitlab-ci.yml
+ - Show CI status on commit page
+ - Added CI_BUILD_TAG, _STAGE, _NAME and _TRIGGERED to CI builds
+ - Show CI status on Your projects page and Starred projects page
+ - Remove "Continuous Integration" page from dashboard
+ - Add notes and SSL verification entries to hook APIs (Ben Boeckel)
+ - Fix grammar in admin area "labels" .nothing-here-block when no labels exist.
+ - Move CI runners page to project settings area
+ - Move CI variables page to project settings area
+ - Move CI triggers page to project settings area
+ - Move CI project settings page to CE project settings area
+ - Fix bug when removed file was not appearing in merge request diff
+ - Show warning when build cannot be served by any of the available CI runners
+ - Note the original location of a moved project when notifying users of the move
+ - Improve error message when merging fails
+ - Add support of multibyte characters in LDAP UID (Roman Petrov)
+ - Show additions/deletions stats on merge request diff
+ - Remove footer text in emails (Zeger-Jan van de Weg)
+ - Ensure code blocks are properly highlighted after a note is updated
+ - Fix wrong access level badge on MR comments
+ - Hide password in the service settings form
+ - Move CI webhooks page to project settings area
+ - Fix User Identities API. It now allows you to properly create or update user's identities.
+ - Add user preference to change layout width (Peter Göbel)
+ - Use commit status in merge request widget as preferred source of CI status
+ - Integrate CI commit and build pages into project pages
+ - Move CI services page to project settings area
+ - Add "Quick Submit" behavior to input fields throughout the application. Use
+ Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux.
+ - Fix position of hamburger in header for smaller screens (Han Loong Liauw)
+ - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
+ - Persist filters when sorting on admin user page (Jerry Lukins)
+ - Update style of snippets pages (Han Loong Liauw)
+ - Allow dashboard and group issues/MRs to be filtered by label
+ - Add spellcheck=false to certain input fields
+ - Invalidate stored service password if the endpoint URL is changed
+ - Project names are not fully shown if group name is too big, even on group page view
+ - Apply new design for Files page
+ - Add "New Page" button to Wiki Pages tab (Stan Hu)
+ - Only render 404 page from /public
+ - Hide passwords from services API (Alex Lossent)
+ - Fix: Images cannot show when projects' path was changed
+ - Let gitlab-git-http-server generate and serve 'git archive' downloads
+ - Optimize query when filtering on issuables (Zeger-Jan van de Weg)
+ - Fix padding of outdated discussion item.
+ - Animate the logo on hover
+
+## 8.0.5
+
+ - Correct lookup-by-email for LDAP logins
+ - Fix loading spinner sometimes not being hidden on Merge Request tab switches
+
+## 8.0.4
+
+ - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
+ - Fix referrals for :back and relative URL installs
+ - Fix anchors to comments in diffs
+ - Remove CI token from build traces
+ - Fix "Assign All" button on Runner admin page
+ - Fix search in Files
+ - Add full project namespace to payload of system webhooks (Ricardo Band)
+
+## 8.0.3
+
+ - Fix URL shown in Slack notifications
+ - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
+ - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
+ - Add work_in_progress key to MR webhooks (Ben Boeckel)
+
+## 8.0.2
+
+ - Fix default avatar not rendering in network graph (Stan Hu)
+ - Skip check_initd_configured_correctly on omnibus installs
+ - Prevent double-prefixing of help page paths
+ - Clarify confirmation text on user deletion
+ - Make commit graphs responsive to window width changes (Stan Hu)
+ - Fix top margin for sign-in button on public pages
+ - Fix LDAP attribute mapping
+ - Remove git refs used internally by GitLab from network graph (Stan Hu)
+ - Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu)
+ - Fix Reply by email for non-UTF-8 messages.
+ - Add option to use StartTLS with Reply by email IMAP server.
+ - Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
+
+## 8.0.1
+
+ - Improve CI migration procedure and documentation
+
+## 8.0.0 (2015-09-22)
+
+ - Fix Markdown links not showing up in dashboard activity feed (Stan Hu)
+ - Remove milestones from merge requests when milestones are deleted (Stan Hu)
+ - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu)
+ - Fix broken sort in merge request API (Stan Hu)
+ - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu)
+ - Gracefully handle errors in syntax highlighting by leaving the block unformatted (Stan Hu)
+ - Add "replace" and "upload" functionalities to allow user replace existing file and upload new file into current repository
+ - Fix URL construction for merge requests, issues, notes, and commits for relative URL config (Stan Hu)
+ - Fix emoji URLs in Markdown when relative_url_root is used (Stan Hu)
+ - Omit filename in Content-Disposition header in raw file download to avoid RFC 6266 encoding issues (Stan HU)
+ - Fix broken Wiki Page History (Stan Hu)
+ - Import forked repositories asynchronously to prevent large repositories from timing out (Stan Hu)
+ - Prevent anchors from being hidden by header (Stan Hu)
+ - Fix bug where only the first 15 Bitbucket issues would be imported (Stan Hu)
+ - Sort issues by creation date in Bitbucket importer (Stan Hu)
+ - Prevent too many redirects upon login when home page URL is set to external_url (Stan Hu)
+ - Improve dropdown positioning on the project home page (Hannes Rosenögger)
+ - Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibilty mode (Stan Hu)
+ - Remove user OAuth tokens from the database and request new tokens each session (Stan Hu)
+ - Restrict users API endpoints to use integer IDs (Stan Hu)
+ - Only show recent push event if the branch still exists or a recent merge request has not been created (Stan Hu)
+ - Remove satellites
+ - Better performance for web editor (switched from satellites to rugged)
+ - Faster merge
+ - Ability to fetch merge requests from refs/merge-requests/:id
+ - Allow displaying of archived projects in the admin interface (Artem Sidorenko)
+ - Allow configuration of import sources for new projects (Artem Sidorenko)
+ - Search for comments should be case insensetive
+ - Create cross-reference for closing references on commits pushed to non-default branches (Maël Valais)
+ - Ability to search milestones
+ - Gracefully handle SMTP user input errors (e.g. incorrect email addresses) to prevent Sidekiq retries (Stan Hu)
+ - Move dashboard activity to separate page (for your projects and starred projects)
+ - Improve performance of git blame
+ - Limit content width to 1200px for most of pages to improve readability on big screens
+ - Fix 500 error when submit project snippet without body
+ - Improve search page usability
+ - Bring more UI consistency in way how projects, snippets and groups lists are rendered
+ - Make all profiles and group public
+ - Fixed login failure when extern_uid changes (Joel Koglin)
+ - Don't notify users without access to the project when they are (accidentally) mentioned in a note.
+ - Retrieving oauth token with LDAP credentials
+ - Load Application settings from running database unless env var USE_DB=false
+ - Added Drone CI integration (Kirill Zaitsev)
+ - Allow developers to retry builds
+ - Hide advanced project options for non-admin users
+ - Fail builds if no .gitlab-ci.yml is found
+ - Refactored service API and added automatically service docs generator (Kirill Zaitsev)
+ - Added web_url key project hook_attrs (Kirill Zaitsev)
+ - Add ability to get user information by ID of an SSH key via the API
+ - Fix bug which IE cannot show image at markdown when the image is raw file of gitlab
+ - Add support for Crowd
+ - Global Labels that are available to all projects
+ - Fix highlighting of deleted lines in diffs.
+ - Project notification level can be set on the project page itself
+ - Added service API endpoint to retrieve service parameters (Petheő Bence)
+ - Add FogBugz project import (Jared Szechy)
+ - Sort users autocomplete lists by user (Allister Antosik)
+ - Webhook for issue now contains repository field (Jungkook Park)
+ - Add ability to add custom text to the help page (Jeroen van Baarsen)
+ - Add pg_schema to backup config
+ - Fix references to target project issues in Merge Requests markdown preview and textareas (Francesco Levorato)
+ - Redirect from incorrectly cased group or project path to correct one (Francesco Levorato)
+ - Removed API calls from CE to CI
+
## 7.14.3
- No changes
diff --git a/changelogs/unreleased/13695-order-contributors-in-api.yml b/changelogs/unreleased/13695-order-contributors-in-api.yml
deleted file mode 100644
index 26bf8650a4a..00000000000
--- a/changelogs/unreleased/13695-order-contributors-in-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds ordering to projects contributors in API
-merge_request: 15469
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/15832-fix-access-level-update-for-requesters.yml b/changelogs/unreleased/15832-fix-access-level-update-for-requesters.yml
deleted file mode 100644
index 9d6c958cb3e..00000000000
--- a/changelogs/unreleased/15832-fix-access-level-update-for-requesters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix error that was preventing users to change the access level of access requests for Groups or Projects
-merge_request: 15832
-author:
-type: fixed
diff --git a/changelogs/unreleased/15922-validate-file-status-when-commiting-multiple-files.yml b/changelogs/unreleased/15922-validate-file-status-when-commiting-multiple-files.yml
deleted file mode 100644
index db2bd6e692b..00000000000
--- a/changelogs/unreleased/15922-validate-file-status-when-commiting-multiple-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Validate file status when commiting multiple files'
-merge_request: 15922
-author:
-type: added
diff --git a/changelogs/unreleased/15955-improve-search-query.yml b/changelogs/unreleased/15955-improve-search-query.yml
deleted file mode 100644
index 80cb8af617f..00000000000
--- a/changelogs/unreleased/15955-improve-search-query.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve search query for merge requests.
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/16036-ignore-lost-found-folder-during-backup-on-a-volume.yml b/changelogs/unreleased/16036-ignore-lost-found-folder-during-backup-on-a-volume.yml
deleted file mode 100644
index 833650559a3..00000000000
--- a/changelogs/unreleased/16036-ignore-lost-found-folder-during-backup-on-a-volume.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Ignore lost+found folder during backup on a volume"
-merge_request: 16036
-author: Julien Millau
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/16117-improve-search-for-issues.yml b/changelogs/unreleased/16117-improve-search-for-issues.yml
deleted file mode 100644
index 92d5820ddd2..00000000000
--- a/changelogs/unreleased/16117-improve-search-for-issues.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve search query for issues.
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/16468-add-fast-blank.yml b/changelogs/unreleased/16468-add-fast-blank.yml
new file mode 100644
index 00000000000..ef68888ae33
--- /dev/null
+++ b/changelogs/unreleased/16468-add-fast-blank.yml
@@ -0,0 +1,5 @@
+---
+title: "Add fast-blank"
+merge_request: 16468
+author:
+type: performance
diff --git a/changelogs/unreleased/20035-pause-resume-runners.yml b/changelogs/unreleased/20035-pause-resume-runners.yml
deleted file mode 100644
index 98757e60683..00000000000
--- a/changelogs/unreleased/20035-pause-resume-runners.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add pause/resume button to project runners
-merge_request: 16032
-author: Mario de la Ossa
-type: added
diff --git a/changelogs/unreleased/24035-api-create-application.yml b/changelogs/unreleased/24035-api-create-application.yml
new file mode 100644
index 00000000000..c583a020d9d
--- /dev/null
+++ b/changelogs/unreleased/24035-api-create-application.yml
@@ -0,0 +1,4 @@
+---
+title: Add application create API
+merge_request: 8160
+author: Nicolas Merelli @PNSalocin
diff --git a/changelogs/unreleased/24347-dont-post-system-note-when-branch-creation-fails.yml b/changelogs/unreleased/24347-dont-post-system-note-when-branch-creation-fails.yml
deleted file mode 100644
index 61153ad4f1a..00000000000
--- a/changelogs/unreleased/24347-dont-post-system-note-when-branch-creation-fails.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix when branch creation fails don't post system note
-merge_request:
-author: Mateusz Bajorski
-type: fixed
diff --git a/changelogs/unreleased/25317-prioritize-author-date-over-commit.yml b/changelogs/unreleased/25317-prioritize-author-date-over-commit.yml
deleted file mode 100644
index a5f6d316a7d..00000000000
--- a/changelogs/unreleased/25317-prioritize-author-date-over-commit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show authored date rather than committed date on the commit list
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/26296-update-styling-disabled-buttons.yml b/changelogs/unreleased/26296-update-styling-disabled-buttons.yml
new file mode 100644
index 00000000000..5fa109d75e0
--- /dev/null
+++ b/changelogs/unreleased/26296-update-styling-disabled-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Set standard disabled state for all buttons
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/28004-consider-refactoring-member-view-by-using-presenter.yml b/changelogs/unreleased/28004-consider-refactoring-member-view-by-using-presenter.yml
deleted file mode 100644
index 0e91d4ae403..00000000000
--- a/changelogs/unreleased/28004-consider-refactoring-member-view-by-using-presenter.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Refactor member view using a Presenter
-merge_request: 9645
-author: TM Lee
diff --git a/changelogs/unreleased/31995-project-limit-default-fix.yml b/changelogs/unreleased/31995-project-limit-default-fix.yml
deleted file mode 100644
index 4f25eb34b45..00000000000
--- a/changelogs/unreleased/31995-project-limit-default-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: User#projects_limit remove DB default and added NOT NULL constraint
-merge_request: 16165
-author: Mario de la Ossa
-type: fixed
diff --git a/changelogs/unreleased/32364-updating-slack-notification-not-working-by-api.yml b/changelogs/unreleased/32364-updating-slack-notification-not-working-by-api.yml
deleted file mode 100644
index e3fae55c6f0..00000000000
--- a/changelogs/unreleased/32364-updating-slack-notification-not-working-by-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support new chat notifications parameters in Services API
-merge_request: 11435
-author:
-type: added
diff --git a/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml b/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml
new file mode 100644
index 00000000000..f4c44983736
--- /dev/null
+++ b/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml
@@ -0,0 +1,5 @@
+---
+title: Fix copy/paste on iOS devices due to a bug in webkit
+merge_request: 15804
+author:
+type: fixed
diff --git a/changelogs/unreleased/33028-event-tag-links.yml b/changelogs/unreleased/33028-event-tag-links.yml
deleted file mode 100644
index 1d674200dcd..00000000000
--- a/changelogs/unreleased/33028-event-tag-links.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix tags in the Activity tab not being clickable
-merge_request: 15996
-author: Mario de la Ossa
-type: fixed
diff --git a/changelogs/unreleased/33609-hide-pagination.yml b/changelogs/unreleased/33609-hide-pagination.yml
deleted file mode 100644
index 3586b091cb1..00000000000
--- a/changelogs/unreleased/33609-hide-pagination.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable Vue pagination when only one page of content is available
-merge_request: 15999
-author: Mario de la Ossa
-type: fixed
diff --git a/changelogs/unreleased/33926-update-issuable-icons.yml b/changelogs/unreleased/33926-update-issuable-icons.yml
deleted file mode 100644
index 87076dde545..00000000000
--- a/changelogs/unreleased/33926-update-issuable-icons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update issuable status icons
-merge_request: 15898
-author:
-type: changed
diff --git a/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml b/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml
new file mode 100644
index 00000000000..09e2af1e4d3
--- /dev/null
+++ b/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml
@@ -0,0 +1,6 @@
+---
+title: Fix the Projects API with_issues_enabled filter behaving incorrectly
+ any user
+merge_request: 12724
+author: Jan Christophersen
+type: fixed
diff --git a/changelogs/unreleased/34252-trailing-plus.yml b/changelogs/unreleased/34252-trailing-plus.yml
new file mode 100644
index 00000000000..fce17cb6ab9
--- /dev/null
+++ b/changelogs/unreleased/34252-trailing-plus.yml
@@ -0,0 +1,5 @@
+---
+title: Allow trailing + on labels in board filters
+merge_request: 16490
+author:
+type: fixed
diff --git a/changelogs/unreleased/34534-switch-to-axios.yml b/changelogs/unreleased/34534-switch-to-axios.yml
deleted file mode 100644
index 1200272c9eb..00000000000
--- a/changelogs/unreleased/34534-switch-to-axios.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix some POST/DELETE requests in IE by switching some bundles to Axios for Ajax requests
-merge_request: 15951
-author:
-type: fixed
diff --git a/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml b/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml
new file mode 100644
index 00000000000..0791847b64d
--- /dev/null
+++ b/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Fix default avatar icon missing when Gravatar is disabled
+merge_request: 16681
+author: Felix Geyer
+type: fixed
diff --git a/changelogs/unreleased/36020-private-npm-modules.yml b/changelogs/unreleased/36020-private-npm-modules.yml
deleted file mode 100644
index 5c2585a602e..00000000000
--- a/changelogs/unreleased/36020-private-npm-modules.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not generate NPM links for private NPM modules in blob view
-merge_request: 16002
-author: Mario de la Ossa
-type: added
diff --git a/changelogs/unreleased/36669-default-mr-title-with-external-issues.yml b/changelogs/unreleased/36669-default-mr-title-with-external-issues.yml
deleted file mode 100644
index 6af9ac4b099..00000000000
--- a/changelogs/unreleased/36669-default-mr-title-with-external-issues.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Default merge request title is set correctly again when external issue tracker is activated
-merge_request: 16356
-author: Ben305
-type: fixed
diff --git a/changelogs/unreleased/36782-replace-team-user-role-with-add_role-user-in-specs.yml b/changelogs/unreleased/36782-replace-team-user-role-with-add_role-user-in-specs.yml
deleted file mode 100644
index 8773ac73a75..00000000000
--- a/changelogs/unreleased/36782-replace-team-user-role-with-add_role-user-in-specs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace '.team << [user, role]' with 'add_role(user)' in specs
-merge_request: 16069
-author: "@blackst0ne"
-type: other
diff --git a/changelogs/unreleased/36958-enable-ordering-projects-subgroups-by-name.yml b/changelogs/unreleased/36958-enable-ordering-projects-subgroups-by-name.yml
deleted file mode 100644
index 8348e3e8ceb..00000000000
--- a/changelogs/unreleased/36958-enable-ordering-projects-subgroups-by-name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable ordering of groups and their children by name
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/37843-ci-trace-ansi-colours-256-bold-have-no-css-due-wrongly-ansi2html-light-color-variant-conversion-feature.yml b/changelogs/unreleased/37843-ci-trace-ansi-colours-256-bold-have-no-css-due-wrongly-ansi2html-light-color-variant-conversion-feature.yml
deleted file mode 100644
index abf98cd2af4..00000000000
--- a/changelogs/unreleased/37843-ci-trace-ansi-colours-256-bold-have-no-css-due-wrongly-ansi2html-light-color-variant-conversion-feature.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix ANSI 256 bold colors in pipelines job output
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/38019-hide-runner-token.yml b/changelogs/unreleased/38019-hide-runner-token.yml
deleted file mode 100644
index 11ae0a685ef..00000000000
--- a/changelogs/unreleased/38019-hide-runner-token.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide runner token in CI/CD settings page
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/38030-add-graph-value-to-hover.yml b/changelogs/unreleased/38030-add-graph-value-to-hover.yml
deleted file mode 100644
index 233db2b19c9..00000000000
--- a/changelogs/unreleased/38030-add-graph-value-to-hover.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display graph values on hover within monitoring page
-merge_request: 16261
-author:
-type: changed
diff --git a/changelogs/unreleased/38145_ux_issues_in_system_info_page.yml b/changelogs/unreleased/38145_ux_issues_in_system_info_page.yml
deleted file mode 100644
index d2358750518..00000000000
--- a/changelogs/unreleased/38145_ux_issues_in_system_info_page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes the wording of headers in system info page
-merge_request: 15802
-author: Gilbert Roulot
-type: fixed
diff --git a/changelogs/unreleased/38239-update-toggle-design.yml b/changelogs/unreleased/38239-update-toggle-design.yml
deleted file mode 100644
index 4d9034e8515..00000000000
--- a/changelogs/unreleased/38239-update-toggle-design.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update feature toggle design to use icons and make it i18n friendly
-merge_request: 15904
-author:
-type: changed
diff --git a/changelogs/unreleased/38318-search-merge-requests-with-api.yml b/changelogs/unreleased/38318-search-merge-requests-with-api.yml
deleted file mode 100644
index d8b2f1f25c8..00000000000
--- a/changelogs/unreleased/38318-search-merge-requests-with-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add optional search param for Merge Requests API
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/38541-cancel-alignment.yml b/changelogs/unreleased/38541-cancel-alignment.yml
deleted file mode 100644
index c6d5136dd57..00000000000
--- a/changelogs/unreleased/38541-cancel-alignment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix button alignment on MWPS component
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/38596-fix-backspace-visual-token-clearing.yml b/changelogs/unreleased/38596-fix-backspace-visual-token-clearing.yml
deleted file mode 100644
index 4a9d0b66a8c..00000000000
--- a/changelogs/unreleased/38596-fix-backspace-visual-token-clearing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Clears visual token on second backspace
-merge_request:
-author: Martin Wortschack
-type: fixed
diff --git a/changelogs/unreleased/38893-banzai-upload-filter-relative-urls.yml b/changelogs/unreleased/38893-banzai-upload-filter-relative-urls.yml
deleted file mode 100644
index 9ab0a0159e9..00000000000
--- a/changelogs/unreleased/38893-banzai-upload-filter-relative-urls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use relative URLs when linking to uploaded files
-merge_request: 15751
-author:
-type: other
diff --git a/changelogs/unreleased/39246-fork-and-import-jobs-should-only-be-marked-as-failed-when-the-number-of-retries-was-exhausted.yml b/changelogs/unreleased/39246-fork-and-import-jobs-should-only-be-marked-as-failed-when-the-number-of-retries-was-exhausted.yml
deleted file mode 100644
index ce238a2c79f..00000000000
--- a/changelogs/unreleased/39246-fork-and-import-jobs-should-only-be-marked-as-failed-when-the-number-of-retries-was-exhausted.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Only mark import and fork jobs as failed once all Sidekiq retries get exhausted
-merge_request: 15844
-author:
-type: changed
diff --git a/changelogs/unreleased/39298-list-of-avatars-2.yml b/changelogs/unreleased/39298-list-of-avatars-2.yml
deleted file mode 100644
index e2095561c0e..00000000000
--- a/changelogs/unreleased/39298-list-of-avatars-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: List of avatars should never show +1
-merge_request: 15972
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/39608-comment-on-image-discussions-tab-alignment.yml b/changelogs/unreleased/39608-comment-on-image-discussions-tab-alignment.yml
deleted file mode 100644
index 5021fe88caf..00000000000
--- a/changelogs/unreleased/39608-comment-on-image-discussions-tab-alignment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update comment on image cursor and icons
-merge_request: 15760
-author:
-type: fixed
diff --git a/changelogs/unreleased/3968-protected-branch-is-not-set-for-default-branch-on-import.yml b/changelogs/unreleased/3968-protected-branch-is-not-set-for-default-branch-on-import.yml
deleted file mode 100644
index e972ac6d54a..00000000000
--- a/changelogs/unreleased/3968-protected-branch-is-not-set-for-default-branch-on-import.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Protected branch is now created for default branch on import
-merge_request: 16198
-author:
-type: fixed
diff --git a/changelogs/unreleased/39917-revert-this-merge-request-text.yml b/changelogs/unreleased/39917-revert-this-merge-request-text.yml
new file mode 100644
index 00000000000..9a27be1f9c6
--- /dev/null
+++ b/changelogs/unreleased/39917-revert-this-merge-request-text.yml
@@ -0,0 +1,5 @@
+---
+title: Changes Revert this merge request text
+merge_request: 16611
+author: Jacopo Beschi @jacopo-beschi
+type: changed
diff --git a/changelogs/unreleased/39957-redirect-to-gpc-page-if-users-try-to-create-a-cluster-but-the-account-is-not-enabled.yml b/changelogs/unreleased/39957-redirect-to-gpc-page-if-users-try-to-create-a-cluster-but-the-account-is-not-enabled.yml
deleted file mode 100644
index d8fd1f14bd4..00000000000
--- a/changelogs/unreleased/39957-redirect-to-gpc-page-if-users-try-to-create-a-cluster-but-the-account-is-not-enabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Implement checking GCP project billing status in cluster creation form.
-merge_request: 15665
-author:
-type: changed
diff --git a/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml b/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml
new file mode 100644
index 00000000000..ffab28acbd5
--- /dev/null
+++ b/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml
@@ -0,0 +1,5 @@
+---
+title: Handle special characters on API request of issuable templates
+merge_request: 15323
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml b/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml
new file mode 100644
index 00000000000..519f411d642
--- /dev/null
+++ b/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml
@@ -0,0 +1,5 @@
+---
+title: Stop loading spinner on error of issuable templates
+merge_request: 16600
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/40031-include-assset_sync-gem.yml b/changelogs/unreleased/40031-include-assset_sync-gem.yml
deleted file mode 100644
index 93ce565b32c..00000000000
--- a/changelogs/unreleased/40031-include-assset_sync-gem.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add assets_sync gem to Gemfile
-merge_request: 15734
-author:
-type: added
diff --git a/changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml b/changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml
deleted file mode 100644
index e2fade2bfd9..00000000000
--- a/changelogs/unreleased/40040-decouple-multi-file-editor-from-file-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds the multi file editor as a new beta feature
-merge_request: 15430
-author:
-type: feature
diff --git a/changelogs/unreleased/40063-markdown-editor-improvements.yml b/changelogs/unreleased/40063-markdown-editor-improvements.yml
deleted file mode 100644
index fa2f09408b4..00000000000
--- a/changelogs/unreleased/40063-markdown-editor-improvements.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide markdown toolbar in preview mode
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/40190-fix-slash-commands-dropdown-description-mis-alignement-on-firefox.yml b/changelogs/unreleased/40190-fix-slash-commands-dropdown-description-mis-alignement-on-firefox.yml
deleted file mode 100644
index 71a606ff607..00000000000
--- a/changelogs/unreleased/40190-fix-slash-commands-dropdown-description-mis-alignement-on-firefox.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Fix slash commands dropdown description mis-alignment on Firefox"
-merge_request: 16125
-author: Maurizio De Santis
-type: fixed
diff --git a/changelogs/unreleased/40228-verify-integrity-of-repositories.yml b/changelogs/unreleased/40228-verify-integrity-of-repositories.yml
deleted file mode 100644
index 261d48652db..00000000000
--- a/changelogs/unreleased/40228-verify-integrity-of-repositories.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix gitlab-rake gitlab:import:repos import schedule
-merge_request: 15931
-author:
-type: fixed
diff --git a/changelogs/unreleased/40274-user-settings-breadcrumbs.yml b/changelogs/unreleased/40274-user-settings-breadcrumbs.yml
deleted file mode 100644
index 1f381668aca..00000000000
--- a/changelogs/unreleased/40274-user-settings-breadcrumbs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix breadcrumbs in User Settings
-merge_request: 16172
-author: rfwatson
-type: fixed
diff --git a/changelogs/unreleased/40301-rebase.yml b/changelogs/unreleased/40301-rebase.yml
deleted file mode 100644
index 1c0fc0cd8ae..00000000000
--- a/changelogs/unreleased/40301-rebase.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow user to rebase merge requests.
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/40418-migrate-existing-data-from-kubernetesservice-to-clusters-platforms-kubernetes.yml b/changelogs/unreleased/40418-migrate-existing-data-from-kubernetesservice-to-clusters-platforms-kubernetes.yml
deleted file mode 100644
index 5e158d831a6..00000000000
--- a/changelogs/unreleased/40418-migrate-existing-data-from-kubernetesservice-to-clusters-platforms-kubernetes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Migrate existing data from KubernetesService to Clusters::Platforms::Kubernetes
-merge_request: 15589
-author:
-type: changed
diff --git a/changelogs/unreleased/40453-fix-api-endpoints-to-edit-wiki-pages-where-project-belongs-to-a-group.yml b/changelogs/unreleased/40453-fix-api-endpoints-to-edit-wiki-pages-where-project-belongs-to-a-group.yml
deleted file mode 100644
index 30917098a95..00000000000
--- a/changelogs/unreleased/40453-fix-api-endpoints-to-edit-wiki-pages-where-project-belongs-to-a-group.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix API endpoints to edit wiki pages where project belongs to a group
-merge_request: 16170
-author:
-type: fixed
diff --git a/changelogs/unreleased/40509_sorting_tags_api.yml b/changelogs/unreleased/40509_sorting_tags_api.yml
deleted file mode 100644
index 38b198d0fe3..00000000000
--- a/changelogs/unreleased/40509_sorting_tags_api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: add support for sorting in tags api
-merge_request: 15772
-author: haseebeqx
-type: added
diff --git a/changelogs/unreleased/40533-groups-tree-updates.yml b/changelogs/unreleased/40533-groups-tree-updates.yml
deleted file mode 100644
index 1bc0aa90f9e..00000000000
--- a/changelogs/unreleased/40533-groups-tree-updates.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Update groups tree to use GitLab SVG icons, add last updated at information
- for projects
-merge_request: 15980
-author:
-type: changed
diff --git a/changelogs/unreleased/40540-use-limit-for-global-search.yml b/changelogs/unreleased/40540-use-limit-for-global-search.yml
new file mode 100644
index 00000000000..7d9612c16df
--- /dev/null
+++ b/changelogs/unreleased/40540-use-limit-for-global-search.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize search queries on the search page by setting a limit for matching records.
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/40549-render-emoj-in-groups-overview.yml b/changelogs/unreleased/40549-render-emoj-in-groups-overview.yml
deleted file mode 100644
index 9b2f58df440..00000000000
--- a/changelogs/unreleased/40549-render-emoj-in-groups-overview.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rendering of emoji's in Group-Overview
-merge_request: 16098
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/40622-use-left-right-and-max-count.yml b/changelogs/unreleased/40622-use-left-right-and-max-count.yml
deleted file mode 100644
index c4c8f271cbe..00000000000
--- a/changelogs/unreleased/40622-use-left-right-and-max-count.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Improve the performance for counting diverging commits. Show 999+
- if it is more than 1000 commits
-merge_request: 15963
-author:
-type: performance
diff --git a/changelogs/unreleased/40780-choose-file.yml b/changelogs/unreleased/40780-choose-file.yml
deleted file mode 100644
index 73e59dfcce8..00000000000
--- a/changelogs/unreleased/40780-choose-file.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Browse file to Choose file in all occurences
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/40871-todo-notification-count-shows-notification-without-having-a-todo.yml b/changelogs/unreleased/40871-todo-notification-count-shows-notification-without-having-a-todo.yml
deleted file mode 100644
index ee196629def..00000000000
--- a/changelogs/unreleased/40871-todo-notification-count-shows-notification-without-having-a-todo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Reset todo counters when the target is deleted
-merge_request: 15807
-author:
-type: fixed
diff --git a/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml b/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml
deleted file mode 100644
index 485133b46a7..00000000000
--- a/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use relative URL for projects to avoid storing domains
-merge_request: 15876
-author:
-type: fixed
diff --git a/changelogs/unreleased/41016-import-gitlab-shell-projects.yml b/changelogs/unreleased/41016-import-gitlab-shell-projects.yml
deleted file mode 100644
index 47a9e9c3eec..00000000000
--- a/changelogs/unreleased/41016-import-gitlab-shell-projects.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Import some code and functionality from gitlab-shell to improve subprocess
- handling
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml b/changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml
deleted file mode 100644
index ffb79d7d79f..00000000000
--- a/changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Prometheus to available Cluster applications
-merge_request: 15895
-author:
-type: added
diff --git a/changelogs/unreleased/41054-disable-creation-of-new-kubernetes-integrations.yml b/changelogs/unreleased/41054-disable-creation-of-new-kubernetes-integrations.yml
deleted file mode 100644
index b960b14624c..00000000000
--- a/changelogs/unreleased/41054-disable-creation-of-new-kubernetes-integrations.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Disable creation of new Kubernetes Integrations unless they're active or created
- from template
-merge_request: 41054
-author:
-type: added
diff --git a/changelogs/unreleased/41056-create-cluster-from-kubernetes-integration-application-template.yml b/changelogs/unreleased/41056-create-cluster-from-kubernetes-integration-application-template.yml
deleted file mode 100644
index 2dd6fc5f1b5..00000000000
--- a/changelogs/unreleased/41056-create-cluster-from-kubernetes-integration-application-template.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow automatic creation of Kubernetes Integration from template
-merge_request: 16104
-author:
-type: added
diff --git a/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml b/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml
new file mode 100644
index 00000000000..5e706740962
--- /dev/null
+++ b/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml
@@ -0,0 +1,5 @@
+---
+title: Shows signin tab after new user email confirmation
+merge_request: 16174
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml b/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml
new file mode 100644
index 00000000000..76d3c6eda24
--- /dev/null
+++ b/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml
@@ -0,0 +1,5 @@
+---
+title: Allows html text in commits atom feed
+merge_request: 16603
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/41244-issue-board-shortcut-working-while-no-issues.yml b/changelogs/unreleased/41244-issue-board-shortcut-working-while-no-issues.yml
deleted file mode 100644
index b2c3a86551b..00000000000
--- a/changelogs/unreleased/41244-issue-board-shortcut-working-while-no-issues.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: disables shortcut to issue boards when issues are not enabled
-merge_request: 16020
-author: Christiaan Van den Poel
-type: fixed
diff --git a/changelogs/unreleased/41247-timestamp.yml b/changelogs/unreleased/41247-timestamp.yml
new file mode 100644
index 00000000000..65f1a7485ad
--- /dev/null
+++ b/changelogs/unreleased/41247-timestamp.yml
@@ -0,0 +1,6 @@
+---
+title: For issues display time of last edit of title or description instead of time
+ of any attribute change
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/41249-clearing-the-cache.yml b/changelogs/unreleased/41249-clearing-the-cache.yml
deleted file mode 100644
index 221589a1239..00000000000
--- a/changelogs/unreleased/41249-clearing-the-cache.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Implement project jobs cache reset
-merge_request: 16067
-author:
-type: added
diff --git a/changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml b/changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml
deleted file mode 100644
index 188a854ebee..00000000000
--- a/changelogs/unreleased/41268-bump-ruby-to-2-3-6.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade Ruby to 2.3.6 to include security patches
-merge_request: 16016
-author:
-type: security
diff --git a/changelogs/unreleased/41424-gitlab-rake-gitlab-import-repos-schedules-an-import.yml b/changelogs/unreleased/41424-gitlab-rake-gitlab-import-repos-schedules-an-import.yml
deleted file mode 100644
index b495754a5a8..00000000000
--- a/changelogs/unreleased/41424-gitlab-rake-gitlab-import-repos-schedules-an-import.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix gitlab-rake gitlab:import:repos import schedule
-merge_request: 16115
-author:
-type: fixed
diff --git a/changelogs/unreleased/41468-error-500-trying-to-view-a-merge-request-json-undefined-method-binary-for-nil-nilclass.yml b/changelogs/unreleased/41468-error-500-trying-to-view-a-merge-request-json-undefined-method-binary-for-nil-nilclass.yml
deleted file mode 100644
index f69116382f0..00000000000
--- a/changelogs/unreleased/41468-error-500-trying-to-view-a-merge-request-json-undefined-method-binary-for-nil-nilclass.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix viewing merge request diffs where the underlying blobs are unavailable
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/41491-fix-nil-blob-name-error.yml b/changelogs/unreleased/41491-fix-nil-blob-name-error.yml
deleted file mode 100644
index cf7e63ea46a..00000000000
--- a/changelogs/unreleased/41491-fix-nil-blob-name-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 500 error when visiting a commit where the blobs do not exist
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/41532-email-reason.yml b/changelogs/unreleased/41532-email-reason.yml
new file mode 100644
index 00000000000..83c28769217
--- /dev/null
+++ b/changelogs/unreleased/41532-email-reason.yml
@@ -0,0 +1,5 @@
+---
+title: Initial work to add notification reason to emails
+merge_request: 16160
+author: Mario de la Ossa
+type: added
diff --git a/changelogs/unreleased/41673-blank-query-members-api.yml b/changelogs/unreleased/41673-blank-query-members-api.yml
new file mode 100644
index 00000000000..677c5e250c8
--- /dev/null
+++ b/changelogs/unreleased/41673-blank-query-members-api.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error on empty query for Members API
+merge_request: 16235
+author:
+type: fixed
diff --git a/changelogs/unreleased/41727-target-branch-name.yml b/changelogs/unreleased/41727-target-branch-name.yml
deleted file mode 100644
index aaedf6f1d12..00000000000
--- a/changelogs/unreleased/41727-target-branch-name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Set target_branch to the ref branch when creating MR from issue
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml b/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml
new file mode 100644
index 00000000000..03060c357fe
--- /dev/null
+++ b/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unused CSS selectors for Cycle Analytics
+merge_request: 16270
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/41754-update-scss-lint-to-0-56-0.yml b/changelogs/unreleased/41754-update-scss-lint-to-0-56-0.yml
deleted file mode 100644
index b96dd376cec..00000000000
--- a/changelogs/unreleased/41754-update-scss-lint-to-0-56-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update scss-lint to 0.56.0
-merge_request: 16278
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/41789-fix-up-web-ide-user-preference-copy-and-buttons.yml b/changelogs/unreleased/41789-fix-up-web-ide-user-preference-copy-and-buttons.yml
deleted file mode 100644
index fe87cd5cadb..00000000000
--- a/changelogs/unreleased/41789-fix-up-web-ide-user-preference-copy-and-buttons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix web ide user preferences copy and buttons
-merge_request: 41789
-author:
-type: other
diff --git a/changelogs/unreleased/41814-text-decoration-skip.yml b/changelogs/unreleased/41814-text-decoration-skip.yml
new file mode 100644
index 00000000000..3e39d26be93
--- /dev/null
+++ b/changelogs/unreleased/41814-text-decoration-skip.yml
@@ -0,0 +1,5 @@
+---
+title: Improve readability of underlined links for dyslexic users
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/41882-respect-only-path-in-relative-link-filter.yml b/changelogs/unreleased/41882-respect-only-path-in-relative-link-filter.yml
deleted file mode 100644
index d4b7ec6a3b5..00000000000
--- a/changelogs/unreleased/41882-respect-only-path-in-relative-link-filter.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure that emails contain absolute, rather than relative, links to user uploads
-merge_request: 16364
-author:
-type: fixed
diff --git a/changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml b/changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml
new file mode 100644
index 00000000000..38684cd3c44
--- /dev/null
+++ b/changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing "allow users to request access" option in public project permissions
+merge_request: 16485
+author:
+type: fixed
diff --git a/changelogs/unreleased/42025-fix-issue-api.yml b/changelogs/unreleased/42025-fix-issue-api.yml
deleted file mode 100644
index abb83bb2fad..00000000000
--- a/changelogs/unreleased/42025-fix-issue-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[API] Fix creating issue when assignee_id is empty"
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/42031-fix-links-to-uploads-in-wikis.yml b/changelogs/unreleased/42031-fix-links-to-uploads-in-wikis.yml
deleted file mode 100644
index 027cb414f23..00000000000
--- a/changelogs/unreleased/42031-fix-links-to-uploads-in-wikis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix links to uploaded files on wiki pages
-merge_request: 16499
-author:
-type: fixed
diff --git a/changelogs/unreleased/42046-fork-icon.yml b/changelogs/unreleased/42046-fork-icon.yml
deleted file mode 100644
index def89ff7b08..00000000000
--- a/changelogs/unreleased/42046-fork-icon.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix giant fork icons on forks page
-merge_request: 16474
-author:
-type: fixed
diff --git a/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml b/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml
new file mode 100644
index 00000000000..5cb5dc3ccd8
--- /dev/null
+++ b/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml
@@ -0,0 +1,5 @@
+---
+title: Link Auto DevOps settings to Clusters page
+merge_request: 16641
+author:
+type: changed
diff --git a/changelogs/unreleased/42154-fix-artifact-size-calc.yml b/changelogs/unreleased/42154-fix-artifact-size-calc.yml
new file mode 100644
index 00000000000..3d6911abf09
--- /dev/null
+++ b/changelogs/unreleased/42154-fix-artifact-size-calc.yml
@@ -0,0 +1,5 @@
+---
+title: Fix a bug calculating artifact size for project statistics
+merge_request: 16539
+author:
+type: fixed
diff --git a/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml b/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml
new file mode 100644
index 00000000000..ac8e4b034b5
--- /dev/null
+++ b/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Fix duplicate item in protected branch/tag dropdown
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml b/changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml
new file mode 100644
index 00000000000..c64bee9126e
--- /dev/null
+++ b/changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml
@@ -0,0 +1,5 @@
+---
+title: Fix encoding issue when counting commit count
+merge_request: 16637
+author:
+type: fixed
diff --git a/changelogs/unreleased/42206-permit-password-for-git-param.yml b/changelogs/unreleased/42206-permit-password-for-git-param.yml
new file mode 100644
index 00000000000..563dd528ad5
--- /dev/null
+++ b/changelogs/unreleased/42206-permit-password-for-git-param.yml
@@ -0,0 +1,5 @@
+---
+title: Permits 'password_authentication_enabled_for_git' parameter for ApplicationSettingsController
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42220-add-pending-empty-state.yml b/changelogs/unreleased/42220-add-pending-empty-state.yml
new file mode 100644
index 00000000000..ad39578f2d9
--- /dev/null
+++ b/changelogs/unreleased/42220-add-pending-empty-state.yml
@@ -0,0 +1,5 @@
+---
+title: Adds empty state illustration for pending job
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml b/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml
new file mode 100644
index 00000000000..fbc589ea53d
--- /dev/null
+++ b/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml
@@ -0,0 +1,5 @@
+---
+title: Fix protected branches API to accept name parameter with dot
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42251-explicit-timezone-for-karma.yml b/changelogs/unreleased/42251-explicit-timezone-for-karma.yml
new file mode 100644
index 00000000000..25e0e774c48
--- /dev/null
+++ b/changelogs/unreleased/42251-explicit-timezone-for-karma.yml
@@ -0,0 +1,5 @@
+---
+title: Set timezone for karma to UTC
+merge_request: 16602
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml b/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml
new file mode 100644
index 00000000000..bd7e0d3a1b0
--- /dev/null
+++ b/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml
@@ -0,0 +1,5 @@
+---
+title: Disable MR check out button when source branch is deleted
+merge_request: 16631
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/42285-not-found-status-icon.yml b/changelogs/unreleased/42285-not-found-status-icon.yml
new file mode 100644
index 00000000000..ea7ff9d6ae7
--- /dev/null
+++ b/changelogs/unreleased/42285-not-found-status-icon.yml
@@ -0,0 +1,5 @@
+---
+title: Replace verified badge icons and uniform colors
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml b/changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml
new file mode 100644
index 00000000000..660f4f5d42c
--- /dev/null
+++ b/changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml
@@ -0,0 +1,6 @@
+---
+title: Fixes destination already exists, and some particular service errors on Import/Export
+ error
+merge_request: 16714
+author:
+type: fixed
diff --git a/changelogs/unreleased/ac-autodevopfix-kubectl-version.yml b/changelogs/unreleased/ac-autodevopfix-kubectl-version.yml
deleted file mode 100644
index 0ceeb7ccee1..00000000000
--- a/changelogs/unreleased/ac-autodevopfix-kubectl-version.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Force Auto DevOps kubectl version to 1.8.6
-merge_request: 16218
-author:
-type: fixed
diff --git a/changelogs/unreleased/add-tcp-check-rake-task.yml b/changelogs/unreleased/add-tcp-check-rake-task.yml
deleted file mode 100644
index a7c04bd0d55..00000000000
--- a/changelogs/unreleased/add-tcp-check-rake-task.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a gitlab:tcp_check rake task
-merge_request: 15759
-author:
-type: added
diff --git a/changelogs/unreleased/anchor-issue-references.yml b/changelogs/unreleased/anchor-issue-references.yml
deleted file mode 100644
index 78896427417..00000000000
--- a/changelogs/unreleased/anchor-issue-references.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix false positive issue references in merge requests caused by header anchor
- links.
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/api-domains-expose-project_id.yml b/changelogs/unreleased/api-domains-expose-project_id.yml
deleted file mode 100644
index 22617ffe9b5..00000000000
--- a/changelogs/unreleased/api-domains-expose-project_id.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose project_id on /api/v4/pages/domains
-merge_request: 16200
-author: Luc Didry
-type: changed
diff --git a/changelogs/unreleased/bump_mysql_gem.yml b/changelogs/unreleased/bump_mysql_gem.yml
deleted file mode 100644
index 58166949d72..00000000000
--- a/changelogs/unreleased/bump_mysql_gem.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump mysql2 gem version from 0.4.5 to 0.4.10
-merge_request:
-author: asaparov
-type: other
diff --git a/changelogs/unreleased/bvl-fork-public-project-to-private-namespace.yml b/changelogs/unreleased/bvl-fork-public-project-to-private-namespace.yml
deleted file mode 100644
index b802625943d..00000000000
--- a/changelogs/unreleased/bvl-fork-public-project-to-private-namespace.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow forking a public project to a private group
-merge_request: 16050
-author:
-type: changed
diff --git a/changelogs/unreleased/change-issues-closed-at-background-migration.yml b/changelogs/unreleased/change-issues-closed-at-background-migration.yml
deleted file mode 100644
index 1c81c6a889e..00000000000
--- a/changelogs/unreleased/change-issues-closed-at-background-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use a background migration for issues.closed_at
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/changes-dropdown-ellipsis.yml b/changelogs/unreleased/changes-dropdown-ellipsis.yml
deleted file mode 100644
index 7e3f378cc33..00000000000
--- a/changelogs/unreleased/changes-dropdown-ellipsis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed chanages dropdown ellipsis positioning
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/conditionally-eager-load-event-target-authors.yml b/changelogs/unreleased/conditionally-eager-load-event-target-authors.yml
deleted file mode 100644
index a5f1a958fa8..00000000000
--- a/changelogs/unreleased/conditionally-eager-load-event-target-authors.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Eager load event target authors whenever possible
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/cs-fix-commercial-content-check.yml b/changelogs/unreleased/cs-fix-commercial-content-check.yml
new file mode 100644
index 00000000000..fec80e3ecd2
--- /dev/null
+++ b/changelogs/unreleased/cs-fix-commercial-content-check.yml
@@ -0,0 +1,6 @@
+---
+title: Fix version information not showing on help page if commercial content display
+ was disabled.
+merge_request: 16743
+author:
+type: fixed
diff --git a/changelogs/unreleased/da-handle-hashed-storage-repos-using-repo-import-task.yml b/changelogs/unreleased/da-handle-hashed-storage-repos-using-repo-import-task.yml
deleted file mode 100644
index 74a00d49ab3..00000000000
--- a/changelogs/unreleased/da-handle-hashed-storage-repos-using-repo-import-task.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Handle GitLab hashed storage repositories using the repo import task
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/default-to-https-for-gravatar-urls.yml b/changelogs/unreleased/default-to-https-for-gravatar-urls.yml
new file mode 100644
index 00000000000..544c34fe31d
--- /dev/null
+++ b/changelogs/unreleased/default-to-https-for-gravatar-urls.yml
@@ -0,0 +1,5 @@
+---
+title: Default to HTTPS for all Gravatar URLs
+merge_request: 16666
+author:
+type: fixed
diff --git a/changelogs/unreleased/delay-background-migrations.yml b/changelogs/unreleased/delay-background-migrations.yml
deleted file mode 100644
index aa12591e7d2..00000000000
--- a/changelogs/unreleased/delay-background-migrations.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Run background migrations with a minimum interval
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/disable-pages-on-jobs.yml b/changelogs/unreleased/disable-pages-on-jobs.yml
deleted file mode 100644
index 629768efce1..00000000000
--- a/changelogs/unreleased/disable-pages-on-jobs.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Use simple Next/Prev paging for jobs to avoid large count queries on arbitrarily
- large sets of historical jobs
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/disable-throwOnError-in-katex.yml b/changelogs/unreleased/disable-throwOnError-in-katex.yml
new file mode 100644
index 00000000000..0cd17bb29fe
--- /dev/null
+++ b/changelogs/unreleased/disable-throwOnError-in-katex.yml
@@ -0,0 +1,5 @@
+---
+title: Disable throwOnError in KaTeX to reveal user where is the problem
+merge_request: 16684
+author: Jakub Jirutka
+type: other
diff --git a/changelogs/unreleased/dm-diff-note-for-line-performance.yml b/changelogs/unreleased/dm-diff-note-for-line-performance.yml
deleted file mode 100644
index cbc418ab103..00000000000
--- a/changelogs/unreleased/dm-diff-note-for-line-performance.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance of MR discussions on large diffs
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml b/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml
new file mode 100644
index 00000000000..f59021c0ec9
--- /dev/null
+++ b/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml
@@ -0,0 +1,5 @@
+---
+title: Execute system hooks after-commit when executing project hooks
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/docs-add-why-do-i-get-signed-out-authentication-section.yml b/changelogs/unreleased/docs-add-why-do-i-get-signed-out-authentication-section.yml
deleted file mode 100644
index bc245880ed0..00000000000
--- a/changelogs/unreleased/docs-add-why-do-i-get-signed-out-authentication-section.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add docs for why you might be signed out when using the Remember me token
-merge_request: 15756
-author:
-type: other
diff --git a/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml b/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml
new file mode 100644
index 00000000000..3fce53bc941
--- /dev/null
+++ b/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Add section headers to plus button dropdown
+merge_request: 16394
+author: George Tsiolis
+type: added
diff --git a/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml b/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml
new file mode 100644
index 00000000000..8f3459a7381
--- /dev/null
+++ b/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Improve empty project overview
+merge_request: 16617
+author: George Tsiolis
+type: added
diff --git a/changelogs/unreleased/feature-39591-visibility-level.yml b/changelogs/unreleased/feature-39591-visibility-level.yml
new file mode 100644
index 00000000000..4bbc9bdbb2e
--- /dev/null
+++ b/changelogs/unreleased/feature-39591-visibility-level.yml
@@ -0,0 +1,5 @@
+---
+title: Open visibility level help in a new tab
+merge_request:
+author: Jussi Räsänen
+type: fixed
diff --git a/changelogs/unreleased/feature-40842-provide-oracles-webgate-cookies-to-jira-requests.yml b/changelogs/unreleased/feature-40842-provide-oracles-webgate-cookies-to-jira-requests.yml
deleted file mode 100644
index d5ff5bc4627..00000000000
--- a/changelogs/unreleased/feature-40842-provide-oracles-webgate-cookies-to-jira-requests.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Provide additional cookies to JIRA service requests to allow Oracle WebGates
- Basic Auth
-merge_request:
-author: Stanislaw Wozniak
-type: changed
diff --git a/changelogs/unreleased/feature-api_runners_online.yml b/changelogs/unreleased/feature-api_runners_online.yml
deleted file mode 100644
index 08f4dd16f28..00000000000
--- a/changelogs/unreleased/feature-api_runners_online.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add online and status attribute to runner api entity
-merge_request: 11750
-author:
-type: added
diff --git a/changelogs/unreleased/feature-merge-request-system-hook.yml b/changelogs/unreleased/feature-merge-request-system-hook.yml
new file mode 100644
index 00000000000..cfc4c4235d6
--- /dev/null
+++ b/changelogs/unreleased/feature-merge-request-system-hook.yml
@@ -0,0 +1,5 @@
+---
+title: System hooks for Merge Requests
+merge_request: 14387
+author: Alexis Reigel
+type: added
diff --git a/changelogs/unreleased/file-content-large-screen-padding.yml b/changelogs/unreleased/file-content-large-screen-padding.yml
new file mode 100644
index 00000000000..5691cd09b1f
--- /dev/null
+++ b/changelogs/unreleased/file-content-large-screen-padding.yml
@@ -0,0 +1,5 @@
+---
+title: Double padding for file-content wiki class on larger screens
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/fix-abuse-reports-link-url.yml b/changelogs/unreleased/fix-abuse-reports-link-url.yml
deleted file mode 100644
index 44c26f35984..00000000000
--- a/changelogs/unreleased/fix-abuse-reports-link-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix abuse reports link url in admin area navbar
-merge_request: 16068
-author: megos
-type: fixed
diff --git a/changelogs/unreleased/fix-activity-inline-event-line-height.yml b/changelogs/unreleased/fix-activity-inline-event-line-height.yml
deleted file mode 100644
index 85e69567499..00000000000
--- a/changelogs/unreleased/fix-activity-inline-event-line-height.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix activity inline event line height on mobile
-merge_request: 16121
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml b/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml
new file mode 100644
index 00000000000..d8e97b7ad04
--- /dev/null
+++ b/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml
@@ -0,0 +1,5 @@
+---
+title: Add horizontal scroll to wiki tables
+merge_request: 16527
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml b/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml
new file mode 100644
index 00000000000..2e0f59f81e9
--- /dev/null
+++ b/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml
@@ -0,0 +1,5 @@
+---
+title: Adjust layout width for fixed layout
+merge_request: 16337
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/fix-cache-clear-windows.yml b/changelogs/unreleased/fix-cache-clear-windows.yml
new file mode 100644
index 00000000000..2be6bac004b
--- /dev/null
+++ b/changelogs/unreleased/fix-cache-clear-windows.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix cache clear bug withg using : on Windows'
+merge_request: 16740
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml b/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml
deleted file mode 100644
index 8668aa18669..00000000000
--- a/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Execute quick actions (if present) when creating MR from issue
-merge_request: 15810
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-docs-help-shortcut.yml b/changelogs/unreleased/fix-docs-help-shortcut.yml
deleted file mode 100644
index 8c172e44160..00000000000
--- a/changelogs/unreleased/fix-docs-help-shortcut.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix shortcut links on help page
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-import-export-restoring-associations.yml b/changelogs/unreleased/fix-gb-fix-import-export-restoring-associations.yml
deleted file mode 100644
index 58df0024d61..00000000000
--- a/changelogs/unreleased/fix-gb-fix-import-export-restoring-associations.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix missing references to pipeline objects when restoring project with import/export
- feature
-merge_request: 16221
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-last-push-event-widget-layout.yml b/changelogs/unreleased/fix-last-push-event-widget-layout.yml
deleted file mode 100644
index ba5b115ca19..00000000000
--- a/changelogs/unreleased/fix-last-push-event-widget-layout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Last push event widget width for fixed layout
-merge_request: 15862
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-move-2fa-disable-button.yml b/changelogs/unreleased/fix-move-2fa-disable-button.yml
deleted file mode 100644
index bac98ad5148..00000000000
--- a/changelogs/unreleased/fix-move-2fa-disable-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move 2FA disable button
-merge_request: 16177
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-onion-skin-reenter.yml b/changelogs/unreleased/fix-onion-skin-reenter.yml
deleted file mode 100644
index 66b12c037b0..00000000000
--- a/changelogs/unreleased/fix-onion-skin-reenter.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix onion-skin re-entering state
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-postgresql-table-grant.yml b/changelogs/unreleased/fix-postgresql-table-grant.yml
new file mode 100644
index 00000000000..1c6559f6f73
--- /dev/null
+++ b/changelogs/unreleased/fix-postgresql-table-grant.yml
@@ -0,0 +1,5 @@
+---
+title: Use has_table_privilege for TRIGGER on PostgreSQL
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-profile-settings-content-width.yml b/changelogs/unreleased/fix-profile-settings-content-width.yml
deleted file mode 100644
index bf164dc587d..00000000000
--- a/changelogs/unreleased/fix-profile-settings-content-width.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust content width for User Settings, GPG Keys
-merge_request: 16093
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-profile-settings-sidebar-heading.yml b/changelogs/unreleased/fix-profile-settings-sidebar-heading.yml
deleted file mode 100644
index 75e0ea5612f..00000000000
--- a/changelogs/unreleased/fix-profile-settings-sidebar-heading.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Keep typographic hierarchy in User Settings
-merge_request: 16090
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-remove-unnecessary-sidebar-element-alignment.yml b/changelogs/unreleased/fix-remove-unnecessary-sidebar-element-alignment.yml
deleted file mode 100644
index 24f6f62b934..00000000000
--- a/changelogs/unreleased/fix-remove-unnecessary-sidebar-element-alignment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unnecessary sidebar element realignment
-merge_request: 16159
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix_build_count_in_pipeline_success_maild.yml b/changelogs/unreleased/fix_build_count_in_pipeline_success_maild.yml
deleted file mode 100644
index c39bba62271..00000000000
--- a/changelogs/unreleased/fix_build_count_in_pipeline_success_maild.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix build count in pipeline success mail
-merge_request: 15827
-author: Christiaan Van den Poel
-type: fixed
diff --git a/changelogs/unreleased/fj-40053-error-500-members-list.yml b/changelogs/unreleased/fj-40053-error-500-members-list.yml
deleted file mode 100644
index 8c82950bd41..00000000000
--- a/changelogs/unreleased/fj-40053-error-500-members-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixing error 500 when member exist but not the user
-merge_request: 15970
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml b/changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml
deleted file mode 100644
index 3fd8b0eb988..00000000000
--- a/changelogs/unreleased/fj-40279-normalize-ldap-dn-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Normalizing Identity extern_uid when saving the record
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-41477-fix-bug-wiki-last-version.yml b/changelogs/unreleased/fj-41477-fix-bug-wiki-last-version.yml
deleted file mode 100644
index e4b1343876a..00000000000
--- a/changelogs/unreleased/fj-41477-fix-bug-wiki-last-version.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixing bug when wiki last version
-merge_request: 16197
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-41598-fixing-request-mime-type.yml b/changelogs/unreleased/fj-41598-fixing-request-mime-type.yml
deleted file mode 100644
index 85e4d78b2df..00000000000
--- a/changelogs/unreleased/fj-41598-fixing-request-mime-type.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixing rack request mime type when using rack attack
-merge_request: 16427
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-41681-add-param-disable-commit-stats-api.yml b/changelogs/unreleased/fj-41681-add-param-disable-commit-stats-api.yml
deleted file mode 100644
index dca4dec224c..00000000000
--- a/changelogs/unreleased/fj-41681-add-param-disable-commit-stats-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added option to disable commits stats in the commit endpoint
-merge_request: 16309
-author:
-type: added
diff --git a/changelogs/unreleased/fl-mr-widget-refactor.yml b/changelogs/unreleased/fl-mr-widget-refactor.yml
new file mode 100644
index 00000000000..d59cca68409
--- /dev/null
+++ b/changelogs/unreleased/fl-mr-widget-refactor.yml
@@ -0,0 +1,5 @@
+---
+title: Refactors mr widget components into vue files and adds i18n
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/gitaly-git-http-ssh.yml b/changelogs/unreleased/gitaly-git-http-ssh.yml
new file mode 100644
index 00000000000..98812e92e2a
--- /dev/null
+++ b/changelogs/unreleased/gitaly-git-http-ssh.yml
@@ -0,0 +1,6 @@
+---
+title: Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH
+ pull
+merge_request: 16586
+author:
+type: other
diff --git a/changelogs/unreleased/gitaly-repo-exists.yml b/changelogs/unreleased/gitaly-repo-exists.yml
new file mode 100644
index 00000000000..a9eb42a2038
--- /dev/null
+++ b/changelogs/unreleased/gitaly-repo-exists.yml
@@ -0,0 +1,5 @@
+---
+title: Make Gitaly RepositoryExists opt-out
+merge_request: 16680
+author:
+type: other
diff --git a/changelogs/unreleased/index-namespaces-lower-name.yml b/changelogs/unreleased/index-namespaces-lower-name.yml
deleted file mode 100644
index ef08b6d6755..00000000000
--- a/changelogs/unreleased/index-namespaces-lower-name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add index on namespaces lower(name) for UsersController#exists
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/issue-description-field-typo.yml b/changelogs/unreleased/issue-description-field-typo.yml
deleted file mode 100644
index 9c4c179876d..00000000000
--- a/changelogs/unreleased/issue-description-field-typo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed typo for issue description field declaration
-merge_request:
-author: Marcus Amargi
-type: fixed
diff --git a/changelogs/unreleased/issue_40500.yml b/changelogs/unreleased/issue_40500.yml
deleted file mode 100644
index 35e8938fdad..00000000000
--- a/changelogs/unreleased/issue_40500.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix timeout when filtering issues by label
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/issues-40986-get-participants-from-issues-mr-api.yml b/changelogs/unreleased/issues-40986-get-participants-from-issues-mr-api.yml
deleted file mode 100644
index 4cac87b0cdb..00000000000
--- a/changelogs/unreleased/issues-40986-get-participants-from-issues-mr-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: get participants from merge_requests & issues'
-merge_request: 16187
-author: Brent Greeff
-type: added
diff --git a/changelogs/unreleased/jej-backport-authorized-keys-to-ce.yml b/changelogs/unreleased/jej-backport-authorized-keys-to-ce.yml
deleted file mode 100644
index 4386c631f59..00000000000
--- a/changelogs/unreleased/jej-backport-authorized-keys-to-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Backport fast database lookup of SSH authorized_keys from EE
-merge_request: 16014
-author:
-type: added
diff --git a/changelogs/unreleased/jej-lfs-rev-list-handles-non-utf-paths-41627.yml b/changelogs/unreleased/jej-lfs-rev-list-handles-non-utf-paths-41627.yml
deleted file mode 100644
index 24f18c07ac5..00000000000
--- a/changelogs/unreleased/jej-lfs-rev-list-handles-non-utf-paths-41627.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent RevList failing on non utf8 paths
-merge_request: 16440
-author:
-type: fixed
diff --git a/changelogs/unreleased/jivl-activate-repo-cookie-preferences.yml b/changelogs/unreleased/jivl-activate-repo-cookie-preferences.yml
deleted file mode 100644
index 778eaa84381..00000000000
--- a/changelogs/unreleased/jivl-activate-repo-cookie-preferences.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added option to user preferences to enable the multi file editor
-merge_request: 16056
-author:
-type: added
diff --git a/changelogs/unreleased/jivl-fix-import-project-url-bug.yml b/changelogs/unreleased/jivl-fix-import-project-url-bug.yml
deleted file mode 100644
index 0d97b9c9a53..00000000000
--- a/changelogs/unreleased/jivl-fix-import-project-url-bug.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix import project url not updating project name
-merge_request: 16120
-author:
-type: fixed
diff --git a/changelogs/unreleased/jramsay-4012-i18n-compare.yml b/changelogs/unreleased/jramsay-4012-i18n-compare.yml
deleted file mode 100644
index ff15724be39..00000000000
--- a/changelogs/unreleased/jramsay-4012-i18n-compare.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add i18n helpers to branch comparison view
-merge_request: 16031
-author: James Ramsay
-type: added
diff --git a/changelogs/unreleased/jramsay-41590-add-readme-case.yml b/changelogs/unreleased/jramsay-41590-add-readme-case.yml
deleted file mode 100644
index 37b2bd44e0e..00000000000
--- a/changelogs/unreleased/jramsay-41590-add-readme-case.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix inconsistent downcase of filenames in prefilled `Add` commit messages
-merge_request: 16232
-author: James Ramsay
-type: fixed
diff --git a/changelogs/unreleased/ldap_username_attributes.yml b/changelogs/unreleased/ldap_username_attributes.yml
deleted file mode 100644
index 89bbca58fc9..00000000000
--- a/changelogs/unreleased/ldap_username_attributes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modify `LDAP::Person` to return username value based on attributes
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/lfs-badge.yml b/changelogs/unreleased/lfs-badge.yml
deleted file mode 100644
index e4ed4d6741f..00000000000
--- a/changelogs/unreleased/lfs-badge.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added badge to tree & blob views to indicate LFS tracked files
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/mk-fix-permanent-redirect-validation.yml b/changelogs/unreleased/mk-fix-permanent-redirect-validation.yml
deleted file mode 100644
index 153b2ccc25c..00000000000
--- a/changelogs/unreleased/mk-fix-permanent-redirect-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent invalid Route path if path is unchanged
-merge_request: 16397
-author:
-type: fixed
diff --git a/changelogs/unreleased/mk-no-op-delete-conflicting-redirects.yml b/changelogs/unreleased/mk-no-op-delete-conflicting-redirects.yml
deleted file mode 100644
index 37fdb1df6df..00000000000
--- a/changelogs/unreleased/mk-no-op-delete-conflicting-redirects.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Prevent excessive DB load due to faulty DeleteConflictingRedirectRoutes background
- migration
-merge_request: 16205
-author:
-type: fixed
diff --git a/changelogs/unreleased/mr-status-box-update.yml b/changelogs/unreleased/mr-status-box-update.yml
deleted file mode 100644
index 68265be16a1..00000000000
--- a/changelogs/unreleased/mr-status-box-update.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed merge request status badge not updating after merging
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/multiple-clusters-single-list.yml b/changelogs/unreleased/multiple-clusters-single-list.yml
deleted file mode 100644
index 55743f3c00e..00000000000
--- a/changelogs/unreleased/multiple-clusters-single-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Present multiple clusters in a single list instead of a tabbed view
-merge_request: 15669
-author:
-type: changed
diff --git a/changelogs/unreleased/optimize-issues-avoid-noop-empty-cache-updates2.yml b/changelogs/unreleased/optimize-issues-avoid-noop-empty-cache-updates2.yml
deleted file mode 100644
index e0c3136be69..00000000000
--- a/changelogs/unreleased/optimize-issues-avoid-noop-empty-cache-updates2.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Treat empty markdown and html strings as valid cached text, not missing cache
- that needs to be updated
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml b/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml
new file mode 100644
index 00000000000..1cffb213f23
--- /dev/null
+++ b/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml
@@ -0,0 +1,5 @@
+---
+title: Close and do not reload MR diffs when source branch is deleted
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-introduce-merge-request-statistics.yml b/changelogs/unreleased/osw-introduce-merge-request-statistics.yml
deleted file mode 100644
index fed7c2141fb..00000000000
--- a/changelogs/unreleased/osw-introduce-merge-request-statistics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cache merged and closed events data in merge_request_metrics table
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/remove-incorrect-guidance.yml b/changelogs/unreleased/remove-incorrect-guidance.yml
deleted file mode 100644
index eeb5745698f..00000000000
--- a/changelogs/unreleased/remove-incorrect-guidance.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Removed incorrect guidance stating blocked users will be removed from groups
- and project as members
-merge_request: 15947
-author: CesarApodaca
-type: fixed
diff --git a/changelogs/unreleased/remove-links-mr-empty-state.yml b/changelogs/unreleased/remove-links-mr-empty-state.yml
deleted file mode 100644
index c666bc2c81d..00000000000
--- a/changelogs/unreleased/remove-links-mr-empty-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove related links in MR widget when empty state
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/remove-soft-removals.yml b/changelogs/unreleased/remove-soft-removals.yml
deleted file mode 100644
index aa53d33e502..00000000000
--- a/changelogs/unreleased/remove-soft-removals.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove soft removals related code
-merge_request: 15789
-author:
-type: changed
diff --git a/changelogs/unreleased/remove-tabindexes-from-tag-form.yml b/changelogs/unreleased/remove-tabindexes-from-tag-form.yml
deleted file mode 100644
index a15bf2a7a4f..00000000000
--- a/changelogs/unreleased/remove-tabindexes-from-tag-form.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: removed tabindexes from tag form
-merge_request:
-author: Marcus Amargi
-type: changed
diff --git a/changelogs/unreleased/sh-add-gitaly-health-check.yml b/changelogs/unreleased/sh-add-gitaly-health-check.yml
new file mode 100644
index 00000000000..32c4c5362b4
--- /dev/null
+++ b/changelogs/unreleased/sh-add-gitaly-health-check.yml
@@ -0,0 +1,5 @@
+---
+title: Add a gRPC health check to ensure Gitaly is up
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/sh-add-schedule-pipeline-run-now.yml b/changelogs/unreleased/sh-add-schedule-pipeline-run-now.yml
deleted file mode 100644
index 6d06f695f10..00000000000
--- a/changelogs/unreleased/sh-add-schedule-pipeline-run-now.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add button to run scheduled pipeline immediately
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/sh-catch-invalid-uri-markdown.yml b/changelogs/unreleased/sh-catch-invalid-uri-markdown.yml
deleted file mode 100644
index 9b0233fe988..00000000000
--- a/changelogs/unreleased/sh-catch-invalid-uri-markdown.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Gracefully handle garbled URIs in Markdown
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-bare-import-hooks.yml b/changelogs/unreleased/sh-fix-bare-import-hooks.yml
deleted file mode 100644
index deb6c62f738..00000000000
--- a/changelogs/unreleased/sh-fix-bare-import-hooks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix hooks not being set up properly for bare import Rake task
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-make-kib-human.yml b/changelogs/unreleased/sh-make-kib-human.yml
deleted file mode 100644
index c40bb34fa4a..00000000000
--- a/changelogs/unreleased/sh-make-kib-human.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Humanize the units of "Showing last X KiB of log" in job trace
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-optimize-commit-stats.yml b/changelogs/unreleased/sh-optimize-commit-stats.yml
deleted file mode 100644
index 8c1be1252fb..00000000000
--- a/changelogs/unreleased/sh-optimize-commit-stats.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Speed up generation of commit stats by using Rugged native methods
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-validate-path-project-import.yml b/changelogs/unreleased/sh-validate-path-project-import.yml
deleted file mode 100644
index acad66c0ab2..00000000000
--- a/changelogs/unreleased/sh-validate-path-project-import.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid leaving a push event empty if payload cannot be created
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/show-inline-edit-btn.yml b/changelogs/unreleased/show-inline-edit-btn.yml
deleted file mode 100644
index 8cfe9b7d75a..00000000000
--- a/changelogs/unreleased/show-inline-edit-btn.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move edit button to second row on issue page (and change it to a pencil icon)
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/show_proper_labels_in_board_issue_sidebar_when_issue_is_closed.yml b/changelogs/unreleased/show_proper_labels_in_board_issue_sidebar_when_issue_is_closed.yml
deleted file mode 100644
index c2ab34b20a5..00000000000
--- a/changelogs/unreleased/show_proper_labels_in_board_issue_sidebar_when_issue_is_closed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: show None when issue is in closed list and no labels assigned
-merge_request: 15976
-author: Christiaan Van den Poel
-type: fixed
diff --git a/changelogs/unreleased/sophie-h-gitlab-ce-patch-15.yml b/changelogs/unreleased/sophie-h-gitlab-ce-patch-15.yml
deleted file mode 100644
index b5e3210c737..00000000000
--- a/changelogs/unreleased/sophie-h-gitlab-ce-patch-15.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide link to issues/MRs from labels list if issues/MRs are disabled.
-merge_request: 15863
-author: Sophie Herold
-type: fixed
diff --git a/changelogs/unreleased/tc-correct-email-in-reply-to.yml b/changelogs/unreleased/tc-correct-email-in-reply-to.yml
deleted file mode 100644
index 1c8043f6a5c..00000000000
--- a/changelogs/unreleased/tc-correct-email-in-reply-to.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make mail notifications of discussion notes In-Reply-To of each other
-merge_request: 14289
-author:
-type: changed
diff --git a/changelogs/unreleased/update-node-docs.yml b/changelogs/unreleased/update-node-docs.yml
new file mode 100644
index 00000000000..a1d9d12f0ca
--- /dev/null
+++ b/changelogs/unreleased/update-node-docs.yml
@@ -0,0 +1,5 @@
+---
+title: fix documentation about node version
+merge_request: 16720
+author: Tobias Gurtzick
+type: other
diff --git a/changelogs/unreleased/update-redis-rack.yml b/changelogs/unreleased/update-redis-rack.yml
deleted file mode 100644
index 6e2e6e203b8..00000000000
--- a/changelogs/unreleased/update-redis-rack.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update redis-rack to 2.0.4
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/ux-guide-deprecation.yml b/changelogs/unreleased/ux-guide-deprecation.yml
new file mode 100644
index 00000000000..16477f59abf
--- /dev/null
+++ b/changelogs/unreleased/ux-guide-deprecation.yml
@@ -0,0 +1,6 @@
+---
+title: Add note within ux documentation that further changes should be made within
+ the design.gitlab project
+merge_request:
+author:
+type: deprecated
diff --git a/changelogs/unreleased/winh-delete-milestone-modal.yml b/changelogs/unreleased/winh-delete-milestone-modal.yml
new file mode 100644
index 00000000000..6517fbd5f63
--- /dev/null
+++ b/changelogs/unreleased/winh-delete-milestone-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Add modal for deleting a milestone
+merge_request: 16229
+author:
+type: other
diff --git a/changelogs/unreleased/winh-modal-target-id.yml b/changelogs/unreleased/winh-modal-target-id.yml
deleted file mode 100644
index f8d5b72be50..00000000000
--- a/changelogs/unreleased/winh-modal-target-id.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add id to modal.vue to support data-toggle="modal"
-merge_request: 16189
-author:
-type: other
diff --git a/changelogs/unreleased/winh-search-page-filters.yml b/changelogs/unreleased/winh-search-page-filters.yml
new file mode 100644
index 00000000000..90c5cd8d818
--- /dev/null
+++ b/changelogs/unreleased/winh-search-page-filters.yml
@@ -0,0 +1,5 @@
+---
+title: Filter groups and projects dropdowns of search page on backend
+merge_request: 16336
+author:
+type: fixed
diff --git a/changelogs/unreleased/winh-translate-contributors-page-dates.yml b/changelogs/unreleased/winh-translate-contributors-page-dates.yml
deleted file mode 100644
index 74801bbd86e..00000000000
--- a/changelogs/unreleased/winh-translate-contributors-page-dates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Translate date ranges on contributors page
-merge_request: 15846
-author:
-type: changed
diff --git a/config/application.rb b/config/application.rb
index ea9a07cbde9..2067428ff62 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -98,10 +98,11 @@ module Gitlab
# Enable the asset pipeline
config.assets.enabled = true
+
# Support legacy unicode file named img emojis, `1F939.png`
config.assets.paths << Gemojione.images_path
- config.assets.paths << "vendor/assets/fonts"
- config.assets.precompile << "*.png"
+ config.assets.paths << "#{config.root}/vendor/assets/fonts"
+
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
@@ -110,7 +111,6 @@ module Gitlab
config.assets.precompile << "xterm/xterm.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
- config.assets.precompile << "vendor/assets/fonts/*"
config.assets.precompile << "test.css"
config.assets.precompile << "locale/**/app.js"
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 60df92a44fc..778cca4297f 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -467,8 +467,8 @@
- - :license
- pikaday
- MIT
- - :who:
- :why:
+ - :who:
+ :why:
:versions: []
:when: 2017-10-17 17:46:12.367554000 Z
- - :license
@@ -503,3 +503,16 @@
:versions:
- 1.0.9
:when: 2017-11-16 13:02:06.765282000 Z
+- - :license
+ - JSONStream
+ - MIT
+ - :who: Tim Zallmann
+ :why: https://github.com/dominictarr/JSONStream/blob/master/LICENSE.MIT
+ :versions: []
+ :when: 2018-01-17 22:46:12.367554000 Z
+- - :approve
+ - uws
+ - :who: Tim Zallmann
+ :why: zlib license + Development Lib + https://github.com/uNetworking/uWebSockets/blob/master/LICENSE
+ :versions: []
+ :when: 2018-01-17 23:46:12.367554000 Z
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index f2f05b3eeb2..25f4085deb2 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -175,14 +175,16 @@ production: &base
host: 'https://mattermost.example.com'
## Gravatar
- ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
+ ## If using gravatar.com, there's nothing to change here. For Libravatar
+ ## you'll need to provide the custom URLs. For more information,
+ ## see: https://docs.gitlab.com/ee/customization/libravatar.html
gravatar:
- # gravatar urls: possible placeholders: %{hash} %{size} %{email} %{username}
- # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
+ # Gravatar/Libravatar URLs: possible placeholders: %{hash} %{size} %{email} %{username}
+ # plain_url: "http://..." # default: https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
## Auxiliary jobs
- # Periodically executed jobs, to self-heal Gitlab, do external synchronizations, etc.
+ # Periodically executed jobs, to self-heal GitLab, do external synchronizations, etc.
# Please read here for more information: https://github.com/ondrejbartas/sidekiq-cron#adding-cron-job
cron_jobs:
# Flag stuck CI jobs as failed
@@ -640,6 +642,8 @@ test:
enabled: true
lfs:
enabled: false
+ artifacts:
+ path: tmp/tests/artifacts
gitlab:
host: localhost
port: 80
@@ -650,8 +654,6 @@ test:
# user: YOUR_USERNAME
pages:
path: tmp/tests/pages
- artifacts:
- path: tmp/tests/artifacts
repositories:
storages:
default:
diff --git a/config/initializers/0_post_deployment_migrations.rb b/config/initializers/0_post_deployment_migrations.rb
index 0068a03d214..3d81b869b52 100644
--- a/config/initializers/0_post_deployment_migrations.rb
+++ b/config/initializers/0_post_deployment_migrations.rb
@@ -2,11 +2,13 @@
# before other initializers as Rails may otherwise memoize a list of migrations
# excluding the post deployment migrations.
unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
- path = Rails.root.join('db', 'post_migrate').to_s
+ Rails.application.config.paths['db'].each do |db_path|
+ path = Rails.root.join(db_path, 'post_migrate').to_s
- Rails.application.config.paths['db/migrate'] << path
+ Rails.application.config.paths['db/migrate'] << path
- # Rails memoizes migrations at certain points where it won't read the above
- # path just yet. As such we must also update the following list of paths.
- ActiveRecord::Migrator.migrations_paths << path
+ # Rails memoizes migrations at certain points where it won't read the above
+ # path just yet. As such we must also update the following list of paths.
+ ActiveRecord::Migrator.migrations_paths << path
+ end
end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index abc992e49dc..899e612ffbd 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -350,7 +350,7 @@ Settings.mattermost['host'] = nil unless Settings.mattermost.enabled
#
Settings['gravatar'] ||= Settingslogic.new({})
Settings.gravatar['enabled'] = true if Settings.gravatar['enabled'].nil?
-Settings.gravatar['plain_url'] ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
+Settings.gravatar['plain_url'] ||= 'https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['host'] = Settings.host_without_www(Settings.gravatar['plain_url'])
diff --git a/config/initializers/date_time_formats.rb b/config/initializers/date_time_formats.rb
index 57568203cab..1939ced512d 100644
--- a/config/initializers/date_time_formats.rb
+++ b/config/initializers/date_time_formats.rb
@@ -2,8 +2,10 @@
# :medium - Nov 10, 2007
# :long - November 10, 2007
Date::DATE_FORMATS[:medium] = '%b %-d, %Y'
+Date::DATE_FORMATS[:csv] = '%Y-%m-%d'
# :short - 18 Jan 06:10
# :medium - Jan 18, 2007 6:10am
# :long - January 18, 2007 06:10
Time::DATE_FORMATS[:medium] = '%b %-d, %Y %-I:%M%P'
+Time::DATE_FORMATS[:csv] = '%Y-%m-%d %H:%M:%S'
diff --git a/config/initializers/rugged_use_gitlab_git_attributes.rb b/config/initializers/rugged_use_gitlab_git_attributes.rb
deleted file mode 100644
index 1cfb3bcb4bd..00000000000
--- a/config/initializers/rugged_use_gitlab_git_attributes.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# We don't want to ever call Rugged::Repository#fetch_attributes, because it has
-# a lot of I/O overhead:
-# <https://gitlab.com/gitlab-org/gitlab_git/commit/340e111e040ae847b614d35b4d3173ec48329015>
-#
-# While we don't do this from within the GitLab source itself, the Linguist gem
-# has a dependency on Rugged and uses the gitattributes file when calculating
-# repository-wide language statistics:
-# <https://github.com/github/linguist/blob/v4.7.0/lib/linguist/lazy_blob.rb#L33-L36>
-#
-# The options passed by Linguist are those assumed by Gitlab::Git::Attributes
-# anyway, and there is no great efficiency gain from just fetching the listed
-# attributes with our implementation, so we ignore the additional arguments.
-#
-module Rugged
- class Repository
- module UseGitlabGitAttributes
- def fetch_attributes(name, *)
- attributes.attributes(name)
- end
-
- def attributes
- @attributes ||= Gitlab::Git::Attributes.new(path)
- end
- end
-
- prepend UseGitlabGitAttributes
- end
-end
diff --git a/config/karma.config.js b/config/karma.config.js
index 9f018d14b8f..a101d35704e 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -18,6 +18,8 @@ webpackConfig.devtool = 'cheap-inline-source-map';
// Karma configuration
module.exports = function(config) {
+ process.env.TZ = 'Etc/UTC';
+
var progressReporter = process.env.CI ? 'mocha' : 'progress';
var karmaConfig = {
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 8932db138d9..795e5d4e6bc 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2,6 +2,18 @@
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
+ hello: "Hello world"
+ activerecord:
+ attributes:
+ issue_link:
+ source: Source issue
+ target: Target issue
+ errors:
+ messages:
+ label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
+ wrong_size: "is the wrong size (should be %{file_size})"
+ size_too_small: "is too small (should be at least %{file_size})"
+ size_too_big: "is too big (should be at most %{file_size})"
views:
pagination:
previous: "Prev"
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 43ada9ba145..0496bd85b4e 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -40,7 +40,7 @@ constraints(ProjectUrlConstrainer.new) do
#
# Templates
#
- get '/templates/:template_type/:key' => 'templates#show', as: :template
+ get '/templates/:template_type/:key' => 'templates#show', as: :template, constraints: { key: /[^\/]+/ }
resource :avatar, only: [:show, :destroy]
resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do
diff --git a/config/unicorn.rb.example.development b/config/unicorn.rb.example.development
index 3cd00d53a15..0df028648d1 100644
--- a/config/unicorn.rb.example.development
+++ b/config/unicorn.rb.example.development
@@ -1,2 +1,15 @@
worker_processes 2
timeout 60
+
+before_fork do |server, worker|
+ if /darwin/ =~ RUBY_PLATFORM
+ require 'fiddle'
+
+ # Dynamically load Foundation.framework, ~implicitly~ initialising
+ # the Objective-C runtime before any forking happens in Unicorn
+ #
+ # From https://bugs.ruby-lang.org/issues/14009
+ Fiddle.dlopen '/System/Library/Frameworks/Foundation.framework/Foundation'
+ end
+end
+
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 95fa79990e2..783677b5b8d 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -44,9 +44,6 @@ var config = {
graphs: './graphs/graphs_bundle.js',
graphs_charts: './graphs/graphs_charts.js',
graphs_show: './graphs/graphs_show.js',
- group: './group.js',
- groups: './groups/index.js',
- groups_list: './groups_list.js',
help: './help/help.js',
how_to_merge: './how_to_merge.js',
issue_show: './issue_show/index.js',
@@ -66,7 +63,6 @@ var config = {
pipelines_times: './pipelines/pipelines_times.js',
profile: './profile/profile_bundle.js',
project_import_gl: './projects/project_import_gitlab_project.js',
- project_new: './projects/project_new.js',
prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches',
protected_tags: './protected_tags',
@@ -86,7 +82,6 @@ var config = {
test: './test.js',
two_factor_auth: './two_factor_auth.js',
users: './users/index.js',
- performance_bar: './performance_bar.js',
webpack_runtime: './webpack.js',
},
@@ -120,7 +115,12 @@ var config = {
{
test: /\_worker\.js$/,
use: [
- { loader: 'worker-loader' },
+ {
+ loader: 'worker-loader',
+ options: {
+ inline: true
+ }
+ },
{ loader: 'babel-loader' },
],
},
diff --git a/db/migrate/20170425112128_create_pipeline_schedules_table.rb b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
index 57df47f5f42..4f9c56a1ad8 100644
--- a/db/migrate/20170425112128_create_pipeline_schedules_table.rb
+++ b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
@@ -17,7 +17,7 @@ class CreatePipelineSchedulesTable < ActiveRecord::Migration
t.boolean :active, default: true
t.datetime :deleted_at
- t.timestamps
+ t.timestamps null: true
end
add_index(:ci_pipeline_schedules, :project_id)
diff --git a/db/migrate/20171211145425_add_can_push_to_deploy_keys_projects.rb b/db/migrate/20171211145425_add_can_push_to_deploy_keys_projects.rb
new file mode 100644
index 00000000000..5dc723db9f9
--- /dev/null
+++ b/db/migrate/20171211145425_add_can_push_to_deploy_keys_projects.rb
@@ -0,0 +1,15 @@
+class AddCanPushToDeployKeysProjects < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :deploy_keys_projects, :can_push, :boolean, default: false, allow_null: false
+ end
+
+ def down
+ remove_column :deploy_keys_projects, :can_push
+ end
+end
diff --git a/db/migrate/20171215113714_populate_can_push_from_deploy_keys_projects.rb b/db/migrate/20171215113714_populate_can_push_from_deploy_keys_projects.rb
new file mode 100644
index 00000000000..680855af945
--- /dev/null
+++ b/db/migrate/20171215113714_populate_can_push_from_deploy_keys_projects.rb
@@ -0,0 +1,64 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class PopulateCanPushFromDeployKeysProjects < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+ DATABASE_NAME = Gitlab::Database.database_name
+
+ disable_ddl_transaction!
+
+ class DeploysKeyProject < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'deploy_keys_projects'
+ end
+
+ def up
+ DeploysKeyProject.each_batch(of: 10_000) do |batch|
+ start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
+
+ if Gitlab::Database.mysql?
+ execute <<-EOF.strip_heredoc
+ UPDATE deploy_keys_projects, #{DATABASE_NAME}.keys
+ SET deploy_keys_projects.can_push = #{DATABASE_NAME}.keys.can_push
+ WHERE deploy_keys_projects.deploy_key_id = #{DATABASE_NAME}.keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ else
+ execute <<-EOF.strip_heredoc
+ UPDATE deploy_keys_projects
+ SET can_push = keys.can_push
+ FROM keys
+ WHERE deploy_key_id = keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ end
+ end
+ end
+
+ def down
+ DeploysKeyProject.each_batch(of: 10_000) do |batch|
+ start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
+
+ if Gitlab::Database.mysql?
+ execute <<-EOF.strip_heredoc
+ UPDATE deploy_keys_projects, #{DATABASE_NAME}.keys
+ SET #{DATABASE_NAME}.keys.can_push = deploy_keys_projects.can_push
+ WHERE deploy_keys_projects.deploy_key_id = #{DATABASE_NAME}.keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ else
+ execute <<-EOF.strip_heredoc
+ UPDATE keys
+ SET can_push = deploy_keys_projects.can_push
+ FROM deploy_keys_projects
+ WHERE deploy_keys_projects.deploy_key_id = keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ end
+ end
+ end
+end
diff --git a/db/migrate/20180113220114_rework_redirect_routes_indexes.rb b/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
new file mode 100644
index 00000000000..ab9971be074
--- /dev/null
+++ b/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
@@ -0,0 +1,68 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class ReworkRedirectRoutesIndexes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME_UNIQUE = "index_redirect_routes_on_path_unique_text_pattern_ops"
+
+ INDEX_NAME_PERM = "index_redirect_routes_on_path_text_pattern_ops_where_permanent"
+ INDEX_NAME_TEMP = "index_redirect_routes_on_path_text_pattern_ops_where_temporary"
+
+ OLD_INDEX_NAME_PATH_TPOPS = "index_redirect_routes_on_path_text_pattern_ops"
+ OLD_INDEX_NAME_PATH_LOWER = "index_on_redirect_routes_lower_path"
+
+ def up
+ disable_statement_timeout
+
+ # this is a plain btree on a single boolean column. It'll never be
+ # selective enough to be valuable. This class is called by
+ # setup_postgresql.rake so it needs to be able to handle this
+ # index not existing.
+ if index_exists?(:redirect_routes, :permanent)
+ remove_concurrent_index(:redirect_routes, :permanent)
+ end
+
+ # If we're on MySQL then the existing index on path is ok. But on
+ # Postgres we need to clean things up:
+ return unless Gitlab::Database.postgresql?
+
+ if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
+
+ # Unique index on lower(path) across both types of redirect_routes:
+ execute("CREATE UNIQUE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_UNIQUE} ON redirect_routes (lower(path) varchar_pattern_ops);")
+
+ # Make two indexes on path -- one for permanent and one for temporary routes:
+ execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
+ execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+
+ # Remove the old indexes:
+
+ # This one needed to be on lower(path) but wasn't so it's replaced with the two above
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_TPOPS};"
+
+ # This one isn't needed because we only ever do = and LIKE on this
+ # column so the varchar_pattern_ops index is sufficient
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_LOWER};"
+ end
+
+ def down
+ disable_statement_timeout
+
+ add_concurrent_index(:redirect_routes, :permanent)
+
+ return unless Gitlab::Database.postgresql?
+
+ execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
+ execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
+
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_UNIQUE};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};")
+ end
+end
diff --git a/db/migrate/20180115201419_add_index_updated_at_to_issues.rb b/db/migrate/20180115201419_add_index_updated_at_to_issues.rb
new file mode 100644
index 00000000000..a5a48fc97be
--- /dev/null
+++ b/db/migrate/20180115201419_add_index_updated_at_to_issues.rb
@@ -0,0 +1,15 @@
+class AddIndexUpdatedAtToIssues < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :issues, :updated_at
+ end
+
+ def down
+ remove_concurrent_index :issues, :updated_at
+ end
+end
diff --git a/db/post_migrate/20171215121205_post_populate_can_push_from_deploy_keys_projects.rb b/db/post_migrate/20171215121205_post_populate_can_push_from_deploy_keys_projects.rb
new file mode 100644
index 00000000000..3a5850df3db
--- /dev/null
+++ b/db/post_migrate/20171215121205_post_populate_can_push_from_deploy_keys_projects.rb
@@ -0,0 +1,63 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class PostPopulateCanPushFromDeployKeysProjects < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ DATABASE_NAME = Gitlab::Database.database_name
+
+ disable_ddl_transaction!
+
+ class DeploysKeyProject < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'deploy_keys_projects'
+ end
+
+ def up
+ DeploysKeyProject.each_batch(of: 10_000) do |batch|
+ start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
+
+ if Gitlab::Database.mysql?
+ execute <<-EOF.strip_heredoc
+ UPDATE deploy_keys_projects, #{DATABASE_NAME}.keys
+ SET deploy_keys_projects.can_push = #{DATABASE_NAME}.keys.can_push
+ WHERE deploy_keys_projects.deploy_key_id = #{DATABASE_NAME}.keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ else
+ execute <<-EOF.strip_heredoc
+ UPDATE deploy_keys_projects
+ SET can_push = keys.can_push
+ FROM keys
+ WHERE deploy_key_id = keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ end
+ end
+ end
+
+ def down
+ DeploysKeyProject.each_batch(of: 10_000) do |batch|
+ start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
+
+ if Gitlab::Database.mysql?
+ execute <<-EOF.strip_heredoc
+ UPDATE deploy_keys_projects, #{DATABASE_NAME}.keys
+ SET #{DATABASE_NAME}.keys.can_push = deploy_keys_projects.can_push
+ WHERE deploy_keys_projects.deploy_key_id = #{DATABASE_NAME}.keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ else
+ execute <<-EOF.strip_heredoc
+ UPDATE keys
+ SET can_push = deploy_keys_projects.can_push
+ FROM deploy_keys_projects
+ WHERE deploy_keys_projects.deploy_key_id = keys.id
+ AND deploy_keys_projects.id BETWEEN #{start_id} AND #{end_id}
+ EOF
+ end
+ end
+ end
+end
diff --git a/db/post_migrate/20171215121259_remove_can_push_from_keys.rb b/db/post_migrate/20171215121259_remove_can_push_from_keys.rb
new file mode 100644
index 00000000000..0599811d986
--- /dev/null
+++ b/db/post_migrate/20171215121259_remove_can_push_from_keys.rb
@@ -0,0 +1,17 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveCanPushFromKeys < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ disable_ddl_transaction!
+
+ def up
+ remove_column :keys, :can_push
+ end
+
+ def down
+ add_column_with_default :keys, :can_push, :boolean, default: false, allow_null: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a32d20b8f28..4e82a688725 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180105212544) do
+ActiveRecord::Schema.define(version: 20180115201419) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -626,6 +626,7 @@ ActiveRecord::Schema.define(version: 20180105212544) do
t.integer "project_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
+ t.boolean "can_push", default: false, null: false
end
add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
@@ -885,6 +886,7 @@ ActiveRecord::Schema.define(version: 20180105212544) do
add_index "issues", ["relative_position"], name: "index_issues_on_relative_position", using: :btree
add_index "issues", ["state"], name: "index_issues_on_state", using: :btree
add_index "issues", ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
+ add_index "issues", ["updated_at"], name: "index_issues_on_updated_at", using: :btree
add_index "issues", ["updated_by_id"], name: "index_issues_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree
create_table "keys", force: :cascade do |t|
@@ -896,7 +898,6 @@ ActiveRecord::Schema.define(version: 20180105212544) do
t.string "type"
t.string "fingerprint"
t.boolean "public", default: false, null: false
- t.boolean "can_push", default: false, null: false
t.datetime "last_used_at"
end
@@ -1538,8 +1539,6 @@ ActiveRecord::Schema.define(version: 20180105212544) do
end
add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
- add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
- add_index "redirect_routes", ["permanent"], name: "index_redirect_routes_on_permanent", using: :btree
add_index "redirect_routes", ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree
create_table "releases", force: :cascade do |t|
diff --git a/doc/README.md b/doc/README.md
index 11d52001440..330670587f5 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -10,9 +10,9 @@ platform for software development!
GitLab offers the most scalable Git-based fully integrated platform for software development, with flexible products and subscription plans:
-- **GitLab Community Edition (CE)** is an [opensource product](https://gitlab.com/gitlab-org/gitlab-ce/),
+- **GitLab Community Edition (CE)** is an [open source product](https://gitlab.com/gitlab-org/gitlab-ce/),
self-hosted, free to use. Every feature available in GitLab CE is also available on GitLab Enterprise Edition (Starter and Premium) and GitLab.com.
-- **GitLab Enterprise Edition (EE)** is an [opencore product](https://gitlab.com/gitlab-org/gitlab-ee/),
+- **GitLab Enterprise Edition (EE)** is an [open-core product](https://gitlab.com/gitlab-org/gitlab-ee/),
self-hosted, fully featured solution of GitLab, available under distinct [subscriptions](https://about.gitlab.com/products/): **GitLab Enterprise Edition Starter (EES)**, **GitLab Enterprise Edition Premium (EEP)**, and **GitLab Enterprise Edition Ultimate (EEU)**.
- **GitLab.com**: SaaS GitLab solution, with [free and paid subscriptions](https://about.gitlab.com/gitlab-com/). GitLab.com is hosted by GitLab, Inc., and administrated by GitLab (users don't have access to admin settings).
@@ -148,8 +148,8 @@ Regular users don't have access to GitLab administration tools and settings.
## Contributor documentation
-GitLab Community Edition is [opensource](https://gitlab.com/gitlab-org/gitlab-ce/)
-and Enterprise Editions are [opencore](https://gitlab.com/gitlab-org/gitlab-ee/).
+GitLab Community Edition is [open source](https://gitlab.com/gitlab-org/gitlab-ce/)
+and Enterprise Editions are [open-core](https://gitlab.com/gitlab-org/gitlab-ee/).
Learn how to contribute to GitLab:
- [Development](development/README.md): All styleguides and explanations how to contribute.
diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md
index cb42b7743c5..664657650d4 100644
--- a/doc/administration/auth/okta.md
+++ b/doc/administration/auth/okta.md
@@ -144,7 +144,7 @@ Now that the Okta app is configured, it's time to enable it in GitLab.
1. [Reconfigure][reconf] or [restart] GitLab for Omnibus and installations
from source respectively for the changes to take effect.
-You might want to try this out on a incognito browser window.
+You might want to try this out on an incognito browser window.
## Configuring groups
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 57e54815b68..2441ff85783 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -483,7 +483,7 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry.
1. A certificate keypair is required for GitLab and the Container Registry to
communicate securely. By default omnibus-gitlab will generate one keypair,
which is saved to `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key`.
- When using an non-bundled Container Registry, you will need to supply a
+ When using a non-bundled Container Registry, you will need to supply a
custom certificate key. To do that, add the following to
`/etc/gitlab/gitlab.rb`
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index 0e92f7c5a34..fd2677996b1 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -154,7 +154,7 @@ who will take all the decisions to restore the service availability by:
- Reconfigure the old **Master** and demote to **Slave** when it comes back online
You must have at least `3` Redis Sentinel servers, and they need to
-be each in a independent machine (that are believed to fail independently),
+be each in an independent machine (that are believed to fail independently),
ideally in different geographical areas.
You can configure them in the same machines where you've configured the other
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index 835ed8c8006..9d1589d84aa 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -1,5 +1,11 @@
# Fast lookup of authorized SSH keys in the database
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in
+> [GitLab Enterprise Edition Standard](https://about.gitlab.com/gitlab-ee) 9.3.
+>
+> [Available in](https://gitlab.com/gitlab-org/gitlab-ee/issues/3953) GitLab
+> Community Edition 10.4.
+
Regular SSH operations become slow as the number of users grows because OpenSSH
searches for a key to authorize a user via a linear search. In the worst case,
such as when the user is not authorized to access GitLab, OpenSSH will scan the
@@ -30,7 +36,7 @@ Add the following to your `sshd_config` file. This is usuaully located at
Omnibus Docker:
```
-AuthorizedKeysCommand /opt/embedded/gitlab-shell/bin/gitlab-shell-authorized-keys-check git %u %k
+AuthorizedKeysCommand /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell-authorized-keys-check git %u %k
AuthorizedKeysCommandUser git
```
diff --git a/doc/api/applications.md b/doc/api/applications.md
new file mode 100644
index 00000000000..933867ed0bb
--- /dev/null
+++ b/doc/api/applications.md
@@ -0,0 +1,37 @@
+# Applications API
+
+> [Introduced][ce-8160] in GitLab 10.5
+
+[ce-8160]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8160
+
+## Create a application
+
+Create a application by posting a JSON payload.
+
+User must be admin to do that.
+
+Returns `200` if the request succeeds.
+
+```
+POST /applications
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `name` | string | yes | The name of the application |
+| `redirect_uri` | string | yes | The redirect URI of the application |
+| `scopes` | string | yes | The scopes of the application |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "name=MyApplication&redirect_uri=http://redirect.uri&scopes=" https://gitlab.example.com/api/v3/applications
+```
+
+Example response:
+
+```json
+{
+ "application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737",
+ "secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34",
+ "callback_url": "http://redirect.uri"
+}
+```
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index d6924741ee4..3f9542d6653 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -172,7 +172,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
-| `award_id` | integer | yes | The ID of a award_emoji |
+| `award_id` | integer | yes | The ID of an award_emoji |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji/344
@@ -197,7 +197,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
-| `note_id` | integer | yes | The ID of an note |
+| `note_id` | integer | yes | The ID of a note |
```bash
@@ -323,7 +323,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note |
-| `award_id` | integer | yes | The ID of a award_emoji |
+| `award_id` | integer | yes | The ID of an award_emoji |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji/345
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index 273d5a56b6f..698fa22a438 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -19,15 +19,13 @@ Example response:
{
"id": 1,
"title": "Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "can_push": false,
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2013-10-02T10:12:29Z"
},
{
"id": 3,
"title": "Another Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "can_push": true,
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2013-10-02T11:12:29Z"
}
]
@@ -57,15 +55,15 @@ Example response:
"id": 1,
"title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "can_push": false,
- "created_at": "2013-10-02T10:12:29Z"
+ "created_at": "2013-10-02T10:12:29Z",
+ "can_push": false
},
{
"id": 3,
"title": "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "can_push": false,
- "created_at": "2013-10-02T11:12:29Z"
+ "created_at": "2013-10-02T11:12:29Z",
+ "can_push": false
}
]
```
@@ -96,8 +94,8 @@ Example response:
"id": 1,
"title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "can_push": false,
- "created_at": "2013-10-02T10:12:29Z"
+ "created_at": "2013-10-02T10:12:29Z",
+ "can_push": false
}
```
@@ -135,6 +133,36 @@ Example response:
}
```
+## Update deploy key
+
+Updates a deploy key for a project.
+
+```
+PUT /projects/:id/deploy_keys/:key_id
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `title` | string | no | New deploy key's title |
+| `can_push` | boolean | no | Can deploy key push to the project's repository |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "New deploy key", "can_push": true}' "https://gitlab.example.com/api/v4/projects/5/deploy_keys/11"
+```
+
+Example response:
+
+```json
+{
+ "id": 11,
+ "title": "New deploy key",
+ "key": "ssh-rsa AAAA...",
+ "created_at": "2015-08-29T12:44:31.550Z",
+ "can_push": true
+}
+```
+
## Delete deploy key
Removes a deploy key from the project. If the deploy key is used only for this project, it will be deleted from the system.
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index a96fb3124fc..21d3ac73000 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -73,7 +73,7 @@ POST /groups/:id/milestones
Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
-- `title` (required) - The title of an milestone
+- `title` (required) - The title of a milestone
- `description` (optional) - The description of the milestone
- `due_date` (optional) - The due date of the milestone
- `start_date` (optional) - The start date of the milestone
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 22ccc6a46f3..2957a0a5f48 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -591,7 +591,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| `title` | string | no | Title of MR |
| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
-| `labels` | string | no | Comma-separated label names for an merge request. Set to an empty string to unassign all labels. |
+| `labels` | string | no | Comma-separated label names for a merge request. Set to an empty string to unassign all labels. |
| `description` | string | no | Description of MR |
| `state_event` | string | no | New state (close/reopen) |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index d35e940d7b1..07e66f89443 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -70,7 +70,7 @@ POST /projects/:id/milestones
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
-- `title` (required) - The title of an milestone
+- `title` (required) - The title of a milestone
- `description` (optional) - The description of the milestone
- `due_date` (optional) - The due date of the milestone
- `start_date` (optional) - The start date of the milestone
diff --git a/doc/api/notes.md b/doc/api/notes.md
index d02ef84d0bd..1b68bd99ce2 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -158,7 +158,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project snippet
-- `note_id` (required) - The ID of an snippet note
+- `note_id` (required) - The ID of a snippet note
```json
{
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index ad2521230e6..cc495c5d091 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -131,12 +131,13 @@ Available only for admins.
GET /projects/:id/snippets/:snippet_id/user_agent_detail
```
-| Attribute | Type | Required | Description |
-|-------------|---------|----------|--------------------------------------|
-| `id` | Integer | yes | The ID of a snippet |
+| Attribute | Type | Required | Description |
+|---------------|---------|----------|--------------------------------------|
+| `id` | Integer | yes | The ID of a project |
+| `snippet_id` | Integer | yes | The ID of a snippet |
```bash
-curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/snippets/1/user_agent_detail
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/snippets/2/user_agent_detail
```
Example response:
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 5a403f7593a..46f5de5aa0e 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -982,7 +982,7 @@ Example response:
## Unarchive a project
Unarchives the project if the user is either admin or the project owner of this project. This action is
-idempotent, thus unarchiving an non-archived project will not change the project.
+idempotent, thus unarchiving a non-archived project will not change the project.
```
POST /projects/:id/unarchive
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index 9750475f0a6..dd424470b67 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -33,6 +33,7 @@ Example response:
"created_at":"2016-10-31T12:32:15.192Z",
"push_events":true,
"tag_push_events":false,
+ "merge_requests_events": true,
"enable_ssl_verification":true
}
]
@@ -54,6 +55,7 @@ POST /hooks
| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
| `push_events` | boolean | no | When true, the hook will fire on push events |
| `tag_push_events` | boolean | no | When true, the hook will fire on new tags being pushed |
+| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
Example request:
@@ -72,6 +74,7 @@ Example response:
"created_at":"2016-10-31T12:32:15.192Z",
"push_events":true,
"tag_push_events":false,
+ "merge_requests_events": true,
"enable_ssl_verification":true
}
]
diff --git a/doc/articles/index.md b/doc/articles/index.md
index c1c3ff67328..9f85533ea94 100644
--- a/doc/articles/index.md
+++ b/doc/articles/index.md
@@ -1,3 +1,7 @@
+---
+comments: false
+---
+
# Technical articles list (deprecated)
[Technical articles](../development/writing_documentation.md#technical-articles) are
@@ -10,4 +14,5 @@ The list of technical articles was [deprecated](https://gitlab.com/gitlab-org/gi
- [GitLab administrator](../administration/index.md)
- [GitLab CI/CD](../ci/README.md)
- [GitLab Pages](../user/project/pages/index.md)
+- [GitLab user](../user/index.md)
- [Install GitLab](../install/README.md)
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index c03e16b1b38..58c4a71cef9 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -455,7 +455,7 @@ Mappings are defined as entries in the root YAML array, and are identified by a
- Literal periods (`.`) should be escaped as `\.`.
- `public`
- a string, starting and ending with `'`.
- - Can include `\N` expressions to refer to capture groups in the `source` regular expression in order of their occurence, starting with `\1`.
+ - Can include `\N` expressions to refer to capture groups in the `source` regular expression in order of their occurrence, starting with `\1`.
The public path for a source path is determined by finding the first `source` expression that matches it, and returning the corresponding `public` path, replacing the `\N` expressions with the values of the `()` capture groups if appropriate.
diff --git a/doc/ci/examples/dast.md b/doc/ci/examples/dast.md
index 16ff8d5bb3e..7bf647bbb8b 100644
--- a/doc/ci/examples/dast.md
+++ b/doc/ci/examples/dast.md
@@ -1,11 +1,12 @@
# Dynamic Application Security Testing with GitLab CI/CD
-This example shows how to run
[Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_program_analysis)
-on your project's source code by using GitLab CI/CD.
+is using the popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy)
+to perform an analysis on your running web application.
-DAST is using the popular open source tool
-[OWASP ZAProxy](https://github.com/zaproxy/zaproxy) to perform an analysis.
+It can be very useful combined with [Review Apps](../review_apps/index.md).
+
+## Example
All you need is a GitLab Runner with the Docker executor (the shared Runners on
GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`,
@@ -14,22 +15,26 @@ called `dast`:
```yaml
dast:
image: owasp/zap2docker-stable
+ variables:
+ website: "https://example.com"
script:
- mkdir /zap/wrk/
- - /zap/zap-baseline.py -J gl-dast-report.json -t https://example.com || true
+ - /zap/zap-baseline.py -J gl-dast-report.json -t $website || true
- cp /zap/wrk/gl-dast-report.json .
artifacts:
paths: [gl-dast-report.json]
```
-The above example will create a `dast` job in your CI pipeline and will allow
-you to download and analyze the report artifact in JSON format.
+The above example will create a `dast` job in your CI/CD pipeline which will run
+the tests on the URL defined in the `website` variable (change it to use your
+own) and finally write the results in the `gl-dast-report.json` file. You can
+then download and analyze the report artifact in JSON format.
TIP: **Tip:**
Starting with [GitLab Enterprise Edition Ultimate][ee] 10.4, this information will
be automatically extracted and shown right in the merge request widget. To do
so, the CI job must be named `dast` and the artifact path must be
`gl-dast-report.json`.
-[Learn more on dynamic application security testing results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
+[Learn more about DAST results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
[ee]: https://about.gitlab.com/gitlab-ee/
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index 6768a2e012f..a2ba29a4ee2 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -167,7 +167,7 @@ Finally, push to GitLab and let the tests begin!
### Test against different PHP versions in Shell builds
The [phpenv][] project allows you to easily manage different versions of PHP
-each with its own config. This is specially usefull when testing PHP projects
+each with its own config. This is especially useful when testing PHP projects
with the Shell executor.
You will have to install it on your build machine under the `gitlab-runner`
@@ -227,7 +227,7 @@ following in your `.gitlab-ci.yml`:
...
# Composer stores all downloaded packages in the vendor/ directory.
-# Do not use the following if the vendor/ directory is commited to
+# Do not use the following if the vendor/ directory is committed to
# your git repository.
cache:
paths:
diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
index a6ed1c54e16..a433cd5a5dd 100644
--- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
@@ -42,7 +42,7 @@ production:
This project has three jobs:
1. `test` - used to test Django application,
2. `staging` - used to automatically deploy staging environment every push to `master` branch
-3. `production` - used to automatically deploy production environmnet for every created tag
+3. `production` - used to automatically deploy production environment for every created tag
## Store API keys
diff --git a/doc/ci/examples/test-phoenix-application.md b/doc/ci/examples/test-phoenix-application.md
index f6c81b076bc..7e49721daf1 100644
--- a/doc/ci/examples/test-phoenix-application.md
+++ b/doc/ci/examples/test-phoenix-application.md
@@ -53,4 +53,3 @@ If you do not have any migrations yet, you will need to create an empty
## Sources
- https://medium.com/@nahtnam/using-phoenix-on-gitlab-ci-5a51eec81142
-- https://davejlong.com/ci-with-phoenix-and-gitlab/
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index f621bf07251..e504b81eae8 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -135,9 +135,9 @@ Clicking on it you will be directed to the jobs page for that specific commit.
![Single commit jobs page](img/single_commit_status_pending.png)
-Notice that there are two jobs pending which are named after what we wrote in
-`.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured
-yet for these jobs.
+Notice that there is a pending job which is named after what we wrote in
+`.gitlab-ci.yml`. "stuck" indicates that there is no Runner configured
+yet for this job.
The next step is to configure a Runner so that it picks the pending jobs.
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index df66810a838..03aa6ff8e7c 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -144,6 +144,28 @@ To protect/unprotect Runners:
![specific Runners edit icon](img/protected_runners_check_box.png)
+## Manually clearing the Runners cache
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/41249) in GitLab 10.4.
+
+GitLab Runners use [cache](../yaml/README.md#cache) to speed up the execution
+of your jobs by reusing existing data. This however, can sometimes lead to an
+inconsistent behavior.
+
+To start with a fresh copy of the cache, you can easily do it via GitLab's UI:
+
+1. Navigate to your project's **CI/CD > Pipelines** page.
+1. Click on the **Clear Runner caches** to clean up the cache.
+1. On the next push, your CI/CD job will use a new cache.
+
+That way, you don't have to change the [cache key](../yaml/README.md#cache-key)
+in your `.gitlab-ci.yml`.
+
+Behind the scenes, this works by increasing a counter in the database, and the
+value of that counter is used to create the key for the cache. After a push, a
+new key is generated and the old cache is not valid anymore. Eventually, the
+Runner's garbage collector will remove it form the filesystem.
+
## How shared Runners pick jobs
Shared Runners abide to a process queue we call fair usage. The fair usage
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index b8df0bfba20..693c8e9ef18 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -194,7 +194,7 @@ before_script:
##
## You can optionally disable host key checking. Be aware that by adding that
- ## you are suspectible to man-in-the-middle attacks.
+ ## you are susceptible to man-in-the-middle attacks.
## WARNING: Use this only with the Docker executor, if you use it with shell
## you will overwrite your user's SSH config.
##
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index b9d4a2098ed..598a7515b01 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -87,7 +87,7 @@ future GitLab releases.**
## 9.0 Renaming
-To follow conventions of naming across GitLab, and to futher move away from the
+To follow conventions of naming across GitLab, and to further move away from the
`build` term and toward `job` CI variables have been renamed for the 9.0
release.
@@ -110,7 +110,7 @@ future GitLab releases.**
| `CI_BUILD_MANUAL` | `CI_JOB_MANUAL` |
| `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` |
-## `.gitlab-ci.yaml` defined variables
+## `.gitlab-ci.yml` defined variables
>**Note:**
This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index ae0b5c0a2ba..4a650303d45 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -258,7 +258,7 @@ The `cache:key` variable can use any of the [predefined variables](../variables/
The default key is **default** across the project, therefore everything is
shared between each pipelines and jobs by default, starting from GitLab 9.0.
->**Note:** The `cache:key` variable cannot contain the `/` character.
+>**Note:** The `cache:key` variable cannot contain the `/` character, or the equivalent URI encoded `%2F`; a value made only of dots (`.`, `%2E`) is also forbidden.
---
@@ -1293,7 +1293,7 @@ to the CI pipeline:
```yaml
variables:
GIT_STRATEGY: clone
- GIT_CHECKOUT: false
+ GIT_CHECKOUT: "false"
script:
- git checkout master
- git merge $CI_BUILD_REF_NAME
diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md
index 4b9791c95bc..5a784b6de06 100644
--- a/doc/development/automatic_ce_ee_merge.md
+++ b/doc/development/automatic_ce_ee_merge.md
@@ -61,7 +61,7 @@ against EE.
1. Tries to apply it to current EE `master`
1. If it applies cleanly, the job succeeds
-In the case where the job fails, it means you should create a `ee-<ce_branch>`
+In the case where the job fails, it means you should create an `ee-<ce_branch>`
or `<ce_branch>-ee` branch, push it to EE and open a merge request against EE
`master`.
At this point if you retry the failing job in your CE merge request, it should
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index fd2b9d0e908..af2026c483e 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -123,7 +123,7 @@ roughly be as follows:
scheduling jobs for newly created data.
1. In a post-deployment migration you'll need to ensure no jobs remain. To do
so you can use `Gitlab::BackgroundMigration.steal` to process any remaining
- jobs before continueing.
+ jobs before continuing.
1. Remove the old column.
## Example
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index 18f4177a5e5..c1f783ce877 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -12,9 +12,9 @@ following format:
```yaml
---
-title: "Going through change[log]s"
+title: "Change[log]s"
merge_request: 1972
-author: Ozzy Osbourne
+author: Black Sabbath
type: added
```
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index f41d31797af..cfeeed2506d 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -420,7 +420,7 @@ the style below as a guide:
In this case:
- before each step list the installation method is declared in bold
-- three dashes (`---`) are used to create an horizontal line and separate the
+- three dashes (`---`) are used to create a horizontal line and separate the
two methods
- the code blocks are indented one or more spaces under the list item to render
correctly
diff --git a/doc/development/fe_guide/axios.md b/doc/development/fe_guide/axios.md
index 1daa6758171..0d9397c3bd5 100644
--- a/doc/development/fe_guide/axios.md
+++ b/doc/development/fe_guide/axios.md
@@ -27,10 +27,23 @@ This exported module should be used instead of directly using `axios` to ensure
});
```
-## Mock axios response on tests
+## Mock axios response in tests
-To help us mock the responses we need we use [axios-mock-adapter][axios-mock-adapter]
+To help us mock the responses we are using [axios-mock-adapter][axios-mock-adapter].
+Advantages over [`spyOn()`]:
+
+- no need to create response objects
+- does not allow call through (which we want to avoid)
+- simple API to test error cases
+- provides `replyOnce()` to allow for different responses
+
+We have also decided against using [axios interceptors] because they are not suitable for mocking.
+
+[axios interceptors]: https://github.com/axios/axios#interceptors
+[`spyOn()`]: https://jasmine.github.io/api/edge/global.html#spyOn
+
+### Example
```javascript
import axios from '~/lib/utils/axios_utils';
@@ -50,13 +63,13 @@ To help us mock the responses we need we use [axios-mock-adapter][axios-mock-ada
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
```
-### Mock poll requests on tests with axios
+### Mock poll requests in tests with axios
-Because polling function requires an header object, we need to always include an object as the third argument:
+Because polling function requires a header object, we need to always include an object as the third argument:
```javascript
mock.onGet('/users').reply(200, { foo: 'bar' }, {});
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 6e9f18dd1c3..6c93c29124d 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -456,7 +456,7 @@ describe('Todos App', () => {
});
```
#### `mountComponent` helper
-There is an helper in `spec/javascripts/helpers/vue_mount_component_helper.js` that allows you to mount a component with the given props:
+There is a helper in `spec/javascripts/helpers/vue_mount_component_helper.js` that allows you to mount a component with the given props:
```javascript
import Vue from 'vue';
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index a235dd74909..243ac7f0c98 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -4,7 +4,7 @@ When writing migrations for GitLab, you have to take into account that
these will be ran by hundreds of thousands of organizations of all sizes, some with
many years of data in their database.
-In addition, having to take a server offline for a a upgrade small or big is a
+In addition, having to take a server offline for an upgrade small or big is a
big burden for most organizations. For this reason it is important that your
migrations are written carefully, can be applied online and adhere to the style
guide below.
diff --git a/doc/development/performance.md b/doc/development/performance.md
index e7c5a6ca07a..c4162a05b77 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -36,7 +36,8 @@ graphs/dashboards.
GitLab provides built-in tools to aid the process of improving performance:
-* [Sherlock](profiling.md#sherlock)
+* [Profiling](profiling.md)
+ * [Sherlock](profiling.md#sherlock)
* [GitLab Performance Monitoring](../administration/monitoring/performance/index.md)
* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
* [QueryRecoder](query_recorder.md) for preventing `N+1` regressions
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index af79353b721..97c997e0568 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -4,6 +4,41 @@ To make it easier to track down performance problems GitLab comes with a set of
profiling tools, some of these are available by default while others need to be
explicitly enabled.
+## Profiling a URL
+
+There is a `Gitlab::Profiler.profile` method, and corresponding
+`bin/profile-url` script, that enable profiling a GET or POST request to a
+specific URL, either as an anonymous user (the default) or as a specific user.
+
+When using the script, command-line documentation is available by passing no
+arguments.
+
+When using the method in an interactive console session, any changes to the
+application code within that console session will be reflected in the profiler
+output.
+
+For example:
+
+```ruby
+Gitlab::Profiler.profile('/my-user')
+# Returns a RubyProf::Profile for the regular operation of this request
+class UsersController; def show; sleep 100; end; end
+Gitlab::Profiler.profile('/my-user')
+# Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show
+```
+
+Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send
+ActiveRecord and ActionController log output to that logger. Further options are
+documented with the method source.
+
+[GitLab-Profiler](https://gitlab.com/gitlab-com/gitlab-profiler) is a project
+that builds on this to add some additional niceties, such as allowing
+configuration with a single Yaml file for multiple URLs, and uploading of the
+profile and log output to S3.
+
+For GitLab.com, you can find the latest results here:
+<http://redash.gitlab.com/dashboard/gitlab-profiler-statistics>
+
## Sherlock
Sherlock is a custom profiling tool built into GitLab. Sherlock is _only_
@@ -27,13 +62,3 @@ Bullet will log query problems to both the Rails log as well as the Chrome
console.
As a follow up to finding `N+1` queries with Bullet, consider writing a [QueryRecoder test](query_recorder.md) to prevent a regression.
-
-## GitLab Profiler
-
-
-[Gitlab-Profiler](https://gitlab.com/gitlab-com/gitlab-profiler) was built to
-help developers understand why specific URLs of their application may be slow
-and to provide hard data that can help reduce load times.
-
-For GitLab.com, you can find the latest results here:
-<http://redash.gitlab.com/dashboard/gitlab-profiler-statistics>
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index ceff57276d2..dc88ce1522c 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -8,7 +8,7 @@ Note that if your db user does not have advanced privileges you must create the
bundle exec rake setup
```
-The `setup` task is a alias for `gitlab:setup`.
+The `setup` task is an alias for `gitlab:setup`.
This tasks calls `db:reset` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database.
Note: `db:setup` calls `db:seed` but this does nothing.
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index edb8f372ea3..df80cd9f584 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -26,7 +26,6 @@ Here are some things to keep in mind regarding test performance:
- Use `.method` to describe class methods and `#method` to describe instance
methods.
- Use `context` to test branching logic.
-- Don't assert against the absolute value of a sequence-generated attribute (see [Gotchas](../gotchas.md#dont-assert-against-the-absolute-value-of-a-sequence-generated-attribute)).
- Try to match the ordering of tests to the ordering within the class.
- Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines
to separate phases.
@@ -89,6 +88,8 @@ Finished in 34.51 seconds (files took 0.76702 seconds to load)
1 example, 0 failures
```
+Note: `live_debug` only works on javascript enabled specs.
+
### `let` variables
GitLab's RSpec suite has made extensive use of `let` variables to reduce
diff --git a/doc/development/ux_guide/animation.md b/doc/development/ux_guide/animation.md
index d190ee1b0ff..797390a6845 100644
--- a/doc/development/ux_guide/animation.md
+++ b/doc/development/ux_guide/animation.md
@@ -27,7 +27,7 @@ View the [interactive example](http://codepen.io/awhildy/full/GNyEvM/) here.
### Dropdowns
-The dropdown menu should feel like it is appearing from the triggering element. Combining a position shift `400ms cubic-bezier(0.23, 1, 0.32, 1)` with a opacity animation `200ms linear` on the second half of the motion achieves this affect.
+The dropdown menu should feel like it is appearing from the triggering element. Combining a position shift `400ms cubic-bezier(0.23, 1, 0.32, 1)` with an opacity animation `200ms linear` on the second half of the motion achieves this affect.
View the [interactive example](http://codepen.io/awhildy/full/jVLJpb/) here.
diff --git a/doc/development/ux_guide/components.md b/doc/development/ux_guide/components.md
index d396964e7c1..012c64be79f 100644
--- a/doc/development/ux_guide/components.md
+++ b/doc/development/ux_guide/components.md
@@ -108,7 +108,7 @@ Primary buttons communicate the main call to action. There should only be one ca
![Primary button example](img/button-primary.png)
#### Secondary
-Secondary buttons are for alternative commands. They should be conveyed by a button with an stroke, and no background fill.
+Secondary buttons are for alternative commands. They should be conveyed by a button with a stroke, and no background fill.
![Secondary button example](img/button-secondary.png)
@@ -181,7 +181,7 @@ A count element is used in navigation contexts where it is helpful to indicate t
## Lists
-Lists are used where ever there is a single column of information to display. Ths [issues list](https://gitlab.com/gitlab-org/gitlab-ce/issues) is an example of a important list in the GitLab UI.
+Lists are used where ever there is a single column of information to display. Ths [issues list](https://gitlab.com/gitlab-org/gitlab-ce/issues) is an example of an important list in the GitLab UI.
### Types
@@ -269,7 +269,7 @@ Modals are only used for having a conversation and confirmation with the user. T
* Modals contain the header, body, and actions.
* **Header(1):** The header title is a question instead of a descriptive phrase.
* **Body(2):** The content in body should never be ambiguous and unclear. It provides specific information.
- * **Actions(3):** Contains a affirmative action, a dismissive action, and an extra action. The order of actions from left to right: Dismissive action → Extra action → Affirmative action
+ * **Actions(3):** Contains an affirmative action, a dismissive action, and an extra action. The order of actions from left to right: Dismissive action → Extra action → Affirmative action
* Confirmations regarding labels should keep labeling styling.
* References to commits, branches, and tags should be **monospaced**.
diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md
index af842da7f62..070efdc15b5 100644
--- a/doc/development/ux_guide/copy.md
+++ b/doc/development/ux_guide/copy.md
@@ -27,7 +27,7 @@ This means that, as a rule, copy should be very short. A long message or label i
>**Example:**
Use `Add` instead of `Add issue` as a button label.
-Preferrably use context and placement of controls to make it obvious what clicking on them will do.
+Preferably use context and placement of controls to make it obvious what clicking on them will do.
---
diff --git a/doc/development/ux_guide/index.md b/doc/development/ux_guide/index.md
index 42bcf234e12..c59e7b72a1a 100644
--- a/doc/development/ux_guide/index.md
+++ b/doc/development/ux_guide/index.md
@@ -1,3 +1,5 @@
+> We are in the process of transferring UX documentation to the [design.gitlab.com](https://gitlab.com/gitlab-org/design.gitlab.com) project. Any updates to these docs should be made in that project. If documentation does not yet exist within [design.gitlab.com](https://gitlab.com/gitlab-org/design.gitlab.com), [create an issue](https://gitlab.com/gitlab-org/design.gitlab.com/issues) and merge request to add your new changes.
+
# GitLab UX Guide
The goal of this guide is to provide standards, principles and in-depth information to design beautiful and effective GitLab features. This will be a living document, and we welcome contributions, feedback and suggestions.
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 9cc4b56c932..7afe338ae8b 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -178,7 +178,7 @@ address. Read [IP address types and allocation methods in Azure][Azure-IP-Addres
At this stage you should have a running and fully operational VM. However, none of the services on
your VM (e.g. GitLab) will be publicly accessible via the internet until you have opened up the
-neccessary ports to enable access to those services.
+necessary ports to enable access to those services.
Ports are opened by adding _security rules_ to the **"Network security group"** (NSG) which our VM
has been assigned to. If you followed the process above, then Azure will have automatically created
@@ -436,4 +436,4 @@ Check out our other [Technical Articles][GitLab-Technical-Articles] or browse th
[SSH]: https://en.wikipedia.org/wiki/Secure_Shell
[PuTTY]: http://www.putty.org/
-[Using-SSH-In-Putty]: https://mediatemple.net/community/products/dv/204404604/using-ssh-in-putty- \ No newline at end of file
+[Using-SSH-In-Putty]: https://mediatemple.net/community/products/dv/204404604/using-ssh-in-putty-
diff --git a/doc/install/installation.md b/doc/install/installation.md
index b2acd5e78b5..18e29271d0f 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -431,7 +431,7 @@ GitLab Shell is an SSH access and repository management software developed speci
**Note:** GitLab Shell application startup time can be greatly reduced by disabling RubyGems. This can be done in several manners:
* Export `RUBYOPT=--disable-gems` environment variable for the processes
-* Compile Ruby with `configure --disable-rubygems` to disable RubyGems by default. Not recommened for system-wide Ruby.
+* Compile Ruby with `configure --disable-rubygems` to disable RubyGems by default. Not recommended for system-wide Ruby.
* Omnibus GitLab [replaces the *shebang* line of the `gitlab-shell/bin/*` scripts](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1707)
### Install gitlab-workhorse
@@ -442,7 +442,7 @@ which is the recommended location.
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
-You can specify a different Git repository by providing it as an extra paramter:
+You can specify a different Git repository by providing it as an extra parameter:
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse,https://example.com/gitlab-workhorse.git]" RAILS_ENV=production
@@ -486,7 +486,7 @@ Make GitLab start on boot:
# 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
-You can specify a different Git repository by providing it as an extra paramter:
+You can specify a different Git repository by providing it as an extra parameter:
sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,https://example.com/gitaly.git]" RAILS_ENV=production
@@ -656,7 +656,7 @@ Checkout the [GitLab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-
### Adding your Trusted Proxies
-If you are using a reverse proxy on an separate machine, you may want to add the
+If you are using a reverse proxy on a separate machine, you may want to add the
proxy to the trusted proxies list. Otherwise users will appear signed in from the
proxy's IP address.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 272148033b5..4324b4ca0b8 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -7,6 +7,7 @@
- Ubuntu
- Debian
- CentOS
+- openSUSE
- Red Hat Enterprise Linux (please use the CentOS packages and instructions)
- Scientific Linux (please use the CentOS packages and instructions)
- Oracle Linux (please use the CentOS packages and instructions)
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 50bb665216e..bbd2d214fe4 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -169,6 +169,32 @@ For Omnibus GitLab packages:
1. [Reconfigure GitLab] for the changes to take effect
+#### Digital Ocean Spaces
+
+This example can be used for a bucket in Amsterdam (AMS3).
+
+1. Add the following to `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['backup_upload_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'ams3',
+ 'aws_access_key_id' => 'AKIAKIAKI',
+ 'aws_secret_access_key' => 'secret123',
+ 'endpoint' => 'https://ams3.digitaloceanspaces.com'
+ }
+ gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
+ ```
+
+1. [Reconfigure GitLab] for the changes to take effect
+
+#### Other S3 Providers
+
+Not all S3 providers are fully-compatible with the Fog library. For example,
+if you see `411 Length Required` errors after attempting to upload, you may
+need to downgrade the `aws_signature_version` value from the default value to
+2 [due to this issue](https://github.com/fog/fog-aws/issues/428).
+
---
For installations from source:
@@ -470,7 +496,7 @@ more of the following options:
- `BACKUP=timestamp_of_backup` - Required if more than one backup exists.
Read what the [backup timestamp is about](#backup-timestamp).
-- `force=yes` - Do not ask if the authorized_keys file should get regenerated.
+- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed.
### Restore for installation from source
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
index 92066997be8..c61729581e8 100644
--- a/doc/security/rack_attack.md
+++ b/doc/security/rack_attack.md
@@ -1,25 +1,135 @@
-# Rack attack
+# Rack Attack
-To prevent abusive clients doing damage GitLab uses rack-attack gem.
+Rack Attack, also known as Rack::Attack, is [a rubygem](https://github.com/kickstarter/rack-attack)
+that is meant to protect GitLab with the ability to customize throttling and
+blocking user IPs.
+You can prevent brute-force passwords attacks, scrapers, or any other offenders
+by throttling requests from IP addresses making large volumes of requests.
+In case you find throttling is not enough to protect you against abusive clients,
+Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering and
+tracking.
-If you installed or upgraded GitLab by following the official guides this should be enabled by default.
+By default, user sign-in, user sign-up (if enabled), and user password reset is
+limited to 6 requests per minute. After trying for 6 times, the client will
+have to wait for the next minute to be able to try again.
-If you are missing `config/initializers/rack_attack.rb` the following steps need to be taken in order to enable protection for your GitLab instance:
+If you installed or upgraded GitLab by following the [official guides](../install/README.md)
+this should be enabled by default. If your instance is not exposed to any incoming
+connections, it is recommended to disable Rack Attack.
-1. In config/application.rb find and uncomment the following line:
+For more information on how to use these options check out
+[rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
- config.middleware.use Rack::Attack
+## Settings
-1. Rename `config/initializers/rack_attack.rb.example` to `config/initializers/rack_attack.rb`.
+**Omnibus GitLab**
-1. Review the `paths_to_be_protected` and add any other path you need protecting.
+1. Open `/etc/gitlab/gitlab.rb` with you editor
+1. Add the following:
-1. Restart GitLab instance.
+ ```ruby
+ gitlab_rails['rack_attack_git_basic_auth'] = {
+ 'enabled' => true,
+ 'ip_whitelist' => ["127.0.0.1"],
+ 'maxretry' => 10,
+ 'findtime' => 60,
+ 'bantime' => 3600
+ }
+ ```
-By default, user sign-in, user sign-up(if enabled) and user password reset is limited to 6 requests per minute. After trying for 6 times, client will have to wait for the next minute to be able to try again. These settings can be found in `config/initializers/rack_attack.rb`
+3. Reconfigure GitLab:
-If you want more restrictive/relaxed throttle rule change the `limit` or `period` values. For example, more relaxed throttle rule will be if you set limit: 3 and period: 1.second(this will allow 3 requests per second). You can also add other paths to the protected list by adding to `paths_to_be_protected` variable. If you change any of these settings do not forget to restart your GitLab instance.
+ ```
+ sudo gitlab-ctl reconfigure
+ ```
-In case you find throttling is not enough to protect you against abusive clients, rack-attack gem offers IP whitelisting, blacklisting, Fail2ban style filter and tracking.
+The following settings can be configured:
-For more information on how to use these options check out [rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
+- `enabled`: By default this is set to `true`. Set this to `false` to disable Rack Attack.
+- `ip_whitelist`: Whitelist any IPs from being blocked. They must be formatted as strings within a ruby array.
+ For example, `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`.
+- `maxretry`: The maximum amount of times a request can be made in the
+ specified time.
+- `findtime`: The maximum amount of time failed requests can count against an IP
+ before it's blacklisted.
+- `bantime`: The total amount of time that a blacklisted IP will be blocked in
+ seconds.
+
+**Installations from source**
+
+These settings can be found in `config/initializers/rack_attack.rb`. If you are
+missing `config/initializers/rack_attack.rb`, the following steps need to be
+taken in order to enable protection for your GitLab instance:
+
+1. In `config/application.rb` find and uncomment the following line:
+
+ ```ruby
+ config.middleware.use Rack::Attack
+ ```
+
+1. Copy `config/initializers/rack_attack.rb.example` to `config/initializers/rack_attack.rb`
+1. Open `config/initializers/rack_attack.rb`, review the
+ `paths_to_be_protected`, and add any other path you need protecting
+1. Restart GitLab:
+
+ ```sh
+ sudo service gitlab restart
+ ```
+
+If you want more restrictive/relaxed throttle rules, edit
+`config/initializers/rack_attack.rb` and change the `limit` or `period` values.
+For example, more relaxed throttle rules will be if you set
+`limit: 3` and `period: 1.seconds` (this will allow 3 requests per second).
+You can also add other paths to the protected list by adding to `paths_to_be_protected`
+variable. If you change any of these settings do not forget to restart your
+GitLab instance.
+
+## Remove blocked IPs from Rack Attack via Redis
+
+In case you want to remove a blocked IP, follow these steps:
+
+1. Find the IPs that have been blocked in the production log:
+
+ ```sh
+ grep "Rack_Attack" /var/log/gitlab/gitlab-rails/production.log
+ ```
+
+2. Since the blacklist is stored in Redis, you need to open up `redis-cli`:
+
+ ```sh
+ /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket
+ ```
+
+3. You can remove the block using the following syntax, replacing `<ip>` with
+ the actual IP that is blacklisted:
+
+ ```
+ del cache:gitlab:rack::attack:allow2ban:ban:<ip>
+ ```
+
+4. Confirm that the key with the IP no longer shows up:
+
+ ```
+ keys *rack::attack*
+ ```
+
+5. Optionally, add the IP to the whitelist to prevent it from being blacklisted
+ again (see [settings](#settings)).
+
+## Troubleshooting
+
+### Rack attack is blacklisting the load balancer
+
+Rack Attack may block your load balancer if all traffic appears to come from
+the load balancer. In that case, you will need to:
+
+1. [Configure `nginx[real_ip_trusted_addresses]`](https://docs.gitlab.com/omnibus/settings/nginx.html#configuring-gitlab-trusted_proxies-and-the-nginx-real_ip-module).
+ This will keep users' IPs from being listed as the load balancer IPs.
+2. Whitelist the load balancer's IP address(es) in the Rack Attack [settings](#settings).
+3. Reconfigure GitLab:
+
+ ```
+ sudo gitlab-ctl reconfigure
+ ```
+
+4. [Remove the block via Redis.](#remove-blocked-ips-from-rack-attack-via-redis)
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 8c8501bcc23..9ba05c7815b 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -465,6 +465,135 @@ X-Gitlab-Event: System Hook
"total_commits_count": 0
}
```
+
+### Merge request events
+
+Triggered when a new merge request is created, an existing merge request was
+updated/merged/closed or a commit is added in the source branch.
+
+**Request header**:
+
+```
+X-Gitlab-Event: System Hook
+```
+
+```json
+{
+ "object_kind": "merge_request",
+ "user": {
+ "name": "Administrator",
+ "username": "root",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
+ },
+ "project": {
+ "name": "Example",
+ "description": "",
+ "web_url": "http://example.com/jsmith/example",
+ "avatar_url": null,
+ "git_ssh_url": "git@example.com:jsmith/example.git",
+ "git_http_url": "http://example.com/jsmith/example.git",
+ "namespace": "Jsmith",
+ "visibility_level": 0,
+ "path_with_namespace": "jsmith/example",
+ "default_branch": "master",
+ "ci_config_path": "",
+ "homepage": "http://example.com/jsmith/example",
+ "url": "git@example.com:jsmith/example.git",
+ "ssh_url": "git@example.com:jsmith/example.git",
+ "http_url": "http://example.com/jsmith/example.git"
+ },
+ "object_attributes": {
+ "id": 90,
+ "target_branch": "master",
+ "source_branch": "ms-viewport",
+ "source_project_id": 14,
+ "author_id": 51,
+ "assignee_id": 6,
+ "title": "MS-Viewport",
+ "created_at": "2017-09-20T08:31:45.944Z",
+ "updated_at": "2017-09-28T12:23:42.365Z",
+ "milestone_id": null,
+ "state": "opened",
+ "merge_status": "unchecked",
+ "target_project_id": 14,
+ "iid": 1,
+ "description": "",
+ "updated_by_id": 1,
+ "merge_error": null,
+ "merge_params": {
+ "force_remove_source_branch": "0"
+ },
+ "merge_when_pipeline_succeeds": false,
+ "merge_user_id": null,
+ "merge_commit_sha": null,
+ "deleted_at": null,
+ "in_progress_merge_commit_sha": null,
+ "lock_version": 5,
+ "time_estimate": 0,
+ "last_edited_at": "2017-09-27T12:43:37.558Z",
+ "last_edited_by_id": 1,
+ "head_pipeline_id": 61,
+ "ref_fetched": true,
+ "merge_jid": null,
+ "source": {
+ "name": "Awesome Project",
+ "description": "",
+ "web_url": "http://example.com/awesome_space/awesome_project",
+ "avatar_url": null,
+ "git_ssh_url": "git@example.com:awesome_space/awesome_project.git",
+ "git_http_url": "http://example.com/awesome_space/awesome_project.git",
+ "namespace": "root",
+ "visibility_level": 0,
+ "path_with_namespace": "awesome_space/awesome_project",
+ "default_branch": "master",
+ "ci_config_path": "",
+ "homepage": "http://example.com/awesome_space/awesome_project",
+ "url": "http://example.com/awesome_space/awesome_project.git",
+ "ssh_url": "git@example.com:awesome_space/awesome_project.git",
+ "http_url": "http://example.com/awesome_space/awesome_project.git"
+ },
+ "target": {
+ "name": "Awesome Project",
+ "description": "Aut reprehenderit ut est.",
+ "web_url": "http://example.com/awesome_space/awesome_project",
+ "avatar_url": null,
+ "git_ssh_url": "git@example.com:awesome_space/awesome_project.git",
+ "git_http_url": "http://example.com/awesome_space/awesome_project.git",
+ "namespace": "Awesome Space",
+ "visibility_level": 0,
+ "path_with_namespace": "awesome_space/awesome_project",
+ "default_branch": "master",
+ "ci_config_path": "",
+ "homepage": "http://example.com/awesome_space/awesome_project",
+ "url": "http://example.com/awesome_space/awesome_project.git",
+ "ssh_url": "git@example.com:awesome_space/awesome_project.git",
+ "http_url": "http://example.com/awesome_space/awesome_project.git"
+ },
+ "last_commit": {
+ "id": "ba3e0d8ff79c80d5b0bbb4f3e2e343e0aaa662b7",
+ "message": "fixed readme",
+ "timestamp": "2017-09-26T16:12:57Z",
+ "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
+ "author": {
+ "name": "GitLab dev user",
+ "email": "gitlabdev@dv6700.(none)"
+ }
+ },
+ "work_in_progress": false,
+ "total_time_spent": 0,
+ "human_total_time_spent": null,
+ "human_time_estimate": null
+ },
+ "labels": null,
+ "repository": {
+ "name": "git-gpg-test",
+ "url": "git@example.com:awesome_space/awesome_project.git",
+ "description": "",
+ "homepage": "http://example.com/awesome_space/awesome_project"
+ }
+}
+```
+
## Repository Update events
Triggered only once when you push to the repository (including tags).
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 6ad314647ee..144cd4c26b0 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -21,10 +21,10 @@ project in an easy and automatic way:
1. [Auto Code Quality](#auto-code-quality)
1. [Auto SAST (Static Application Security Testing)](#auto-sast)
1. [Auto SAST for Docker images](#auto-sast-for-docker-images)
-1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast)
-1. [Auto Browser Performance Testing](#auto-browser-performance-testing)
1. [Auto Review Apps](#auto-review-apps)
+1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast)
1. [Auto Deploy](#auto-deploy)
+1. [Auto Browser Performance Testing](#auto-browser-performance-testing)
1. [Auto Monitoring](#auto-monitoring)
As Auto DevOps relies on many different components, it's good to have a basic
@@ -66,9 +66,8 @@ To make full use of Auto DevOps, you will need:
a domain configured with wildcard DNS which is gonna be used by all of your
Auto DevOps applications. [Read the specifics](#auto-devops-base-domain).
1. **Kubernetes** (needed for Auto Review Apps, Auto Deploy, and Auto Monitoring) -
- To enable deployments, you will need Kubernetes 1.5+. The [Kubernetes service][kubernetes-service]
- integration will need to be enabled for the project, or enabled as a
- [default service template](../../user/project/integrations/services_templates.md)
+ To enable deployments, you will need Kubernetes 1.5+. You need a [Kubernetes cluster][kubernetes-clusters]
+ for the project, or a Kubernetes [default service template](../../user/project/integrations/services_templates.md)
for the entire GitLab installation.
1. **A load balancer** - You can use NGINX ingress by deploying it to your
Kubernetes cluster using the
@@ -229,6 +228,32 @@ check out.
In GitLab Enterprise Edition Ultimate, any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast_docker.html).
+### Auto Review Apps
+
+NOTE: **Note:**
+This is an optional step, since many projects do not have a Kubernetes cluster
+available. If the [prerequisites](#prerequisites) are not met, the job will
+silently be skipped.
+
+CAUTION: **Caution:**
+Your apps should *not* be manipulated outside of Helm (using Kubernetes directly.)
+This can cause confusion with Helm not detecting the change, and subsequent
+deploys with Auto DevOps can undo your changes. Also, if you change something
+and want to undo it by deploying again, Helm may not detect that anything changed
+in the first place, and thus not realize that it needs to re-apply the old config.
+
+[Review Apps][review-app] are temporary application environments based on the
+branch's code so developers, designers, QA, product managers, and other
+reviewers can actually see and interact with code changes as part of the review
+process. Auto Review Apps create a Review App for each branch.
+
+The Review App will have a unique URL based on the project name, the branch
+name, and a unique number, combined with the Auto DevOps base domain. For
+example, `user-project-branch-1234.example.com`. A link to the Review App shows
+up in the merge request widget for easy discovery. When the branch is deleted,
+for example after the merge request is merged, the Review App will automatically
+be deleted.
+
### Auto DAST
> Introduced in [GitLab Enterprise Edition Ultimate][ee] 10.4.
@@ -256,32 +281,6 @@ Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://h
In GitLab Enterprise Edition Premium, performance differences between the source and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
-### Auto Review Apps
-
-NOTE: **Note:**
-This is an optional step, since many projects do not have a Kubernetes cluster
-available. If the [prerequisites](#prerequisites) are not met, the job will
-silently be skipped.
-
-CAUTION: **Caution:**
-Your apps should *not* be manipulated outside of Helm (using Kubernetes directly.)
-This can cause confusion with Helm not detecting the change, and subsequent
-deploys with Auto DevOps can undo your changes. Also, if you change something
-and want to undo it by deploying again, Helm may not detect that anything changed
-in the first place, and thus not realize that it needs to re-apply the old config.
-
-[Review Apps][review-app] are temporary application environments based on the
-branch's code so developers, designers, QA, product managers, and other
-reviewers can actually see and interact with code changes as part of the review
-process. Auto Review Apps create a Review App for each branch.
-
-The Review App will have a unique URL based on the project name, the branch
-name, and a unique number, combined with the Auto DevOps base domain. For
-example, `user-project-branch-1234.example.com`. A link to the Review App shows
-up in the merge request widget for easy discovery. When the branch is deleted,
-for example after the merge request is merged, the Review App will automatically
-be deleted.
-
### Auto Deploy
NOTE: **Note:**
@@ -587,7 +586,7 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
```
[ce-37115]: https://gitlab.com/gitlab-org/gitlab-ce/issues/37115
-[kubernetes-service]: ../../user/project/integrations/kubernetes.md
+[kubernetes-clusters]: ../../user/project/clusters/index.md
[docker-in-docker]: ../../docker/using_docker_build.md#use-docker-in-docker-executor
[review-app]: ../../ci/review_apps/index.md
[container-registry]: ../../user/project/container_registry.md
diff --git a/doc/topics/git/how_to_install_git/index.md b/doc/topics/git/how_to_install_git/index.md
index cdf61057449..7fb578e9ea8 100644
--- a/doc/topics/git/how_to_install_git/index.md
+++ b/doc/topics/git/how_to_install_git/index.md
@@ -14,7 +14,7 @@ This article will show you how to install Git on macOS, Ubuntu Linux and Windows
Although it is easy to use the version of Git shipped with macOS
or install the latest version of Git on macOS by downloading it from the project website,
we recommend installing it via Homebrew to get access to
-an extensive selection of dependancy managed libraries and applications.
+an extensive selection of dependency managed libraries and applications.
If you are sure you don't need access to any additional development libraries
or don't have approximately 15gb of available disk space for Xcode and Homebrew
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index fbe7353c6ca..a9ccbf5a085 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -229,7 +229,7 @@ Our free on Premise solution with >100,000 users
### GitLab CI
-Our own Continuos Integration [feature](https://about.gitlab.com/gitlab-ci/) that is shipped with each instance
+Our own Continuous Integration [feature](https://about.gitlab.com/gitlab-ci/) that is shipped with each instance
### GitLab EE
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index ddc853afded..47ccd0e6dbc 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -147,7 +147,7 @@ change which will be helpful is the database name for which we can use
## ElastiCache
EC is an in-memory hosted caching solution. Redis maintains its own
-persistance and is used for certain types of application.
+persistence and is used for certain types of application.
Let's choose the ElastiCache service in the Database section from our
AWS console. Now lets create a cache subnet group which will be very
@@ -311,7 +311,7 @@ Here is a tricky part though, when adding subnets we need to associate
public subnets instead of the private ones where our instances will
actually live.
-On the secruity group section let's create a new one named
+On the security group section let's create a new one named
`gitlab-loadbalancer-sec-group` and allow both HTTP ad HTTPS traffic
from anywhere.
diff --git a/doc/update/10.2-to-10.3.md b/doc/update/10.2-to-10.3.md
index d6e2db8a353..f8fe4a4b6bf 100644
--- a/doc/update/10.2-to-10.3.md
+++ b/doc/update/10.2-to-10.3.md
@@ -54,17 +54,16 @@ sudo gem install bundler --no-ri --no-rdoc
### 4. Update Node
-GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and
-it has a minimum requirement of node v4.3.0.
+GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets.
+We require a minimum version of node v6.0.0.
You can check which version you are running with `node -v`. If you are running
-a version older than `v4.3.0` you will need to update to a newer version. You
+a version older than `v6.0.0` you will need to update to a newer version. You
can find instructions to install from community maintained packages or compile
from source at the nodejs.org website.
<https://nodejs.org/en/download/>
-
Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
diff --git a/doc/update/10.3-to-10.4.md b/doc/update/10.3-to-10.4.md
index 67b7e634c94..083f6090a8a 100644
--- a/doc/update/10.3-to-10.4.md
+++ b/doc/update/10.3-to-10.4.md
@@ -56,17 +56,16 @@ sudo gem install bundler --no-ri --no-rdoc
### 4. Update Node
-GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and
-it has a minimum requirement of node v4.3.0.
+GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets.
+We require a minimum version of node v6.0.0.
You can check which version you are running with `node -v`. If you are running
-a version older than `v4.3.0` you will need to update to a newer version. You
+a version older than `v6.0.0` you will need to update to a newer version. You
can find instructions to install from community maintained packages or compile
from source at the nodejs.org website.
<https://nodejs.org/en/download/>
-
Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
JavaScript dependencies.
diff --git a/doc/user/admin_area/monitoring/img/convdev_index.png b/doc/user/admin_area/monitoring/img/convdev_index.png
index ffe18d76c96..1bf1d6a83c9 100644
--- a/doc/user/admin_area/monitoring/img/convdev_index.png
+++ b/doc/user/admin_area/monitoring/img/convdev_index.png
Binary files differ
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 590c3f862fb..d3a2a7dcd14 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -212,7 +212,7 @@ Sign in and re-enable two-factor authentication as soon as possible.
For example, if a user is trying to access a GitLab instance from `first.host.xyz` and `second.host.xyz`:
- The user logs in via `first.host.xyz` and registers their U2F key.
- - The user logs out and attempts to log in via `first.host.xyz` - U2F authentication suceeds.
+ - The user logs out and attempts to log in via `first.host.xyz` - U2F authentication succeeds.
- The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
the U2F key has only been registered on `first.host.xyz`.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 130f7897b1a..e87b4403854 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -1,26 +1,28 @@
# Connecting GitLab with a Kubernetes cluster
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35954) in 10.1.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35954) in GitLab 10.1.
+
+Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes
+cluster in a few steps.
With a cluster associated to your project, you can use Review Apps, deploy your
applications, run your pipelines, and much more, in an easy way.
-Connect your project to Google Kubernetes Engine (GKE) or your own Kubernetes
-cluster in a few steps.
-
-NOTE: **Note:**
-The Cluster integration will eventually supersede the
-[Kubernetes integration](../integrations/kubernetes.md). For the moment,
-you can create only one cluster.
+There are two options when adding a new cluster to your project; either associate
+your account with Google Kubernetes Engine (GKE) so that you can [create new
+clusters](#adding-and-creating-a-new-gke-cluster-via-gitlab) from within GitLab,
+or provide the credentials to an [existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster).
## Prerequisites
-In order to be able to manage your GKE cluster through GitLab, the following
-prerequisites must be met:
+In order to be able to manage your Kubernetes cluster through GitLab, the
+following prerequisites must be met.
+
+**For a cluster hosted on GKE:**
- The [Google authentication integration](../../../integration/google.md) must
be enabled in GitLab at the instance level. If that's not the case, ask your
- administrator to enable it.
+ GitLab administrator to enable it.
- Your associated Google account must have the right privileges to manage
clusters on GKE. That would mean that a [billing
account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
@@ -31,41 +33,88 @@ prerequisites must be met:
- You must have [Resource Manager
API](https://cloud.google.com/resource-manager/)
-If all of the above requirements are met, you can proceed to add a new GKE
+**For an existing Kubernetes cluster:**
+
+- Since the cluster is already created, there are no prerequisites.
+
+---
+
+If all of the above requirements are met, you can proceed to add a new Kubernetes
cluster.
-## Adding a cluster
+## Adding and creating a new GKE cluster via GitLab
+
+NOTE: **Note:**
+You need Master [permissions] and above to access the Clusters page.
+
+Before proceeding, make sure all [prerequisites](#prerequisites) are met.
+To add a new cluster hosted on GKE to your project:
+
+1. Navigate to your project's **CI/CD > Clusters** page.
+1. Click on **Add cluster**.
+1. Click on **Create with GKE**.
+1. Connect your Google account if you haven't done already by clicking the
+ **Sign in with Google** button.
+1. Fill in the requested values:
+ - **Cluster name** (required) - The name you wish to give the cluster.
+ - **GCP project ID** (required) - The ID of the project you created in your GCP
+ console that will host the Kubernetes cluster. This must **not** be confused
+ with the project name. Learn more about [Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
+ - **Zone** - The [zone](https://cloud.google.com/compute/docs/regions-zones/)
+ under which the cluster will be created.
+ - **Number of nodes** - The number of nodes you wish the cluster to have.
+ - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
+ of the Virtual Machine instance that the cluster will be based on.
+ - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster.
+1. Finally, click the **Create cluster** button.
+
+After a few moments, your cluster should be created. If something goes wrong,
+you will be notified.
+
+You can now proceed to install some pre-defined applications and then
+enable the Cluster integration.
+
+## Adding an existing Kubernetes cluster
NOTE: **Note:**
-You need Master [permissions] and above to add a cluster.
-
-There are two options when adding a new cluster; either use Google Kubernetes
-Engine (GKE) or provide the credentials to your own Kubernetes cluster.
-
-To add a new cluster:
-
-1. Navigate to your project's **CI/CD > Cluster** page
-1. If you want to let GitLab create a cluster on GKE for you, go through the
- following steps, otherwise skip to the next one.
- 1. Click on **Create with GKE**
- 1. Connect your Google account if you haven't done already by clicking the
- **Sign in with Google** button
- 1. Fill in the requested values:
- - **Cluster name** (required) - The name you wish to give the cluster.
- - **GCP project ID** (required) - The ID of the project you created in your GCP
- console that will host the Kubernetes cluster. This must **not** be confused
- with the project name. Learn more about [Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
- - **Zone** - The [zone](https://cloud.google.com/compute/docs/regions-zones/)
- under which the cluster will be created.
- - **Number of nodes** - The number of nodes you wish the cluster to have.
- - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
- of the Virtual Machine instance that the cluster will be based on.
- - **Project namespace** - The unique namespace for this project. By default you
- don't have to fill it in; by leaving it blank, GitLab will create one for you.
-1. If you want to use your own existing Kubernetes cluster, click on
- **Add an existing cluster** and fill in the details as described in the
- [Kubernetes integration](../integrations/kubernetes.md) documentation.
-1. Finally, click the **Create cluster** button
+You need Master [permissions] and above to access the Clusters page.
+
+To add an existing Kubernetes cluster to your project:
+
+1. Navigate to your project's **CI/CD > Clusters** page.
+1. Click on **Add cluster**.
+1. Click on **Add an existing cluster** and fill in the details:
+ - **Cluster name** (required) - The name you wish to give the cluster.
+ - **Environment scope** (required)- The
+ [associated environment](#setting-the-environment-scope) to this cluster.
+ - **API URL** (required) -
+ It's the URL that GitLab uses to access the Kubernetes API. Kubernetes
+ exposes several APIs, we want the "base" URL that is common to all of them,
+ e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`.
+ - **CA certificate** (optional) -
+ If the API is using a self-signed TLS certificate, you'll also need to include
+ the `ca.crt` contents here.
+ - **Token** -
+ GitLab authenticates against Kubernetes using service tokens, which are
+ scoped to a particular `namespace`. If you don't have a service token yet,
+ you can follow the
+ [Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
+ to create one. You can also view or create service tokens in the
+ [Kubernetes dashboard](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#config)
+ (under **Config > Secrets**).
+ - **Project namespace** (optional) - The following apply:
+ - By default you don't have to fill it in; by leaving it blank, GitLab will
+ create one for you.
+ - Each project should have a unique namespace.
+ - The project namespace is not necessarily the namespace of the secret, if
+ you're using a secret with broader permissions, like the secret from `default`.
+ - You should **not** use `default` as the project namespace.
+ - If you or someone created a secret specifically for the project, usually
+ with limited permissions, the secret's namespace and project namespace may
+ be the same.
+1. Finally, click the **Create cluster** button.
+
+The Kubernetes service takes the following parameters:
After a few moments, your cluster should be created. If something goes wrong,
you will be notified.
@@ -85,6 +134,91 @@ added directly to your configured cluster. Those applications are needed for
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps](../../../topics/autodevops/index.md) or deploy your own web apps. |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications |
+## Setting the environment scope
+
+When adding more than one clusters, you need to differentiate them with an
+environment scope. The environment scope associates clusters and
+[environments](../../../ci/environments.md) in an 1:1 relationship similar to how the
+[environment-specific variables](../../../ci/variables/README.md#limiting-environment-scopes-of-secret-variables)
+work.
+
+The default environment scope is `*`, which means all jobs, regardless of their
+environment, will use that cluster. Each scope can only be used by a single
+cluster in a project, and a validation error will occur if otherwise.
+
+---
+
+For example, let's say the following clusters exist in a project:
+
+| Cluster | Environment scope |
+| ---------- | ------------------- |
+| Development| `*` |
+| Staging | `staging/*` |
+| Production | `production/*` |
+
+And the following environments are set in [`.gitlab-ci.yml`](../../../ci/yaml/README.md):
+
+```yaml
+stages:
+- test
+- deploy
+
+test:
+ stage: test
+ script: sh test
+
+deploy to staging:
+ stage: deploy
+ script: make deploy
+ environment:
+ name: staging/$CI_COMMIT_REF_NAME
+ url: https://staging.example.com/
+
+deploy to production:
+ stage: deploy
+ script: make deploy
+ environment:
+ name: production/$CI_COMMIT_REF_NAME
+ url: https://example.com/
+```
+
+The result will then be:
+
+- The development cluster will be used for the "test" job.
+- The staging cluster will be used for the "deploy to staging" job.
+- The production cluster will be used for the "deploy to production" job.
+
+## Multiple Kubernetes clusters
+
+> Introduced in [GitLab Enterprise Edition Premium][ee] 10.3.
+
+With GitLab EEP, you can associate more than one Kubernetes clusters to your
+project. That way you can have different clusters for different environments,
+like dev, staging, production, etc.
+
+To add another cluster, follow the same steps as described in [adding a
+Kubernetes cluster](#adding-a-kubernetes-cluster) and make sure to
+[set an environment scope](#setting-the-environment-scope) that will
+differentiate the new cluster with the rest.
+
+## Deployment variables
+
+The Kubernetes cluster integration exposes the following
+[deployment variables](../../../ci/variables/README.md#deployment-variables) in the
+GitLab CI/CD build environment:
+
+- `KUBE_URL` - Equal to the API URL.
+- `KUBE_TOKEN` - The Kubernetes token.
+- `KUBE_NAMESPACE` - The Kubernetes namespace is auto-generated if not specified.
+ The default value is `<project_name>-<project_id>`. You can overwrite it to
+ use different one if needed, otherwise the `KUBE_NAMESPACE` variable will
+ receive the default value.
+- `KUBE_CA_PEM_FILE` - Only present if a custom CA bundle was specified. Path
+ to a file containing PEM data.
+- `KUBE_CA_PEM` (deprecated) - Only if a custom CA bundle was specified. Raw PEM data.
+- `KUBECONFIG` - Path to a file containing `kubeconfig` for this deployment.
+ CA bundle would be embedded if specified.
+
## Enabling or disabling the Cluster integration
After you have successfully added your cluster information, you can enable the
@@ -111,4 +245,62 @@ To remove the Cluster integration from your project, simply click on the
**Remove integration** button. You will then be able to follow the procedure
and [add a cluster](#adding-a-cluster) again.
+## What you can get with the Kubernetes integration
+
+Here's what you can do with GitLab if you enable the Kubernetes integration.
+
+### Deploy Boards (EEP)
+
+> Available in [GitLab Enterprise Edition Premium][ee].
+
+GitLab's Deploy Boards offer a consolidated view of the current health and
+status of each CI [environment](../../../ci/environments.md) running on Kubernetes,
+displaying the status of the pods in the deployment. Developers and other
+teammates can view the progress and status of a rollout, pod by pod, in the
+workflow they already use without any need to access Kubernetes.
+
+[> Read more about Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html)
+
+### Canary Deployments (EEP)
+
+> Available in [GitLab Enterprise Edition Premium][ee].
+
+Leverage [Kubernetes' Canary deployments](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments)
+and visualize your canary deployments right inside the Deploy Board, without
+the need to leave GitLab.
+
+[> Read more about Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html)
+
+### Kubernetes monitoring
+
+Automatically detect and monitor Kubernetes metrics. Automatic monitoring of
+[NGINX ingress](../integrations/prometheus_library/nginx.md) is also supported.
+
+[> Read more about Kubernetes monitoring](../integrations/prometheus_library/kubernetes.md)
+
+### Auto DevOps
+
+Auto DevOps automatically detects, builds, tests, deploys, and monitors your
+applications.
+
+To make full use of Auto DevOps(Auto Deploy, Auto Review Apps, and Auto Monitoring)
+you will need the Kubernetes project integration enabled.
+
+[> Read more about Auto DevOps](../../../topics/autodevops/index.md)
+
+### Web terminals
+
+NOTE: **Note:**
+Introduced in GitLab 8.15. You must be the project owner or have `master` permissions
+to use terminals. Support is limited to the first container in the
+first pod of your environment.
+
+When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
+support to your [environments](../../../ci/environments.md). This is based on the `exec` functionality found in
+Docker and Kubernetes, so you get a new shell session within your existing
+containers. To use this integration, you should deploy to Kubernetes using
+the deployment variables above, ensuring any pods you create are labelled with
+`app=$CI_ENVIRONMENT_SLUG`. GitLab will do the rest!
+
[permissions]: ../../permissions.md
+[ee]: https://about.gitlab.com/gitlab-ee/
diff --git a/doc/user/project/integrations/emails_on_push.md b/doc/user/project/integrations/emails_on_push.md
index 18109fc049c..c01da883562 100644
--- a/doc/user/project/integrations/emails_on_push.md
+++ b/doc/user/project/integrations/emails_on_push.md
@@ -10,7 +10,7 @@ In the _Recipients_ area, provide a list of emails separated by commas.
You can configure any of the following settings depending on your preference.
-+ **Push events** - Email will be triggered when a push event is recieved
++ **Push events** - Email will be triggered when a push event is received
+ **Tag push events** - Email will be triggered when a tag is created and pushed
+ **Send from committer** - Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. `user@gitlab.com`).
+ **Disable code diffs** - Don't include possibly sensitive code diffs in notification body.
diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md
index 710cf78e84f..543baaa81e1 100644
--- a/doc/user/project/integrations/kubernetes.md
+++ b/doc/user/project/integrations/kubernetes.md
@@ -2,11 +2,15 @@
last_updated: 2017-12-28
---
-CAUTION: **Warning:**
-Kubernetes service integration has been deprecated in GitLab 10.3. If the service is active the cluster information still be editable, however we advised to disable and reconfigure the clusters using the new [Clusters](../clusters/index.md) page. If the service is inactive the fields will be uneditable. Read [GitLab 10.3 release post](https://about.gitlab.com/2017/12/22/gitlab-10-3-released/#kubernetes-integration-service) for more information.
-
# GitLab Kubernetes / OpenShift integration
+CAUTION: **Warning:**
+The Kubernetes service integration has been deprecated in GitLab 10.3. If the
+service is active, the cluster information will still be editable, however we
+advise to disable and reconfigure the clusters using the new
+[Clusters](../clusters/index.md) page. If the service is inactive, the fields
+will not be editable. Read [GitLab 10.3 release post](https://about.gitlab.com/2017/12/22/gitlab-10-3-released/#kubernetes-integration-service) for more information.
+
GitLab can be configured to interact with Kubernetes, or other systems using the
Kubernetes API (such as OpenShift).
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 82175c70e49..19df78f4140 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -316,7 +316,7 @@ X-Gitlab-Event: Issue Hook
Triggered when a new comment is made on commits, merge requests, issues, and code snippets.
The note data will be stored in `object_attributes` (e.g. `note`, `noteable_type`). The
payload will also include information about the target of the comment. For example,
-a comment on a issue will include the specific issue information under the `issue` key.
+a comment on an issue will include the specific issue information under the `issue` key.
Valid target types:
1. `commit`
diff --git a/doc/user/project/issues/crosslinking_issues.md b/doc/user/project/issues/crosslinking_issues.md
index 5cc7ea383ae..cc8988be36b 100644
--- a/doc/user/project/issues/crosslinking_issues.md
+++ b/doc/user/project/issues/crosslinking_issues.md
@@ -40,7 +40,7 @@ issues around that same idea.
You do that as explained above, when
[mentioning an issue from a commit message](#from-commit-messages).
-When mentioning the issue "A" in a issue "B", the issue "A" will also
+When mentioning the issue "A" in issue "B", the issue "A" will also
display a notification in its tracker. The same is valid for mentioning
issues in merge requests.
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 20249926910..27832b0fa2b 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -52,7 +52,7 @@ special options available when filtering by milestone:
The milestone sidebar shows percentage complete, start date and due date,
issues, total issue weight, total issue time spent, and merge requests.
-The percentage complete is calcualted as: Closed and merged merge requests plus all closed issues divided by
+The percentage complete is calculated as: Closed and merged merge requests plus all closed issues divided by
total merge requests and issues.
![Milestone sidebar](img/sidebar.png)
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index 17dcd152363..15455a54627 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -91,7 +91,7 @@ to steal the tokens of other jobs.
Since 9.0 [pipeline triggers][triggers] do support the new permission model.
The new triggers do impersonate their associated user including their access
-to projects and their project permissions. To migrate trigger to use new permisison
+to projects and their project permissions. To migrate trigger to use new permission
model use **Take ownership**.
## Before GitLab 8.12
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index 3ab88948fbd..f52f66f518a 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -373,7 +373,7 @@ configuration.
If the case of `404.html`, there are different scenarios. For example:
- If you use project Pages (served under `/projectname/`) and try to access
- `/projectname/non/exsiting_file`, GitLab Pages will try to serve first
+ `/projectname/non/existing_file`, GitLab Pages will try to serve first
`/projectname/404.html`, and then `/404.html`.
- If you use user/group Pages (served under `/`) and try to access
`/non/existing_file` GitLab Pages will try to serve `/404.html`.
diff --git a/doc/user/project/pipelines/img/pipeline_schedule_play.png b/doc/user/project/pipelines/img/pipeline_schedule_play.png
new file mode 100644
index 00000000000..f594ceee19d
--- /dev/null
+++ b/doc/user/project/pipelines/img/pipeline_schedule_play.png
Binary files differ
diff --git a/doc/user/project/pipelines/img/pipeline_schedules_list.png b/doc/user/project/pipelines/img/pipeline_schedules_list.png
index 50d9d184b05..2ab2061db94 100644
--- a/doc/user/project/pipelines/img/pipeline_schedules_list.png
+++ b/doc/user/project/pipelines/img/pipeline_schedules_list.png
Binary files differ
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index 2101e3b1d58..34809a2826f 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -31,6 +31,20 @@ is installed on.
![Schedules list](img/pipeline_schedules_list.png)
+### Running a scheduled pipeline manually
+
+> [Introduced][ce-15700] in GitLab 10.4.
+
+To trigger a pipeline schedule manually, click the "Play" button:
+
+![Play Pipeline Schedule](img/pipeline_schedule_play.png)
+
+This will schedule a background job to run the pipeline schedule. A flash
+message will provide a link to the CI/CD Pipeline index page.
+
+To help avoid abuse, users are rate limited to triggering a pipeline once per
+minute.
+
### Making use of scheduled pipeline variables
> [Introduced][ce-12328] in GitLab 9.4.
@@ -90,4 +104,5 @@ don't have admin access to the server, ask your administrator.
[ce-10533]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10533
[ce-10853]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10853
[ce-12328]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12328
+[ce-15700]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15700
[settings]: https://about.gitlab.com/gitlab-com/settings/#cron-jobs
diff --git a/doc/workflow/lfs/img/lfs-icon.png b/doc/workflow/lfs/img/lfs-icon.png
new file mode 100644
index 00000000000..eef9a14187a
--- /dev/null
+++ b/doc/workflow/lfs/img/lfs-icon.png
Binary files differ
diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
index f7b87dee8e1..ce7895780c3 100644
--- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
@@ -4,6 +4,11 @@ Managing large files such as audio, video and graphics files has always been one
of the shortcomings of Git. The general recommendation is to not have Git repositories
larger than 1GB to preserve performance.
+![Git LFS tracking status](img/lfs-icon.png)
+
+An LFS icon is shown on files tracked by Git LFS to denote if a file is stored
+as a blob or as an LFS pointer.
+
## How it works
Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication
diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md
index 3e2e7d0f7b6..37265a5b771 100644
--- a/doc/workflow/notifications.md
+++ b/doc/workflow/notifications.md
@@ -65,7 +65,7 @@ Below is the table of events users can be notified of:
| Group access level changed | User | Sent when user group access level is changed |
| Project moved | Project members [1] | [1] not disabled |
-### Issue / Merge Request events
+### Issue / Merge request events
In all of the below cases, the notification will be sent to:
- Participants:
@@ -104,3 +104,33 @@ You won't receive notifications for Issues, Merge Requests or Milestones
created by yourself. You will only receive automatic notifications when
somebody else comments or adds changes to the ones that you've created or
mentions you.
+
+### Email Headers
+
+Notification emails include headers that provide extra content about the notification received:
+
+| Header | Description |
+|-----------------------------|-------------------------------------------------------------------------|
+| X-GitLab-Project | The name of the project the notification belongs to |
+| X-GitLab-Project-Id | The ID of the project |
+| X-GitLab-Project-Path | The path of the project |
+| X-GitLab-(Resource)-ID | The ID of the resource the notification is for, where resource is `Issue`, `MergeRequest`, `Commit`, etc|
+| X-GitLab-Discussion-ID | Only in comment emails, the ID of the discussion the comment is from |
+| X-GitLab-Pipeline-Id | Only in pipeline emails, the ID of the pipeline the notification is for |
+| X-GitLab-Reply-Key | A unique token to support reply by email |
+| X-GitLab-NotificationReason | The reason for being notified. "mentioned", "assigned", etc |
+
+#### X-GitLab-NotificationReason
+This header holds the reason for the notification to have been sent out,
+where reason can be `mentioned`, `assigned`, `own_activity`, etc.
+Only one reason is sent out according to its priority:
+- `own_activity`
+- `assigned`
+- `mentioned`
+
+The reason in this header will also be shown in the footer of the notification email. For example an email with the
+reason `assigned` will have this sentence in the footer:
+`"You are receiving this email because you have been assigned an item on {configured GitLab hostname}"`
+
+**Note: Only reasons listed above have been implemented so far**
+Further implementation is [being discussed here](https://gitlab.com/gitlab-org/gitlab-ce/issues/42062)
diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature
index 1af05b3c326..d121222308d 100644
--- a/features/project/issues/milestones.feature
+++ b/features/project/issues/milestones.feature
@@ -18,12 +18,15 @@ Feature: Project Issues Milestones
Given I click link "New Milestone"
And I submit new milestone "v2.3"
Then I should see milestone "v2.3"
- Given I click link to remove milestone
+ Given I click button to remove milestone
+ And I confirm in modal
When I visit project "Shop" activity page
Then I should see deleted milestone activity
+ @javascript
Scenario: I delete new milestone
- Given I click link to remove milestone
+ Given I click button to remove milestone
+ And I confirm in modal
And I should see no milestones
@javascript
diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb
index 33a24e8913a..4ce67aa651c 100644
--- a/features/steps/project/issues/milestones.rb
+++ b/features/steps/project/issues/milestones.rb
@@ -3,7 +3,6 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
include SharedProject
include SharedPaths
include SharedMarkdown
- include CapybaraHelpers
step 'I should see milestone "v2.2"' do
milestone = @project.milestones.find_by(title: "v2.2")
@@ -65,8 +64,12 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
expect(page).to have_selector('#tab-issues li.issuable-row', count: 4)
end
- step 'I click link to remove milestone' do
- confirm_modal_if_present { click_link 'Delete' }
+ step 'I click button to remove milestone' do
+ click_button 'Delete'
+ end
+
+ step 'I confirm in modal' do
+ click_button 'Delete milestone'
end
step 'I should see no milestones' do
diff --git a/features/steps/user.rb b/features/steps/user.rb
deleted file mode 100644
index 321c1e942d5..00000000000
--- a/features/steps/user.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-class Spinach::Features::User < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedUser
- include SharedProject
-
- step 'I should see user "John Doe" page' do
- expect(title).to match(/^\s*John Doe/)
- end
-
- step '"John Doe" has contributions' do
- user = User.find_by(name: 'John Doe')
- project = contributed_project
-
- # Issue contribution
- issue_params = { title: 'Bug in old browser' }
- Issues::CreateService.new(project, user, issue_params).execute
-
- # Push code contribution
- event = create(:push_event, project: project, author: user)
-
- create(:push_event_payload, event: event, commit_count: 3)
- end
-
- step 'I should see contributed projects' do
- page.within '#contributed' do
- expect(page).to have_content(@contributed_project.name)
- end
- end
-
- step 'I should see contributions calendar' do
- expect(page).to have_css('.js-contrib-calendar')
- end
-
- def contributed_project
- @contributed_project ||= create(:project, :public, :empty_repo)
- end
-end
diff --git a/features/support/capybara_helpers.rb b/features/support/capybara_helpers.rb
deleted file mode 100644
index 647f8d087c3..00000000000
--- a/features/support/capybara_helpers.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module CapybaraHelpers
- def confirm_modal_if_present
- if Capybara.current_driver == Capybara.javascript_driver
- accept_confirm { yield }
- return
- end
-
- yield
- end
-end
diff --git a/features/support/db_cleaner.rb b/features/support/db_cleaner.rb
index 8294bb1445f..31c922d23c3 100644
--- a/features/support/db_cleaner.rb
+++ b/features/support/db_cleaner.rb
@@ -1,6 +1,6 @@
require 'database_cleaner'
-DatabaseCleaner[:active_record].strategy = :truncation
+DatabaseCleaner[:active_record].strategy = :deletion
Spinach.hooks.before_scenario do
DatabaseCleaner.start
diff --git a/features/user.feature b/features/user.feature
deleted file mode 100644
index e0cadba30a1..00000000000
--- a/features/user.feature
+++ /dev/null
@@ -1,86 +0,0 @@
-Feature: User
- Background:
- Given User "John Doe" exists
- And "John Doe" owns private project "Enterprise"
-
- # Signed out
-
- @javascript
- Scenario: I visit user "John Doe" page while not signed in when he owns a public project
- Given "John Doe" owns internal project "Internal"
- And "John Doe" owns public project "Community"
- When I visit user "John Doe" page
- And I click on "Personal projects" tab
- Then I should see user "John Doe" page
- And I should not see project "Enterprise"
- And I should not see project "Internal"
- And I should see project "Community"
-
- # Signed in as someone else
-
- @javascript
- Scenario: I visit user "John Doe" page while signed in as someone else when he owns a public project
- Given "John Doe" owns public project "Community"
- And "John Doe" owns internal project "Internal"
- And I sign in as a user
- When I visit user "John Doe" page
- And I click on "Personal projects" tab
- Then I should see user "John Doe" page
- And I should not see project "Enterprise"
- And I should see project "Internal"
- And I should see project "Community"
-
- @javascript
- Scenario: I visit user "John Doe" page while signed in as someone else when he is not authorized to a public project
- Given "John Doe" owns internal project "Internal"
- And I sign in as a user
- When I visit user "John Doe" page
- And I click on "Personal projects" tab
- Then I should see user "John Doe" page
- And I should not see project "Enterprise"
- And I should see project "Internal"
- And I should not see project "Community"
-
- @javascript
- Scenario: I visit user "John Doe" page while signed in as someone else when he is not authorized to a project I can see
- Given I sign in as a user
- When I visit user "John Doe" page
- And I click on "Personal projects" tab
- Then I should see user "John Doe" page
- And I should not see project "Enterprise"
- And I should not see project "Internal"
- And I should not see project "Community"
-
- # Signed in as the user himself
-
- @javascript
- Scenario: I visit user "John Doe" page while signed in as "John Doe" when he has a public project
- Given "John Doe" owns internal project "Internal"
- And "John Doe" owns public project "Community"
- And I sign in as "John Doe"
- When I visit user "John Doe" page
- And I click on "Personal projects" tab
- Then I should see user "John Doe" page
- And I should see project "Enterprise"
- And I should see project "Internal"
- And I should see project "Community"
-
- @javascript
- Scenario: I visit user "John Doe" page while signed in as "John Doe" when he has no public project
- Given I sign in as "John Doe"
- When I visit user "John Doe" page
- And I click on "Personal projects" tab
- Then I should see user "John Doe" page
- And I should see project "Enterprise"
- And I should not see project "Internal"
- And I should not see project "Community"
-
- @javascript
- Scenario: "John Doe" contribution profile
- Given I sign in as a user
- And "John Doe" has contributions
- When I visit user "John Doe" page
- And I click on "Contributed projects" tab
- Then I should see user "John Doe" page
- And I should see contributed projects
- And I should see contributions calendar
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ae161efb358..f3f64244589 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -106,6 +106,7 @@ module API
# Keep in alphabetical order
mount ::API::AccessRequests
+ mount ::API::Applications
mount ::API::AwardEmoji
mount ::API::Boards
mount ::API::Branches
diff --git a/lib/api/applications.rb b/lib/api/applications.rb
new file mode 100644
index 00000000000..b122cdefe4e
--- /dev/null
+++ b/lib/api/applications.rb
@@ -0,0 +1,27 @@
+module API
+ # External applications API
+ class Applications < Grape::API
+ before { authenticated_as_admin! }
+
+ resource :applications do
+ desc 'Create a new application' do
+ detail 'This feature was introduced in GitLab 10.5'
+ success Entities::ApplicationWithSecret
+ end
+ params do
+ requires :name, type: String, desc: 'Application name'
+ requires :redirect_uri, type: String, desc: 'Application redirect URI'
+ requires :scopes, type: String, desc: 'Application scopes'
+ end
+ post do
+ application = Doorkeeper::Application.new(declared_params)
+
+ if application.save
+ present application, with: Entities::ApplicationWithSecret
+ else
+ render_validation_error! application
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 281269b1190..b0b7b50998f 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -4,6 +4,16 @@ module API
before { authenticate! }
+ helpers do
+ def add_deploy_keys_project(project, attrs = {})
+ project.deploy_keys_projects.create(attrs)
+ end
+
+ def find_by_deploy_key(project, key_id)
+ project.deploy_keys_projects.find_by!(deploy_key: key_id)
+ end
+ end
+
desc 'Return all deploy keys'
params do
use :pagination
@@ -21,28 +31,31 @@ module API
before { authorize_admin_project }
desc "Get a specific project's deploy keys" do
- success Entities::SSHKey
+ success Entities::DeployKeysProject
end
params do
use :pagination
end
get ":id/deploy_keys" do
- present paginate(user_project.deploy_keys), with: Entities::SSHKey
+ keys = user_project.deploy_keys_projects.preload(:deploy_key)
+
+ present paginate(keys), with: Entities::DeployKeysProject
end
desc 'Get single deploy key' do
- success Entities::SSHKey
+ success Entities::DeployKeysProject
end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
get ":id/deploy_keys/:key_id" do
- key = user_project.deploy_keys.find params[:key_id]
- present key, with: Entities::SSHKey
+ key = find_by_deploy_key(user_project, params[:key_id])
+
+ present key, with: Entities::DeployKeysProject
end
desc 'Add new deploy key to currently authenticated user' do
- success Entities::SSHKey
+ success Entities::DeployKeysProject
end
params do
requires :key, type: String, desc: 'The new deploy key'
@@ -53,24 +66,31 @@ module API
params[:key].strip!
# Check for an existing key joined to this project
- key = user_project.deploy_keys.find_by(key: params[:key])
+ key = user_project.deploy_keys_projects
+ .joins(:deploy_key)
+ .find_by(keys: { key: params[:key] })
+
if key
- present key, with: Entities::SSHKey
+ present key, with: Entities::DeployKeysProject
break
end
# Check for available deploy keys in other projects
key = current_user.accessible_deploy_keys.find_by(key: params[:key])
if key
- user_project.deploy_keys << key
- present key, with: Entities::SSHKey
+ added_key = add_deploy_keys_project(user_project, deploy_key: key, can_push: !!params[:can_push])
+
+ present added_key, with: Entities::DeployKeysProject
break
end
# Create a new deploy key
- key = DeployKey.new(declared_params(include_missing: false))
- if key.valid? && user_project.deploy_keys << key
- present key, with: Entities::SSHKey
+ key_attributes = { can_push: !!params[:can_push],
+ deploy_key_attributes: declared_params.except(:can_push) }
+ key = add_deploy_keys_project(user_project, key_attributes)
+
+ if key.valid?
+ present key, with: Entities::DeployKeysProject
else
render_validation_error!(key)
end
@@ -86,14 +106,21 @@ module API
at_least_one_of :title, :can_push
end
put ":id/deploy_keys/:key_id" do
- key = DeployKey.find(params.delete(:key_id))
+ deploy_keys_project = find_by_deploy_key(user_project, params[:key_id])
- authorize!(:update_deploy_key, key)
+ authorize!(:update_deploy_key, deploy_keys_project.deploy_key)
- if key.update_attributes(declared_params(include_missing: false))
- present key, with: Entities::SSHKey
+ can_push = params[:can_push].nil? ? deploy_keys_project.can_push : params[:can_push]
+ title = params[:title] || deploy_keys_project.deploy_key.title
+
+ result = deploy_keys_project.update_attributes(can_push: can_push,
+ deploy_key_attributes: { id: params[:key_id],
+ title: title })
+
+ if result
+ present deploy_keys_project, with: Entities::DeployKeysProject
else
- render_validation_error!(key)
+ render_validation_error!(deploy_keys_project)
end
end
@@ -122,7 +149,7 @@ module API
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
delete ":id/deploy_keys/:key_id" do
- key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ key = user_project.deploy_keys.find(params[:key_id])
not_found!('Deploy Key') unless key
destroy_conditionally!(key)
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index c4ef2c74658..7b9a80a234b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -65,12 +65,12 @@ module API
end
class Hook < Grape::Entity
- expose :id, :url, :created_at, :push_events, :tag_push_events, :repository_update_events
+ expose :id, :url, :created_at, :push_events, :tag_push_events, :merge_requests_events, :repository_update_events
expose :enable_ssl_verification
end
class ProjectHook < Hook
- expose :project_id, :issues_events, :merge_requests_events
+ expose :project_id, :issues_events
expose :note_events, :pipeline_events, :wiki_page_events
expose :job_events
end
@@ -554,13 +554,18 @@ module API
end
class SSHKey < Grape::Entity
- expose :id, :title, :key, :created_at, :can_push
+ expose :id, :title, :key, :created_at
end
class SSHKeyWithUser < SSHKey
expose :user, using: Entities::UserPublic
end
+ class DeployKeysProject < Grape::Entity
+ expose :deploy_key, merge: true, using: Entities::SSHKey
+ expose :can_push
+ end
+
class GPGKey < Grape::Entity
expose :id, :key, :created_at
end
@@ -714,10 +719,7 @@ module API
expose :job_events
# Expose serialized properties
expose :properties do |service, options|
- field_names = service.fields
- .select { |field| options[:include_passwords] || field[:type] != 'password' }
- .map { |field| field[:name] }
- service.properties.slice(*field_names)
+ service.properties.slice(*service.api_field_names)
end
end
@@ -1155,5 +1157,15 @@ module API
pages_domain
end
end
+
+ class Application < Grape::Entity
+ expose :uid, as: :application_id
+ expose :redirect_uri, as: :callback_url
+ end
+
+ # Use with care, this exposes the secret
+ class ApplicationWithSecret < Application
+ expose :secret
+ end
end
end
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index eff1c5b70ea..eb67de81a0d 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -1,11 +1,6 @@
module API
module Helpers
module InternalHelpers
- SSH_GITALY_FEATURES = {
- 'git-receive-pack' => [:ssh_receive_pack, Gitlab::GitalyClient::MigrationStatus::OPT_IN],
- 'git-upload-pack' => [:ssh_upload_pack, Gitlab::GitalyClient::MigrationStatus::OPT_OUT]
- }.freeze
-
attr_reader :redirected_path
def wiki?
@@ -102,8 +97,14 @@ module API
# Return the Gitaly Address if it is enabled
def gitaly_payload(action)
- feature, status = SSH_GITALY_FEATURES[action]
- return unless feature && Gitlab::GitalyClient.feature_enabled?(feature, status: status)
+ return unless %w[git-receive-pack git-upload-pack].include?(action)
+
+ if action == 'git-receive-pack'
+ return unless Gitlab::GitalyClient.feature_enabled?(
+ :ssh_receive_pack,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT
+ )
+ end
{
repository: repository.gitaly_repository,
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 5446f6b54b1..130c6d6da71 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -22,7 +22,7 @@ module API
source = find_source(source_type, params[:id])
users = source.users
- users = users.merge(User.search(params[:query])) if params[:query]
+ users = users.merge(User.search(params[:query])) if params[:query].present?
present paginate(users), with: Entities::Member, source: source
end
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index 5bed58c2d63..39c03c40bab 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -143,7 +143,7 @@ module API
get ":id/snippets/:snippet_id/user_agent_detail" do
authenticated_as_admin!
- snippet = Snippet.find_by!(id: params[:id])
+ snippet = Snippet.find_by!(id: params[:snippet_id], project_id: params[:id])
return not_found!('UserAgentDetail') unless snippet.user_agent_detail
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 653126e79ea..8b5e4f8edcc 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -76,9 +76,9 @@ module API
def present_projects(projects, options = {})
projects = reorder_projects(projects)
- projects = projects.with_statistics if params[:statistics]
- projects = projects.with_issues_enabled if params[:with_issues_enabled]
+ projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled]
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
+ projects = projects.with_statistics if params[:statistics]
projects = paginate(projects)
if current_user
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index 614822509f0..c15c487deb4 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -2,7 +2,7 @@ module API
class ProtectedBranches < Grape::API
include PaginationParams
- BRANCH_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
+ BRANCH_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
before { authorize_admin_project }
diff --git a/lib/api/services.rb b/lib/api/services.rb
index a7f44e2869c..51e33e2c686 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -785,7 +785,7 @@ module API
service_params = declared_params(include_missing: false).merge(active: true)
if service.update_attributes(service_params)
- present service, with: Entities::ProjectService, include_passwords: current_user.admin?
+ present service, with: Entities::ProjectService
else
render_api_error!('400 Bad Request', 400)
end
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index 6b6a03e3300..c7a460df46a 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -26,6 +26,7 @@ module API
optional :token, type: String, desc: 'The token used to validate payloads'
optional :push_events, type: Boolean, desc: "Trigger hook on push events"
optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events"
+ optional :merge_requests_events, type: Boolean, desc: "Trigger hook on tag push events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
end
post do
diff --git a/lib/api/v3/deploy_keys.rb b/lib/api/v3/deploy_keys.rb
index b90e2061da3..47e54ca85a5 100644
--- a/lib/api/v3/deploy_keys.rb
+++ b/lib/api/v3/deploy_keys.rb
@@ -3,6 +3,16 @@ module API
class DeployKeys < Grape::API
before { authenticate! }
+ helpers do
+ def add_deploy_keys_project(project, attrs = {})
+ project.deploy_keys_projects.create(attrs)
+ end
+
+ def find_by_deploy_key(project, key_id)
+ project.deploy_keys_projects.find_by!(deploy_key: key_id)
+ end
+ end
+
get "deploy_keys" do
authenticated_as_admin!
@@ -18,25 +28,28 @@ module API
%w(keys deploy_keys).each do |path|
desc "Get a specific project's deploy keys" do
- success ::API::Entities::SSHKey
+ success ::API::Entities::DeployKeysProject
end
get ":id/#{path}" do
- present user_project.deploy_keys, with: ::API::Entities::SSHKey
+ keys = user_project.deploy_keys_projects.preload(:deploy_key)
+
+ present keys, with: ::API::Entities::DeployKeysProject
end
desc 'Get single deploy key' do
- success ::API::Entities::SSHKey
+ success ::API::Entities::DeployKeysProject
end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
get ":id/#{path}/:key_id" do
- key = user_project.deploy_keys.find params[:key_id]
- present key, with: ::API::Entities::SSHKey
+ key = find_by_deploy_key(user_project, params[:key_id])
+
+ present key, with: ::API::Entities::DeployKeysProject
end
desc 'Add new deploy key to currently authenticated user' do
- success ::API::Entities::SSHKey
+ success ::API::Entities::DeployKeysProject
end
params do
requires :key, type: String, desc: 'The new deploy key'
@@ -47,24 +60,31 @@ module API
params[:key].strip!
# Check for an existing key joined to this project
- key = user_project.deploy_keys.find_by(key: params[:key])
+ key = user_project.deploy_keys_projects
+ .joins(:deploy_key)
+ .find_by(keys: { key: params[:key] })
+
if key
- present key, with: ::API::Entities::SSHKey
+ present key, with: ::API::Entities::DeployKeysProject
break
end
# Check for available deploy keys in other projects
key = current_user.accessible_deploy_keys.find_by(key: params[:key])
if key
- user_project.deploy_keys << key
- present key, with: ::API::Entities::SSHKey
+ added_key = add_deploy_keys_project(user_project, deploy_key: key, can_push: !!params[:can_push])
+
+ present added_key, with: ::API::Entities::DeployKeysProject
break
end
# Create a new deploy key
- key = DeployKey.new(declared_params(include_missing: false))
- if key.valid? && user_project.deploy_keys << key
- present key, with: ::API::Entities::SSHKey
+ key_attributes = { can_push: !!params[:can_push],
+ deploy_key_attributes: declared_params.except(:can_push) }
+ key = add_deploy_keys_project(user_project, key_attributes)
+
+ if key.valid?
+ present key, with: ::API::Entities::DeployKeysProject
else
render_validation_error!(key)
end
diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb
index 64758dae7d3..2ccbb9da1c5 100644
--- a/lib/api/v3/entities.rb
+++ b/lib/api/v3/entities.rb
@@ -257,10 +257,7 @@ module API
expose :job_events, as: :build_events
# Expose serialized properties
expose :properties do |service, options|
- field_names = service.fields
- .select { |field| options[:include_passwords] || field[:type] != 'password' }
- .map { |field| field[:name] }
- service.properties.slice(*field_names)
+ service.properties.slice(*service.api_field_names)
end
end
diff --git a/lib/api/v3/members.rb b/lib/api/v3/members.rb
index de226e4e573..46145cac7a5 100644
--- a/lib/api/v3/members.rb
+++ b/lib/api/v3/members.rb
@@ -23,7 +23,7 @@ module API
source = find_source(source_type, params[:id])
users = source.users
- users = users.merge(User.search(params[:query])) if params[:query]
+ users = users.merge(User.search(params[:query])) if params[:query].present?
present paginate(users), with: ::API::Entities::Member, source: source
end
diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb
index 446f804124b..a7f0813bf74 100644
--- a/lib/api/v3/projects.rb
+++ b/lib/api/v3/projects.rb
@@ -175,7 +175,7 @@ module API
end
get "/search/:query", requirements: { query: /[^\/]+/ } do
search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
- projects = search_service.objects('projects', params[:page])
+ projects = search_service.objects('projects', params[:page], false)
projects = projects.reorder(params[:order_by] => params[:sort])
present paginate(projects), with: ::API::V3::Entities::Project
diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb
index 44ed94d2869..20ca1021c71 100644
--- a/lib/api/v3/services.rb
+++ b/lib/api/v3/services.rb
@@ -622,7 +622,7 @@ module API
end
get ":id/services/:service_slug" do
service = user_project.find_or_initialize_service(params[:service_slug].underscore)
- present service, with: Entities::ProjectService, include_passwords: current_user.admin?
+ present service, with: Entities::ProjectService
end
end
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index f6169b2c85d..9bdedeb6615 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -50,7 +50,7 @@ module Banzai
end
def process_link_to_upload_attr(html_attr)
- path_parts = [html_attr.value]
+ path_parts = [Addressable::URI.unescape(html_attr.value)]
if group
path_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
@@ -58,13 +58,13 @@ module Banzai
path_parts.unshift(relative_url_root, project.full_path)
end
- path = File.join(*path_parts)
+ path = Addressable::URI.escape(File.join(*path_parts))
html_attr.value =
if context[:only_path]
path
else
- URI.join(Gitlab.config.gitlab.base_url, path).to_s
+ Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s
end
end
diff --git a/lib/gitlab/background_migration/copy_column.rb b/lib/gitlab/background_migration/copy_column.rb
index a2cb215c230..ef70f37d5eb 100644
--- a/lib/gitlab/background_migration/copy_column.rb
+++ b/lib/gitlab/background_migration/copy_column.rb
@@ -28,6 +28,8 @@ module Gitlab
UPDATE #{quoted_table}
SET #{quoted_copy_to} = #{quoted_copy_from}
WHERE id BETWEEN #{start_id} AND #{end_id}
+ AND #{quoted_copy_from} IS NOT NULL
+ AND #{quoted_copy_to} IS NULL
SQL
end
diff --git a/lib/gitlab/bare_repository_import/repository.rb b/lib/gitlab/bare_repository_import/repository.rb
index c0c666dfb7b..fe267248275 100644
--- a/lib/gitlab/bare_repository_import/repository.rb
+++ b/lib/gitlab/bare_repository_import/repository.rb
@@ -1,3 +1,5 @@
+# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/953
+#
module Gitlab
module BareRepositoryImport
class Repository
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index ef92fc5a0a0..945d70e7a24 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -16,7 +16,7 @@ module Gitlab
lfs_objects_missing: 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'
}.freeze
- attr_reader :user_access, :project, :skip_authorization, :protocol
+ attr_reader :user_access, :project, :skip_authorization, :protocol, :oldrev, :newrev, :ref, :branch_name, :tag_name
def initialize(
change, user_access:, project:, skip_authorization: false,
@@ -51,9 +51,9 @@ module Gitlab
end
def branch_checks
- return unless @branch_name
+ return unless branch_name
- if deletion? && @branch_name == project.default_branch
+ if deletion? && branch_name == project.default_branch
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_default_branch]
end
@@ -61,7 +61,7 @@ module Gitlab
end
def protected_branch_checks
- return unless ProtectedBranch.protected?(project, @branch_name)
+ return unless ProtectedBranch.protected?(project, branch_name)
if forced_push?
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:force_push_protected_branch]
@@ -75,29 +75,29 @@ module Gitlab
end
def protected_branch_deletion_checks
- unless user_access.can_delete_branch?(@branch_name)
+ unless user_access.can_delete_branch?(branch_name)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_master_delete_protected_branch]
end
- unless protocol == 'web'
+ unless updated_from_web?
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_web_delete_protected_branch]
end
end
def protected_branch_push_checks
if matching_merge_request?
- unless user_access.can_merge_to_branch?(@branch_name) || user_access.can_push_to_branch?(@branch_name)
+ unless user_access.can_merge_to_branch?(branch_name) || user_access.can_push_to_branch?(branch_name)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:merge_protected_branch]
end
else
- unless user_access.can_push_to_branch?(@branch_name)
+ unless user_access.can_push_to_branch?(branch_name)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:push_protected_branch]
end
end
end
def tag_checks
- return unless @tag_name
+ return unless tag_name
if tag_exists? && user_access.cannot_do_action?(:admin_project)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:change_existing_tags]
@@ -107,40 +107,44 @@ module Gitlab
end
def protected_tag_checks
- return unless ProtectedTag.protected?(project, @tag_name)
+ return unless ProtectedTag.protected?(project, tag_name)
raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:update_protected_tag]) if update?
raise(GitAccess::UnauthorizedError, ERROR_MESSAGES[:delete_protected_tag]) if deletion?
- unless user_access.can_create_tag?(@tag_name)
+ unless user_access.can_create_tag?(tag_name)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_tag]
end
end
private
+ def updated_from_web?
+ protocol == 'web'
+ end
+
def tag_exists?
- project.repository.tag_exists?(@tag_name)
+ project.repository.tag_exists?(tag_name)
end
def forced_push?
- Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
+ Gitlab::Checks::ForcePush.force_push?(project, oldrev, newrev)
end
def update?
- !Gitlab::Git.blank_ref?(@oldrev) && !deletion?
+ !Gitlab::Git.blank_ref?(oldrev) && !deletion?
end
def deletion?
- Gitlab::Git.blank_ref?(@newrev)
+ Gitlab::Git.blank_ref?(newrev)
end
def matching_merge_request?
- Checks::MatchingMergeRequest.new(@newrev, @branch_name, @project).match?
+ Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
end
def lfs_objects_exist_check
- lfs_check = Checks::LfsIntegrity.new(project, @newrev)
+ lfs_check = Checks::LfsIntegrity.new(project, newrev)
if lfs_check.objects_missing?
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:lfs_objects_missing]
diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb
index eb606b57667..55658900628 100644
--- a/lib/gitlab/ci/config/entry/validators.rb
+++ b/lib/gitlab/ci/config/entry/validators.rb
@@ -64,10 +64,24 @@ module Gitlab
include LegacyValidationHelpers
def validate_each(record, attribute, value)
- unless validate_string(value)
+ if validate_string(value)
+ validate_path(record, attribute, value)
+ else
record.errors.add(attribute, 'should be a string or symbol')
end
end
+
+ private
+
+ def validate_path(record, attribute, value)
+ path = CGI.unescape(value.to_s)
+
+ if path.include?('/')
+ record.errors.add(attribute, 'cannot contain the "/" character')
+ elsif path == '.' || path == '..'
+ record.errors.add(attribute, 'cannot be "." or ".."')
+ end
+ end
end
class RegexpValidator < ActiveModel::EachValidator
diff --git a/lib/gitlab/database/grant.rb b/lib/gitlab/database/grant.rb
index 9f76967fc77..d32837f5793 100644
--- a/lib/gitlab/database/grant.rb
+++ b/lib/gitlab/database/grant.rb
@@ -12,30 +12,40 @@ module Gitlab
# Returns true if the current user can create and execute triggers on the
# given table.
def self.create_and_execute_trigger?(table)
- priv =
- if Database.postgresql?
- where(privilege_type: 'TRIGGER', table_name: table)
- .where('grantee = user')
- else
- queries = [
- Grant.select(1)
- .from('information_schema.user_privileges')
- .where("PRIVILEGE_TYPE = 'SUPER'")
- .where("GRANTEE = CONCAT('\\'', REPLACE(CURRENT_USER(), '@', '\\'@\\''), '\\'')"),
+ if Database.postgresql?
+ # We _must not_ use quote_table_name as this will produce double
+ # quotes on PostgreSQL and for "has_table_privilege" we need single
+ # quotes.
+ quoted_table = connection.quote(table)
- Grant.select(1)
- .from('information_schema.schema_privileges')
- .where("PRIVILEGE_TYPE = 'TRIGGER'")
- .where('TABLE_SCHEMA = ?', Gitlab::Database.database_name)
- .where("GRANTEE = CONCAT('\\'', REPLACE(CURRENT_USER(), '@', '\\'@\\''), '\\'')")
- ]
+ begin
+ from(nil)
+ .pluck("has_table_privilege(#{quoted_table}, 'TRIGGER')")
+ .first
+ rescue ActiveRecord::StatementInvalid
+ # This error is raised when using a non-existing table name. In this
+ # case we just want to return false as a user technically can't
+ # create triggers for such a table.
+ false
+ end
+ else
+ queries = [
+ Grant.select(1)
+ .from('information_schema.user_privileges')
+ .where("PRIVILEGE_TYPE = 'SUPER'")
+ .where("GRANTEE = CONCAT('\\'', REPLACE(CURRENT_USER(), '@', '\\'@\\''), '\\'')"),
- union = SQL::Union.new(queries).to_sql
+ Grant.select(1)
+ .from('information_schema.schema_privileges')
+ .where("PRIVILEGE_TYPE = 'TRIGGER'")
+ .where('TABLE_SCHEMA = ?', Gitlab::Database.database_name)
+ .where("GRANTEE = CONCAT('\\'', REPLACE(CURRENT_USER(), '@', '\\'@\\''), '\\'')")
+ ]
- Grant.from("(#{union}) privs")
- end
+ union = SQL::Union.new(queries).to_sql
- priv.any?
+ Grant.from("(#{union}) privs").any?
+ end
end
end
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 592a1956ceb..dbe6259fce7 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -525,8 +525,9 @@ module Gitlab
install_rename_triggers(table, column, temp_column)
# Schedule the jobs that will copy the data from the old column to the
- # new one.
- relation.each_batch(of: batch_size) do |batch, index|
+ # new one. Rows with NULL values in our source column are skipped since
+ # the target column is already NULL at this point.
+ relation.where.not(column => nil).each_batch(of: batch_size) do |batch, index|
start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
max_index = index
diff --git a/lib/gitlab/git/attributes_at_ref_parser.rb b/lib/gitlab/git/attributes_at_ref_parser.rb
new file mode 100644
index 00000000000..26b5bd520d5
--- /dev/null
+++ b/lib/gitlab/git/attributes_at_ref_parser.rb
@@ -0,0 +1,14 @@
+module Gitlab
+ module Git
+ # Parses root .gitattributes file at a given ref
+ class AttributesAtRefParser
+ delegate :attributes, to: :@parser
+
+ def initialize(repository, ref)
+ blob = repository.blob_at(ref, '.gitattributes')
+
+ @parser = AttributesParser.new(blob&.data)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/attributes.rb b/lib/gitlab/git/attributes_parser.rb
index 2d20cd473a7..d8aeabb6cba 100644
--- a/lib/gitlab/git/attributes.rb
+++ b/lib/gitlab/git/attributes_parser.rb
@@ -1,42 +1,26 @@
-# Gitaly note: JV: not sure what to make of this class. Why does it use
-# the full disk path of the repository to look up attributes This is
-# problematic in Gitaly, because Gitaly hides the full disk path to the
-# repository from gitlab-ce.
-
module Gitlab
module Git
# Class for parsing Git attribute files and extracting the attributes for
# file patterns.
- #
- # Unlike Rugged this parser only needs a single IO call (a call to `open`),
- # vastly reducing the time spent in extracting attributes.
- #
- # This class _only_ supports parsing the attributes file located at
- # `$GIT_DIR/info/attributes` as GitLab doesn't use any other files
- # (`.gitattributes` is copied to this particular path).
- #
- # Basic usage:
- #
- # attributes = Gitlab::Git::Attributes.new(some_repo.path)
- #
- # attributes.attributes('README.md') # => { "eol" => "lf }
- class Attributes
- # path - The path to the Git repository.
- def initialize(path)
- @path = File.expand_path(path)
- @patterns = nil
+ class AttributesParser
+ def initialize(attributes_data)
+ @data = attributes_data || ""
+
+ if @data.is_a?(File)
+ @patterns = parse_file
+ end
end
# Returns all the Git attributes for the given path.
#
- # path - A path to a file for which to get the attributes.
+ # file_path - A path to a file for which to get the attributes.
#
# Returns a Hash.
- def attributes(path)
- full_path = File.join(@path, path)
+ def attributes(file_path)
+ absolute_path = File.join('/', file_path)
patterns.each do |pattern, attrs|
- return attrs if File.fnmatch?(pattern, full_path)
+ return attrs if File.fnmatch?(pattern, absolute_path)
end
{}
@@ -98,16 +82,10 @@ module Gitlab
# Iterates over every line in the attributes file.
def each_line
- full_path = File.join(@path, 'info/attributes')
+ @data.each_line do |line|
+ break unless line.valid_encoding?
- return unless File.exist?(full_path)
-
- File.open(full_path, 'r') do |handle|
- handle.each_line do |line|
- break unless line.valid_encoding?
-
- yield line.strip
- end
+ yield line.strip
end
end
@@ -125,7 +103,8 @@ module Gitlab
parsed = attrs ? parse_attributes(attrs) : {}
- pairs << [File.join(@path, pattern), parsed]
+ absolute_pattern = File.join('/', pattern)
+ pairs << [absolute_pattern, parsed]
end
# Newer entries take precedence over older entries.
diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb
index 31effdba292..6d6ed065f79 100644
--- a/lib/gitlab/git/blame.rb
+++ b/lib/gitlab/git/blame.rb
@@ -42,9 +42,7 @@ module Gitlab
end
def load_blame_by_shelling_out
- cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{@repo.path} blame -p #{@sha} -- #{@path})
- # Read in binary mode to ensure ASCII-8BIT
- IO.popen(cmd, 'rb') {|io| io.read }
+ @repo.shell_blame(@sha, @path)
end
def process_raw_blame(output)
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 031fccba92b..13120120223 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -34,7 +34,7 @@ module Gitlab
def raw(repository, sha)
Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled|
if is_enabled
- Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
+ repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
else
rugged_raw(repository, sha, limit: MAX_DATA_DISPLAY_SIZE)
end
@@ -70,11 +70,17 @@ module Gitlab
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
- blob_ids.lazy
- .select { |sha| possible_lfs_blob?(repository, sha) }
- .map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
- .select(&:lfs_pointer?)
- .force
+ repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled|
+ if is_enabled
+ repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
+ else
+ blob_ids.lazy
+ .select { |sha| possible_lfs_blob?(repository, sha) }
+ .map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
+ .select(&:lfs_pointer?)
+ .force
+ end
+ end
end
def binary?(data)
@@ -132,6 +138,8 @@ module Gitlab
end
def find_by_gitaly(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE)
+ return unless path
+
path = path.sub(/\A\/*/, '')
path = '/' if path.empty?
name = File.basename(path)
@@ -173,6 +181,8 @@ module Gitlab
end
def find_by_rugged(repository, sha, path, limit:)
+ return unless path
+
rugged_commit = repository.lookup(sha)
root_tree = rugged_commit.tree
@@ -254,7 +264,7 @@ module Gitlab
Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled|
@data = begin
if is_enabled
- Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: id, limit: -1).data
+ repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data
else
repository.lookup(id).content
end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 46e0c0e82a2..768617e2cae 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -239,6 +239,24 @@ module Gitlab
end
end
end
+
+ def extract_signature(repository, commit_id)
+ repository.gitaly_migrate(:extract_commit_signature) do |is_enabled|
+ if is_enabled
+ repository.gitaly_commit_client.extract_signature(commit_id)
+ else
+ rugged_extract_signature(repository, commit_id)
+ end
+ end
+ end
+
+ def rugged_extract_signature(repository, commit_id)
+ begin
+ Rugged::Commit.extract_signature(repository.rugged, commit_id)
+ rescue Rugged::OdbError
+ nil
+ end
+ end
end
def initialize(repository, raw_commit, head = nil)
diff --git a/lib/gitlab/git/info_attributes.rb b/lib/gitlab/git/info_attributes.rb
new file mode 100644
index 00000000000..e79a440950b
--- /dev/null
+++ b/lib/gitlab/git/info_attributes.rb
@@ -0,0 +1,49 @@
+# Gitaly note: JV: not sure what to make of this class. Why does it use
+# the full disk path of the repository to look up attributes This is
+# problematic in Gitaly, because Gitaly hides the full disk path to the
+# repository from gitlab-ce.
+
+module Gitlab
+ module Git
+ # Parses gitattributes at `$GIT_DIR/info/attributes`
+ #
+ # Unlike Rugged this parser only needs a single IO call (a call to `open`),
+ # vastly reducing the time spent in extracting attributes.
+ #
+ # This class _only_ supports parsing the attributes file located at
+ # `$GIT_DIR/info/attributes` as GitLab doesn't use any other files
+ # (`.gitattributes` is copied to this particular path).
+ #
+ # Basic usage:
+ #
+ # attributes = Gitlab::Git::InfoAttributes.new(some_repo.path)
+ #
+ # attributes.attributes('README.md') # => { "eol" => "lf }
+ class InfoAttributes
+ delegate :attributes, :patterns, to: :parser
+
+ # path - The path to the Git repository.
+ def initialize(path)
+ @repo_path = File.expand_path(path)
+ end
+
+ def parser
+ @parser ||= begin
+ if File.exist?(attributes_path)
+ File.open(attributes_path, 'r') do |file_handle|
+ AttributesParser.new(file_handle)
+ end
+ else
+ AttributesParser.new("")
+ end
+ end
+ end
+
+ private
+
+ def attributes_path
+ @attributes_path ||= File.join(@repo_path, 'info/attributes')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb
index 1ccca13ce2f..e0bd2bbe47b 100644
--- a/lib/gitlab/git/popen.rb
+++ b/lib/gitlab/git/popen.rb
@@ -19,6 +19,8 @@ module Gitlab
cmd_output = ""
cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
+ stdout.set_encoding(Encoding::ASCII_8BIT)
+
yield(stdin) if block_given?
stdin.close
diff --git a/lib/gitlab/git/ref.rb b/lib/gitlab/git/ref.rb
index 372ce005b94..a3ba9475ad0 100644
--- a/lib/gitlab/git/ref.rb
+++ b/lib/gitlab/git/ref.rb
@@ -33,9 +33,9 @@ module Gitlab
object
end
- def initialize(repository, name, target, derefenced_target)
+ def initialize(repository, name, target, dereferenced_target)
@name = Gitlab::Git.ref_name(name)
- @dereferenced_target = derefenced_target
+ @dereferenced_target = dereferenced_target
@target = if target.respond_to?(:oid)
target.oid
elsif target.respond_to?(:name)
diff --git a/lib/gitlab/git/remote_mirror.rb b/lib/gitlab/git/remote_mirror.rb
index 38e9d2a8554..ebe46722890 100644
--- a/lib/gitlab/git/remote_mirror.rb
+++ b/lib/gitlab/git/remote_mirror.rb
@@ -6,7 +6,23 @@ module Gitlab
@ref_name = ref_name
end
- def update(only_branches_matching: [], only_tags_matching: [])
+ def update(only_branches_matching: [])
+ @repository.gitaly_migrate(:remote_update_remote_mirror) do |is_enabled|
+ if is_enabled
+ gitaly_update(only_branches_matching)
+ else
+ rugged_update(only_branches_matching)
+ end
+ end
+ end
+
+ private
+
+ def gitaly_update(only_branches_matching)
+ @repository.gitaly_remote_client.update_remote_mirror(@ref_name, only_branches_matching)
+ end
+
+ def rugged_update(only_branches_matching)
local_branches = refs_obj(@repository.local_branches, only_refs_matching: only_branches_matching)
remote_branches = refs_obj(@repository.remote_branches(@ref_name), only_refs_matching: only_branches_matching)
@@ -15,8 +31,8 @@ module Gitlab
delete_refs(local_branches, remote_branches)
- local_tags = refs_obj(@repository.tags, only_refs_matching: only_tags_matching)
- remote_tags = refs_obj(@repository.remote_tags(@ref_name), only_refs_matching: only_tags_matching)
+ local_tags = refs_obj(@repository.tags)
+ remote_tags = refs_obj(@repository.remote_tags(@ref_name))
updated_tags = changed_refs(local_tags, remote_tags)
@repository.push_remote_branches(@ref_name, updated_tags.keys) if updated_tags.present?
@@ -24,8 +40,6 @@ module Gitlab
delete_refs(local_tags, remote_tags)
end
- private
-
def refs_obj(refs, only_refs_matching: [])
refs.each_with_object({}) do |ref, refs|
next if only_refs_matching.present? && !only_refs_matching.include?(ref.name)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index c5d5c3a72c0..3a7930154e5 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -102,7 +102,7 @@ module Gitlab
)
@path = File.join(storage_path, @relative_path)
@name = @relative_path.split("/").last
- @attributes = Gitlab::Git::Attributes.new(path)
+ @attributes = Gitlab::Git::InfoAttributes.new(path)
end
def ==(other)
@@ -133,7 +133,7 @@ module Gitlab
end
def exists?
- Gitlab::GitalyClient.migrate(:repository_exists) do |enabled|
+ Gitlab::GitalyClient.migrate(:repository_exists, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled
gitaly_repository_client.exists?
else
@@ -468,9 +468,13 @@ module Gitlab
}
options = default_options.merge(options)
- options[:limit] ||= 0
options[:offset] ||= 0
+ limit = options[:limit]
+ if limit == 0 || !limit.is_a?(Integer)
+ raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}")
+ end
+
gitaly_migrate(:find_commits) do |is_enabled|
if is_enabled
gitaly_commit_client.find_commits(options)
@@ -563,6 +567,8 @@ module Gitlab
return false if ancestor_id.nil? || descendant_id.nil?
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
+ rescue Rugged::OdbError
+ false
end
# Returns true is +from+ is direct ancestor to +to+, otherwise false
@@ -612,11 +618,11 @@ module Gitlab
if is_enabled
gitaly_ref_client.find_ref_name(sha, ref_path)
else
- args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
+ args = %W(for-each-ref --count=1 #{ref_path} --contains #{sha})
# Not found -> ["", 0]
# Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
- popen(args, @path).first.split.last
+ run_git(args).first.split.last
end
end
end
@@ -885,8 +891,7 @@ module Gitlab
"delete #{ref}\x00\x00"
end
- command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z]
- message, status = popen(command, path) do |stdin|
+ message, status = run_git(%w[update-ref --stdin -z]) do |stdin|
stdin.write(instructions.join)
end
@@ -995,6 +1000,18 @@ module Gitlab
attributes(path)[name]
end
+ # Check .gitattributes for a given ref
+ #
+ # This only checks the root .gitattributes file,
+ # it does not traverse subfolders to find additional .gitattributes files
+ #
+ # This method is around 30 times slower than `attributes`,
+ # which uses `$GIT_DIR/info/attributes`
+ def attributes_at(ref, file_path)
+ parser = AttributesAtRefParser.new(self, ref)
+ parser.attributes(file_path)
+ end
+
def languages(ref = nil)
Gitlab::GitalyClient.migrate(:commit_languages) do |is_enabled|
if is_enabled
@@ -1095,19 +1112,6 @@ module Gitlab
end
end
- def shell_write_ref(ref_path, ref, old_ref)
- raise ArgumentError, "invalid ref_path #{ref_path.inspect}" if ref_path.include?(' ')
- raise ArgumentError, "invalid ref #{ref.inspect}" if ref.include?("\x00")
- raise ArgumentError, "invalid old_ref #{old_ref.inspect}" if !old_ref.nil? && old_ref.include?("\x00")
-
- input = "update #{ref_path}\x00#{ref}\x00#{old_ref}\x00"
- run_git!(%w[update-ref --stdin -z]) { |stdin| stdin.write(input) }
- end
-
- def rugged_write_ref(ref_path, ref)
- rugged.references.create(ref_path, ref, force: true)
- end
-
def fetch_ref(source_repository, source_ref:, target_ref:)
Gitlab::Git.check_namespace!(source_repository)
source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
@@ -1127,23 +1131,6 @@ module Gitlab
end
# Refactoring aid; allows us to copy code from app/models/repository.rb
- def run_git(args, chdir: path, env: {}, nice: false, &block)
- cmd = [Gitlab.config.git.bin_path, *args]
- cmd.unshift("nice") if nice
- circuit_breaker.perform do
- popen(cmd, chdir, env, &block)
- end
- end
-
- def run_git!(args, chdir: path, env: {}, nice: false, &block)
- output, status = run_git(args, chdir: chdir, env: env, nice: nice, &block)
-
- raise GitError, output unless status.zero?
-
- output
- end
-
- # Refactoring aid; allows us to copy code from app/models/repository.rb
def run_git_with_timeout(args, timeout, env: {})
circuit_breaker.perform do
popen_with_timeout([Gitlab.config.git.bin_path, *args], timeout, path, env)
@@ -1204,6 +1191,19 @@ module Gitlab
end
end
+ def create_from_bundle(bundle_path)
+ gitaly_migrate(:create_repo_from_bundle) do |is_enabled|
+ if is_enabled
+ gitaly_repository_client.create_from_bundle(bundle_path)
+ else
+ run_git!(%W(clone --bare -- #{bundle_path} #{path}), chdir: nil)
+ self.class.create_hooks(path, File.expand_path(Gitlab.config.gitlab_shell.hooks_path))
+ end
+ end
+
+ true
+ end
+
def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
gitaly_migrate(:rebase) do |is_enabled|
if is_enabled
@@ -1285,42 +1285,41 @@ module Gitlab
success || gitlab_projects_error
end
+ def bundle_to_disk(save_path)
+ gitaly_migrate(:bundle_to_disk) do |is_enabled|
+ if is_enabled
+ gitaly_repository_client.create_bundle(save_path)
+ else
+ run_git!(%W(bundle create #{save_path} --all))
+ end
+ end
+
+ true
+ end
+
# rubocop:disable Metrics/ParameterLists
def multi_action(
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
start_branch_name: nil, start_repository: self)
- OperationService.new(user, self).with_branch(
- branch_name,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- ) do |start_commit|
- index = Gitlab::Git::Index.new(self)
- parents = []
-
- if start_commit
- index.read_tree(start_commit.rugged_commit.tree)
- parents = [start_commit.sha]
+ gitaly_migrate(:operation_user_commit_files) do |is_enabled|
+ if is_enabled
+ gitaly_operation_client.user_commit_files(user, branch_name,
+ message, actions, author_email, author_name,
+ start_branch_name, start_repository)
+ else
+ rugged_multi_action(user, branch_name, message, actions,
+ author_email, author_name, start_branch_name, start_repository)
end
-
- actions.each { |opts| index.apply(opts.delete(:action), opts) }
-
- committer = user_to_committer(user)
- author = Gitlab::Git.committer_hash(email: author_email, name: author_name) || committer
- options = {
- tree: index.write_tree,
- message: message,
- parents: parents,
- author: author,
- committer: committer
- }
-
- create_commit(options)
end
end
# rubocop:enable Metrics/ParameterLists
+ def write_config(full_path:)
+ rugged.config['gitlab.fullpath'] = full_path if full_path.present?
+ end
+
def gitaly_repository
Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
end
@@ -1349,6 +1348,10 @@ module Gitlab
@gitaly_remote_client ||= Gitlab::GitalyClient::RemoteService.new(self)
end
+ def gitaly_blob_client
+ @gitaly_blob_client ||= Gitlab::GitalyClient::BlobService.new(self)
+ end
+
def gitaly_conflicts_client(our_commit_oid, their_commit_oid)
Gitlab::GitalyClient::ConflictsService.new(self, our_commit_oid, their_commit_oid)
end
@@ -1363,8 +1366,100 @@ module Gitlab
raise CommandError.new(e)
end
+ def refs_contains_sha(ref_type, sha)
+ args = %W(#{ref_type} --contains #{sha})
+ names = run_git(args).first
+
+ if names.respond_to?(:split)
+ names = names.split("\n").map(&:strip)
+
+ names.each do |name|
+ name.slice! '* '
+ end
+
+ names
+ else
+ []
+ end
+ end
+
+ def search_files_by_content(query, ref)
+ return [] if empty? || query.blank?
+
+ offset = 2
+ args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
+
+ run_git(args).first.scrub.split(/^--$/)
+ end
+
+ def search_files_by_name(query, ref)
+ safe_query = Regexp.escape(query.sub(/^\/*/, ""))
+
+ return [] if empty? || safe_query.blank?
+
+ args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query})
+
+ run_git(args).first.lines.map(&:strip)
+ end
+
+ def find_commits_by_message(query, ref, path, limit, offset)
+ gitaly_migrate(:commits_by_message) do |is_enabled|
+ if is_enabled
+ find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
+ else
+ find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
+ end
+ end
+ end
+
+ def shell_blame(sha, path)
+ output, _status = run_git(%W(blame -p #{sha} -- #{path}))
+ output
+ end
+
private
+ def shell_write_ref(ref_path, ref, old_ref)
+ raise ArgumentError, "invalid ref_path #{ref_path.inspect}" if ref_path.include?(' ')
+ raise ArgumentError, "invalid ref #{ref.inspect}" if ref.include?("\x00")
+ raise ArgumentError, "invalid old_ref #{old_ref.inspect}" if !old_ref.nil? && old_ref.include?("\x00")
+
+ input = "update #{ref_path}\x00#{ref}\x00#{old_ref}\x00"
+ run_git!(%w[update-ref --stdin -z]) { |stdin| stdin.write(input) }
+ end
+
+ def rugged_write_ref(ref_path, ref)
+ rugged.references.create(ref_path, ref, force: true)
+ rescue Rugged::ReferenceError => ex
+ Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
+ rescue Rugged::OSError => ex
+ raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
+
+ Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
+ end
+
+ def run_git(args, chdir: path, env: {}, nice: false, &block)
+ cmd = [Gitlab.config.git.bin_path, *args]
+ cmd.unshift("nice") if nice
+
+ object_directories = alternate_object_directories
+ if object_directories.any?
+ env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = object_directories.join(File::PATH_SEPARATOR)
+ end
+
+ circuit_breaker.perform do
+ popen(cmd, chdir, env, &block)
+ end
+ end
+
+ def run_git!(args, chdir: path, env: {}, nice: false, &block)
+ output, status = run_git(args, chdir: chdir, env: env, nice: nice, &block)
+
+ raise GitError, output unless status.zero?
+
+ output
+ end
+
def fresh_worktree?(path)
File.exist?(path) && !clean_stuck_worktree(path)
end
@@ -1543,7 +1638,7 @@ module Gitlab
offset_in_ruby = use_follow_flag && options[:offset].present?
limit += offset if offset_in_ruby
- cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} log]
+ cmd = %w[log]
cmd << "--max-count=#{limit}"
cmd << '--format=%H'
cmd << "--skip=#{offset}" unless offset_in_ruby
@@ -1559,7 +1654,7 @@ module Gitlab
cmd += Array(options[:path])
end
- raw_output = IO.popen(cmd) { |io| io.read }
+ raw_output, _status = run_git(cmd)
lines = offset_in_ruby ? raw_output.lines.drop(offset) : raw_output.lines
lines.map! { |c| Rugged::Commit.new(rugged, c.strip) }
@@ -1597,18 +1692,23 @@ module Gitlab
end
def alternate_object_directories
- relative_paths = Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
+ relative_paths = relative_object_directories
if relative_paths.any?
relative_paths.map { |d| File.join(path, d) }
else
- Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_DIRECTORIES_VARIABLES)
- .flatten
- .compact
- .flat_map { |d| d.split(File::PATH_SEPARATOR) }
+ absolute_object_directories.flat_map { |d| d.split(File::PATH_SEPARATOR) }
end
end
+ def relative_object_directories
+ Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
+ end
+
+ def absolute_object_directories
+ Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_DIRECTORIES_VARIABLES).flatten.compact
+ end
+
# Get the content of a blob for a given commit. If the blob is a commit
# (for submodules) then return the blob's OID.
def blob_content(commit, blob_name)
@@ -1752,13 +1852,13 @@ module Gitlab
def count_commits_by_shelling_out(options)
cmd = count_commits_shelling_command(options)
- raw_output = IO.popen(cmd) { |io| io.read }
+ raw_output, _status = run_git(cmd)
process_count_commits_raw_output(raw_output, options)
end
def count_commits_shelling_command(options)
- cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} rev-list]
+ cmd = %w[rev-list]
cmd << "--after=#{options[:after].iso8601}" if options[:after]
cmd << "--before=#{options[:before].iso8601}" if options[:before]
cmd << "--max-count=#{options[:max_count]}" if options[:max_count]
@@ -1803,20 +1903,17 @@ module Gitlab
return []
end
- cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-tree)
- cmd += %w(-r)
- cmd += %w(--full-tree)
- cmd += %w(--full-name)
- cmd += %W(-- #{actual_ref})
+ cmd = %W(ls-tree -r --full-tree --full-name -- #{actual_ref})
+ raw_output, _status = run_git(cmd)
- raw_output = IO.popen(cmd, &:read).split("\n").map do |f|
+ lines = raw_output.split("\n").map do |f|
stuff, path = f.split("\t")
_mode, type, _sha = stuff.split(" ")
path if type == "blob"
# Contain only blob type
end
- raw_output.compact
+ lines.compact
end
# Returns true if the given ref name exists
@@ -2100,6 +2197,39 @@ module Gitlab
remove_remote(remote_name)
end
+ def rugged_multi_action(
+ user, branch_name, message, actions, author_email, author_name,
+ start_branch_name, start_repository)
+
+ OperationService.new(user, self).with_branch(
+ branch_name,
+ start_branch_name: start_branch_name,
+ start_repository: start_repository
+ ) do |start_commit|
+ index = Gitlab::Git::Index.new(self)
+ parents = []
+
+ if start_commit
+ index.read_tree(start_commit.rugged_commit.tree)
+ parents = [start_commit.sha]
+ end
+
+ actions.each { |opts| index.apply(opts.delete(:action), opts) }
+
+ committer = user_to_committer(user)
+ author = Gitlab::Git.committer_hash(email: author_email, name: author_name) || committer
+ options = {
+ tree: index.write_tree,
+ message: message,
+ parents: parents,
+ author: author,
+ committer: committer
+ }
+
+ create_commit(options)
+ end
+ end
+
def fetch_remote(remote_name = 'origin', env: nil)
run_git(['fetch', remote_name], env: env).last.zero?
end
@@ -2107,6 +2237,26 @@ module Gitlab
def gitlab_projects_error
raise CommandError, @gitlab_projects.output
end
+
+ def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
+ ref ||= root_ref
+
+ args = %W(
+ log #{ref} --pretty=%H --skip #{offset}
+ --max-count #{limit} --grep=#{query} --regexp-ignore-case
+ )
+ args = args.concat(%W(-- #{path})) if path.present?
+
+ git_log_results = run_git(args).first.lines
+
+ git_log_results.map { |c| commit(c.chomp) }.compact
+ end
+
+ def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
+ gitaly_commit_client
+ .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
+ .map { |c| commit(c) }
+ end
end
end
end
diff --git a/lib/gitlab/git/wiki_page.rb b/lib/gitlab/git/wiki_page.rb
index a06bac4414f..669ae11a423 100644
--- a/lib/gitlab/git/wiki_page.rb
+++ b/lib/gitlab/git/wiki_page.rb
@@ -1,7 +1,7 @@
module Gitlab
module Git
class WikiPage
- attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :text_data, :historical
+ attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :text_data, :historical, :formatted_data
# This class is meant to be serializable so that it can be constructed
# by Gitaly and sent over the network to GitLab.
@@ -21,6 +21,7 @@ module Gitlab
@raw_data = gollum_page.raw_data
@name = gollum_page.name
@historical = gollum_page.historical?
+ @formatted_data = gollum_page.formatted_data if gollum_page.is_a?(Gollum::Page)
@version = version
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 4507ea923b4..6bd256f57c7 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -1,6 +1,8 @@
require 'base64'
require 'gitaly'
+require 'grpc/health/v1/health_pb'
+require 'grpc/health/v1/health_services_pb'
module Gitlab
module GitalyClient
@@ -69,14 +71,27 @@ module Gitlab
@stubs ||= {}
@stubs[storage] ||= {}
@stubs[storage][name] ||= begin
- klass = Gitaly.const_get(name.to_s.camelcase.to_sym).const_get(:Stub)
- addr = address(storage)
- addr = addr.sub(%r{^tcp://}, '') if URI(addr).scheme == 'tcp'
+ klass = stub_class(name)
+ addr = stub_address(storage)
klass.new(addr, :this_channel_is_insecure)
end
end
end
+ def self.stub_class(name)
+ if name == :health_check
+ Grpc::Health::V1::Health::Stub
+ else
+ Gitaly.const_get(name.to_s.camelcase.to_sym).const_get(:Stub)
+ end
+ end
+
+ def self.stub_address(storage)
+ addr = address(storage)
+ addr = addr.sub(%r{^tcp://}, '') if URI(addr).scheme == 'tcp'
+ addr
+ end
+
def self.clear_stubs!
MUTEX.synchronize do
@stubs = nil
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index a250eb75bd4..d70a1a7665e 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -32,6 +32,28 @@ module Gitlab
binary: Gitlab::Git::Blob.binary?(data)
)
end
+
+ def batch_lfs_pointers(blob_ids)
+ return [] if blob_ids.empty?
+
+ request = Gitaly::GetLFSPointersRequest.new(
+ repository: @gitaly_repo,
+ blob_ids: blob_ids
+ )
+
+ response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request)
+
+ response.flat_map do |message|
+ message.lfs_pointers.map do |lfs_pointer|
+ Gitlab::Git::Blob.new(
+ id: lfs_pointer.oid,
+ size: lfs_pointer.size,
+ data: lfs_pointer.data,
+ binary: Gitlab::Git::Blob.binary?(lfs_pointer.data)
+ )
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 71b212023d6..cadc7149301 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -38,19 +38,27 @@ module Gitlab
from_id = case from
when NilClass
EMPTY_TREE_ID
- when Rugged::Commit
- from.oid
else
- from
+ if from.respond_to?(:oid)
+ # This is meant to match a Rugged::Commit. This should be impossible in
+ # the future.
+ from.oid
+ else
+ from
+ end
end
to_id = case to
when NilClass
EMPTY_TREE_ID
- when Rugged::Commit
- to.oid
else
- to
+ if to.respond_to?(:oid)
+ # This is meant to match a Rugged::Commit. This should be impossible in
+ # the future.
+ to.oid
+ else
+ to
+ end
end
request_params = diff_between_commits_request_params(from_id, to_id, options)
@@ -125,11 +133,11 @@ module Gitlab
def commit_count(ref, options = {})
request = Gitaly::CountCommitsRequest.new(
repository: @gitaly_repo,
- revision: ref
+ revision: encode_binary(ref)
)
request.after = Google::Protobuf::Timestamp.new(seconds: options[:after].to_i) if options[:after].present?
request.before = Google::Protobuf::Timestamp.new(seconds: options[:before].to_i) if options[:before].present?
- request.path = options[:path] if options[:path].present?
+ request.path = encode_binary(options[:path]) if options[:path].present?
request.max_count = options[:max_count] if options[:max_count].present?
GitalyClient.call(@repository.storage, :commit_service, :count_commits, request, timeout: GitalyClient.medium_timeout).count
@@ -282,6 +290,23 @@ module Gitlab
end
end
+ def extract_signature(commit_id)
+ request = Gitaly::ExtractCommitSignatureRequest.new(repository: @gitaly_repo, commit_id: commit_id)
+ response = GitalyClient.call(@repository.storage, :commit_service, :extract_commit_signature, request)
+
+ signature = ''.b
+ signed_text = ''.b
+
+ response.each do |message|
+ signature << message.signature
+ signed_text << message.signed_text
+ end
+
+ return if signature.blank? && signed_text.blank?
+
+ [signature, signed_text]
+ end
+
private
def call_commit_diff(request_params, options = {})
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index 2565d537aff..e14734495a8 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -25,6 +25,11 @@ module Gitlab
def conflicts?
list_conflict_files.any?
+ rescue GRPC::FailedPrecondition
+ # The server raises this exception when it encounters ConflictSideMissing, which
+ # means a conflict exists but its `theirs` or `ours` data is nil due to a non-existent
+ # file in one of the trees.
+ true
end
def resolve_conflicts(target_repository, resolution, source_branch, target_branch)
diff --git a/lib/gitlab/gitaly_client/health_check_service.rb b/lib/gitlab/gitaly_client/health_check_service.rb
new file mode 100644
index 00000000000..6c1213f5e20
--- /dev/null
+++ b/lib/gitlab/gitaly_client/health_check_service.rb
@@ -0,0 +1,19 @@
+module Gitlab
+ module GitalyClient
+ class HealthCheckService
+ def initialize(storage)
+ @storage = storage
+ end
+
+ # Sends a gRPC health ping to the Gitaly server for the storage shard.
+ def check
+ request = Grpc::Health::V1::HealthCheckRequest.new
+ response = GitalyClient.call(@storage, :health_check, :check, request, timeout: GitalyClient.fast_timeout)
+
+ { success: response&.status == :SERVING }
+ rescue GRPC::BadStatus => e
+ { success: false, message: e.to_s }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 7319de69d13..c2b4155e6a5 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -3,6 +3,8 @@ module Gitlab
class OperationService
include Gitlab::EncodingHelper
+ MAX_MSG_SIZE = 128.kilobytes.freeze
+
def initialize(repository)
@gitaly_repo = repository.gitaly_repository
@repository = repository
@@ -175,6 +177,49 @@ module Gitlab
end
end
+ def user_commit_files(
+ user, branch_name, commit_message, actions, author_email, author_name,
+ start_branch_name, start_repository)
+
+ req_enum = Enumerator.new do |y|
+ header = user_commit_files_request_header(user, branch_name,
+ commit_message, actions, author_email, author_name,
+ start_branch_name, start_repository)
+
+ y.yield Gitaly::UserCommitFilesRequest.new(header: header)
+
+ actions.each do |action|
+ action_header = user_commit_files_action_header(action)
+ y.yield Gitaly::UserCommitFilesRequest.new(
+ action: Gitaly::UserCommitFilesAction.new(header: action_header)
+ )
+
+ reader = binary_stringio(action[:content])
+
+ until reader.eof?
+ chunk = reader.read(MAX_MSG_SIZE)
+
+ y.yield Gitaly::UserCommitFilesRequest.new(
+ action: Gitaly::UserCommitFilesAction.new(content: chunk)
+ )
+ end
+ end
+ end
+
+ response = GitalyClient.call(@repository.storage, :operation_service,
+ :user_commit_files, req_enum, remote_storage: start_repository.storage)
+
+ if (pre_receive_error = response.pre_receive_error.presence)
+ raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error
+ end
+
+ if (index_error = response.index_error.presence)
+ raise Gitlab::Git::Index::IndexError, index_error
+ end
+
+ Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
+ end
+
private
def call_cherry_pick_or_revert(rpc, user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
@@ -212,6 +257,33 @@ module Gitlab
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
end
+
+ def user_commit_files_request_header(
+ user, branch_name, commit_message, actions, author_email, author_name,
+ start_branch_name, start_repository)
+
+ Gitaly::UserCommitFilesRequestHeader.new(
+ repository: @gitaly_repo,
+ user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
+ branch_name: encode_binary(branch_name),
+ commit_message: encode_binary(commit_message),
+ commit_author_name: encode_binary(author_name),
+ commit_author_email: encode_binary(author_email),
+ start_branch_name: encode_binary(start_branch_name),
+ start_repository: start_repository.gitaly_repository
+ )
+ end
+
+ def user_commit_files_action_header(action)
+ Gitaly::UserCommitFilesActionHeader.new(
+ action: action[:action].upcase.to_sym,
+ file_path: encode_binary(action[:file_path]),
+ previous_path: encode_binary(action[:previous_path]),
+ base64_content: action[:encoding] == 'base64'
+ )
+ rescue RangeError
+ raise ArgumentError, "Unknown action '#{action[:action]}'"
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/remote_service.rb b/lib/gitlab/gitaly_client/remote_service.rb
index e58f641d69a..58c356edfd1 100644
--- a/lib/gitlab/gitaly_client/remote_service.rb
+++ b/lib/gitlab/gitaly_client/remote_service.rb
@@ -1,6 +1,8 @@
module Gitlab
module GitalyClient
class RemoteService
+ MAX_MSG_SIZE = 128.kilobytes.freeze
+
def initialize(repository)
@repository = repository
@gitaly_repo = repository.gitaly_repository
@@ -38,6 +40,31 @@ module Gitlab
response.result
end
+
+ def update_remote_mirror(ref_name, only_branches_matching)
+ req_enum = Enumerator.new do |y|
+ y.yield Gitaly::UpdateRemoteMirrorRequest.new(
+ repository: @gitaly_repo,
+ ref_name: ref_name
+ )
+
+ current_size = 0
+
+ slices = only_branches_matching.slice_before do |branch_name|
+ current_size += branch_name.bytesize
+
+ next false if current_size < MAX_MSG_SIZE
+
+ current_size = 0
+ end
+
+ slices.each do |slice|
+ y.yield Gitaly::UpdateRemoteMirrorRequest.new(only_branches_matching: slice)
+ end
+ end
+
+ GitalyClient.call(@storage, :remote_service, :update_remote_mirror, req_enum)
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 12016aee2a6..b0dbaf11598 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -3,6 +3,8 @@ module Gitlab
class RepositoryService
include Gitlab::EncodingHelper
+ MAX_MSG_SIZE = 128.kilobytes.freeze
+
def initialize(repository)
@repository = repository
@gitaly_repo = repository.gitaly_repository
@@ -161,6 +163,46 @@ module Gitlab
return response.error.b, 1
end
end
+
+ def create_bundle(save_path)
+ request = Gitaly::CreateBundleRequest.new(repository: @gitaly_repo)
+ response = GitalyClient.call(
+ @storage,
+ :repository_service,
+ :create_bundle,
+ request,
+ timeout: GitalyClient.default_timeout
+ )
+
+ File.open(save_path, 'wb') do |f|
+ response.each do |message|
+ f.write(message.data)
+ end
+ end
+ end
+
+ def create_from_bundle(bundle_path)
+ request = Gitaly::CreateRepositoryFromBundleRequest.new(repository: @gitaly_repo)
+ enum = Enumerator.new do |y|
+ File.open(bundle_path, 'rb') do |f|
+ while data = f.read(MAX_MSG_SIZE)
+ request.data = data
+
+ y.yield request
+
+ request = Gitaly::CreateRepositoryFromBundleRequest.new
+ end
+ end
+ end
+
+ GitalyClient.call(
+ @storage,
+ :repository_service,
+ :create_repository_from_bundle,
+ enum,
+ timeout: GitalyClient.default_timeout
+ )
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb
index 5437e32e9f1..e70361c163b 100644
--- a/lib/gitlab/github_import/importer/pull_requests_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb
@@ -57,10 +57,7 @@ module Gitlab
end
def commit_exists?(sha)
- project.repository.lookup(sha)
- true
- rescue Rugged::Error
- false
+ project.repository.commit(sha).present?
end
def collection_method
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 0f4ba6f83fc..672b5579dfd 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -4,12 +4,8 @@ module Gitlab
def initialize(commit)
@commit = commit
- @signature_text, @signed_text =
- begin
- Rugged::Commit.extract_signature(@commit.project.repository.rugged, @commit.sha)
- rescue Rugged::OdbError
- nil
- end
+ repo = commit.project.repository.raw_repository
+ @signature_text, @signed_text = Gitlab::Git::Commit.extract_signature(repo, commit.sha)
end
def has_signature?
diff --git a/lib/gitlab/health_checks/gitaly_check.rb b/lib/gitlab/health_checks/gitaly_check.rb
new file mode 100644
index 00000000000..11416c002e3
--- /dev/null
+++ b/lib/gitlab/health_checks/gitaly_check.rb
@@ -0,0 +1,53 @@
+module Gitlab
+ module HealthChecks
+ class GitalyCheck
+ extend BaseAbstractCheck
+
+ METRIC_PREFIX = 'gitaly_health_check'.freeze
+
+ class << self
+ def readiness
+ repository_storages.map do |storage_name|
+ check(storage_name)
+ end
+ end
+
+ def metrics
+ repository_storages.flat_map do |storage_name|
+ result, elapsed = with_timing { check(storage_name) }
+ labels = { shard: storage_name }
+
+ [
+ metric("#{metric_prefix}_success", successful?(result) ? 1 : 0, **labels),
+ metric("#{metric_prefix}_latency_seconds", elapsed, **labels)
+ ].flatten
+ end
+ end
+
+ def check(storage_name)
+ serv = Gitlab::GitalyClient::HealthCheckService.new(storage_name)
+ result = serv.check
+ HealthChecks::Result.new(result[:success], result[:message], shard: storage_name)
+ end
+
+ private
+
+ def metric_prefix
+ METRIC_PREFIX
+ end
+
+ def successful?(result)
+ result[:success]
+ end
+
+ def repository_storages
+ storages.keys
+ end
+
+ def storages
+ Gitlab.config.repositories.storages
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index dd5d35feab9..2f163db936b 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -11,15 +11,6 @@ module Gitlab
untar_with_options(archive: archive, dir: dir, options: 'zxf')
end
- def git_bundle(repo_path:, bundle_path:)
- execute(%W(#{git_bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all))
- end
-
- def git_clone_bundle(repo_path:, bundle_path:)
- execute(%W(#{git_bin_path} clone --bare -- #{bundle_path} #{repo_path}))
- Gitlab::Git::Repository.create_hooks(repo_path, File.expand_path(Gitlab.config.gitlab_shell.hooks_path))
- end
-
def mkdir_p(path)
FileUtils.mkdir_p(path, mode: DEFAULT_MODE)
FileUtils.chmod(DEFAULT_MODE, path)
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 989342389bc..5c971564a73 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -17,12 +17,16 @@ module Gitlab
def import
mkdir_p(@shared.export_path)
+ remove_symlinks!
+
wait_for_archived_file do
decompress_archive
end
rescue => e
@shared.error(e)
false
+ ensure
+ remove_symlinks!
end
private
@@ -43,7 +47,7 @@ module Gitlab
raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result
- remove_symlinks!
+ result
end
def remove_symlinks!
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index d0e5cfcfd3e..5a9bbceac67 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -13,7 +13,7 @@ module Gitlab
def restore
return true unless File.exist?(@path_to_bundle)
- git_clone_bundle(repo_path: @project.repository.path_to_repo, bundle_path: @path_to_bundle)
+ @project.repository.create_from_bundle(@path_to_bundle)
rescue => e
@shared.error(e)
false
diff --git a/lib/gitlab/import_export/repo_saver.rb b/lib/gitlab/import_export/repo_saver.rb
index a7028a32570..695462c7dd2 100644
--- a/lib/gitlab/import_export/repo_saver.rb
+++ b/lib/gitlab/import_export/repo_saver.rb
@@ -21,7 +21,7 @@ module Gitlab
def bundle_to_disk
mkdir_p(@shared.export_path)
- git_bundle(repo_path: path_to_repo, bundle_path: @full_path)
+ @project.repository.bundle_to_disk(@full_path)
rescue => e
@shared.error(e)
false
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index 6130c124dd1..2daeba90a51 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -37,7 +37,7 @@ module Gitlab
end
def archive_file
- @archive_file ||= File.join(@shared.export_path, '..', Gitlab::ImportExport.export_filename(project: @project))
+ @archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
end
end
end
diff --git a/lib/gitlab/import_export/shared.rb b/lib/gitlab/import_export/shared.rb
index 9fd0b709ef2..b34cafc6876 100644
--- a/lib/gitlab/import_export/shared.rb
+++ b/lib/gitlab/import_export/shared.rb
@@ -9,18 +9,35 @@ module Gitlab
end
def export_path
- @export_path ||= Gitlab::ImportExport.export_path(relative_path: opts[:relative_path])
+ @export_path ||= Gitlab::ImportExport.export_path(relative_path: relative_path)
+ end
+
+ def archive_path
+ @archive_path ||= Gitlab::ImportExport.export_path(relative_path: relative_archive_path)
end
def error(error)
error_out(error.message, caller[0].dup)
@errors << error.message
+
# Debug:
- Rails.logger.error(error.backtrace.join("\n"))
+ if error.backtrace
+ Rails.logger.error("Import/Export backtrace: #{error.backtrace.join("\n")}")
+ else
+ Rails.logger.error("No backtrace found")
+ end
end
private
+ def relative_path
+ File.join(opts[:relative_path], SecureRandom.hex)
+ end
+
+ def relative_archive_path
+ File.join(opts[:relative_path], '..')
+ end
+
def error_out(message, caller)
Rails.logger.error("Import/Export error raised on #{caller}: #{message}")
end
diff --git a/lib/gitlab/import_export/wiki_repo_saver.rb b/lib/gitlab/import_export/wiki_repo_saver.rb
index 1e6722a7bba..5fa2e101e29 100644
--- a/lib/gitlab/import_export/wiki_repo_saver.rb
+++ b/lib/gitlab/import_export/wiki_repo_saver.rb
@@ -10,7 +10,7 @@ module Gitlab
def bundle_to_disk(full_path)
mkdir_p(@shared.export_path)
- git_bundle(repo_path: path_to_repo, bundle_path: full_path)
+ @wiki.repository.bundle_to_disk(full_path)
rescue => e
@shared.error(e)
false
diff --git a/lib/gitlab/o_auth.rb b/lib/gitlab/o_auth.rb
new file mode 100644
index 00000000000..5ad8d83bd6e
--- /dev/null
+++ b/lib/gitlab/o_auth.rb
@@ -0,0 +1,6 @@
+module Gitlab
+ module OAuth
+ SignupDisabledError = Class.new(StandardError)
+ SigninDisabledForProviderError = Class.new(StandardError)
+ end
+end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index d33f33d192f..fff9360ea27 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -5,8 +5,6 @@
#
module Gitlab
module OAuth
- SignupDisabledError = Class.new(StandardError)
-
class User
attr_accessor :auth_hash, :gl_user
@@ -29,7 +27,8 @@ module Gitlab
end
def save(provider = 'OAuth')
- unauthorized_to_create unless gl_user
+ raise SigninDisabledForProviderError if oauth_provider_disabled?
+ raise SignupDisabledError unless gl_user
block_after_save = needs_blocking?
@@ -226,8 +225,10 @@ module Gitlab
Gitlab::AppLogger
end
- def unauthorized_to_create
- raise SignupDisabledError
+ def oauth_provider_disabled?
+ Gitlab::CurrentSettings.current_application_settings
+ .disabled_oauth_sign_in_sources
+ .include?(auth_hash.provider)
end
end
end
diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb
index e73245b82c1..e29e168fc5a 100644
--- a/lib/gitlab/performance_bar.rb
+++ b/lib/gitlab/performance_bar.rb
@@ -6,6 +6,7 @@ module Gitlab
EXPIRY_TIME = 5.minutes
def self.enabled?(user = nil)
+ return true if Rails.env.development?
return false unless user && allowed_group_id
allowed_user_ids.include?(user.id)
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
new file mode 100644
index 00000000000..95d94b3cc68
--- /dev/null
+++ b/lib/gitlab/profiler.rb
@@ -0,0 +1,142 @@
+# coding: utf-8
+module Gitlab
+ module Profiler
+ FILTERED_STRING = '[FILTERED]'.freeze
+
+ IGNORE_BACKTRACES = %w[
+ lib/gitlab/i18n.rb
+ lib/gitlab/request_context.rb
+ config/initializers
+ lib/gitlab/database/load_balancing/
+ lib/gitlab/etag_caching/
+ lib/gitlab/metrics/
+ lib/gitlab/middleware/
+ lib/gitlab/performance_bar/
+ lib/gitlab/request_profiler/
+ lib/gitlab/profiler.rb
+ ].freeze
+
+ # Takes a URL to profile (can be a fully-qualified URL, or an absolute path)
+ # and returns the ruby-prof profile result. Formatting that result is the
+ # caller's responsibility. Requests are GET requests unless post_data is
+ # passed.
+ #
+ # Optional arguments:
+ # - logger: will be used for SQL logging, including a summary at the end of
+ # the log file of the total time spent per model class.
+ #
+ # - post_data: a string of raw POST data to use. Changes the HTTP verb to
+ # POST.
+ #
+ # - user: a user to authenticate as. Only works if the user has a valid
+ # personal access token.
+ #
+ # - private_token: instead of providing a user instance, the token can be
+ # given as a string. Takes precedence over the user option.
+ def self.profile(url, logger: nil, post_data: nil, user: nil, private_token: nil)
+ app = ActionDispatch::Integration::Session.new(Rails.application)
+ verb = :get
+ headers = {}
+
+ if post_data
+ verb = :post
+ headers['Content-Type'] = 'application/json'
+ end
+
+ if user
+ private_token ||= user.personal_access_tokens.active.pluck(:token).first
+ end
+
+ headers['Private-Token'] = private_token if private_token
+ logger = create_custom_logger(logger, private_token: private_token)
+
+ RequestStore.begin!
+
+ # Make an initial call for an asset path in development mode to avoid
+ # sprockets dominating the profiler output.
+ ActionController::Base.helpers.asset_path('katex.css') if Rails.env.development?
+
+ # Rails loads internationalization files lazily the first time a
+ # translation is needed. Running this prevents this overhead from showing
+ # up in profiles.
+ ::I18n.t('.')[:test_string]
+
+ # Remove API route mounting from the profile.
+ app.get('/api/v4/users')
+
+ result = with_custom_logger(logger) do
+ RubyProf.profile { app.public_send(verb, url, post_data, headers) } # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ RequestStore.end!
+
+ log_load_times_by_model(logger)
+
+ result
+ end
+
+ def self.create_custom_logger(logger, private_token: nil)
+ return unless logger
+
+ logger.dup.tap do |new_logger|
+ new_logger.instance_variable_set(:@private_token, private_token)
+
+ class << new_logger
+ attr_reader :load_times_by_model, :private_token
+
+ def debug(message, *)
+ message.gsub!(private_token, FILTERED_STRING) if private_token
+
+ _, type, time = *message.match(/(\w+) Load \(([0-9.]+)ms\)/)
+
+ if type && time
+ @load_times_by_model ||= {}
+ @load_times_by_model[type] ||= 0
+ @load_times_by_model[type] += time.to_f
+ end
+
+ super
+
+ backtrace = Rails.backtrace_cleaner.clean(caller)
+
+ backtrace.each do |caller_line|
+ next if caller_line.match(Regexp.union(IGNORE_BACKTRACES))
+
+ stripped_caller_line = caller_line.sub("#{Rails.root}/", '')
+
+ super(" ↳ #{stripped_caller_line}")
+ end
+ end
+ end
+ end
+ end
+
+ def self.with_custom_logger(logger)
+ original_colorize_logging = ActiveSupport::LogSubscriber.colorize_logging
+ original_activerecord_logger = ActiveRecord::Base.logger
+ original_actioncontroller_logger = ActionController::Base.logger
+
+ if logger
+ ActiveSupport::LogSubscriber.colorize_logging = false
+ ActiveRecord::Base.logger = logger
+ ActionController::Base.logger = logger
+ end
+
+ result = yield
+
+ ActiveSupport::LogSubscriber.colorize_logging = original_colorize_logging
+ ActiveRecord::Base.logger = original_activerecord_logger
+ ActionController::Base.logger = original_actioncontroller_logger
+
+ result
+ end
+
+ def self.log_load_times_by_model(logger)
+ return unless logger.respond_to?(:load_times_by_model)
+
+ logger.load_times_by_model.to_a.sort_by(&:last).reverse.each do |(model, time)|
+ logger.info("#{model} total: #{time.round(2)}ms")
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 7771b15069b..4823f703ba4 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -20,7 +20,7 @@ module Gitlab
when 'commits'
Kaminari.paginate_array(commits).page(page).per(per_page)
else
- super
+ super(scope, page, false)
end
end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 0002c7da8f1..7ab85e1c35c 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -67,7 +67,7 @@ module Gitlab
end
def build_trace_section_regex
- @build_trace_section_regexp ||= /section_((?:start)|(?:end)):(\d+):([^\r]+)\r\033\[0K/.freeze
+ @build_trace_section_regexp ||= /section_((?:start)|(?:end)):(\d+):([a-zA-Z0-9_.-]+)\r\033\[0K/.freeze
end
end
end
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 70b639501fd..7362514167f 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -40,19 +40,21 @@ module Gitlab
@default_project_filter = default_project_filter
end
- def objects(scope, page = nil)
- case scope
- when 'projects'
- projects.page(page).per(per_page)
- when 'issues'
- issues.page(page).per(per_page)
- when 'merge_requests'
- merge_requests.page(page).per(per_page)
- when 'milestones'
- milestones.page(page).per(per_page)
- else
- Kaminari.paginate_array([]).page(page).per(per_page)
- end
+ def objects(scope, page = nil, without_count = true)
+ collection = case scope
+ when 'projects'
+ projects.page(page).per(per_page)
+ when 'issues'
+ issues.page(page).per(per_page)
+ when 'merge_requests'
+ merge_requests.page(page).per(per_page)
+ when 'milestones'
+ milestones.page(page).per(per_page)
+ else
+ Kaminari.paginate_array([]).page(page).per(per_page)
+ end
+
+ without_count ? collection.without_count : collection
end
def projects_count
@@ -71,18 +73,46 @@ module Gitlab
@milestones_count ||= milestones.count
end
+ def limited_projects_count
+ @limited_projects_count ||= projects.limit(count_limit).count
+ end
+
+ def limited_issues_count
+ return @limited_issues_count if @limited_issues_count
+
+ # By default getting limited count (e.g. 1000+) is fast on issuable
+ # collections except for issues, where filtering both not confidential
+ # and confidential issues user has access to, is too complex.
+ # It's faster to try to fetch all public issues first, then only
+ # if necessary try to fetch all issues.
+ sum = issues(public_only: true).limit(count_limit).count
+ @limited_issues_count = sum < count_limit ? issues.limit(count_limit).count : sum
+ end
+
+ def limited_merge_requests_count
+ @limited_merge_requests_count ||= merge_requests.limit(count_limit).count
+ end
+
+ def limited_milestones_count
+ @limited_milestones_count ||= milestones.limit(count_limit).count
+ end
+
def single_commit_result?
false
end
+ def count_limit
+ 1001
+ end
+
private
def projects
limit_projects.search(query)
end
- def issues
- issues = IssuesFinder.new(current_user).execute
+ def issues(finder_params = {})
+ issues = IssuesFinder.new(current_user, finder_params).execute
unless default_project_filter
issues = issues.where(project_id: project_ids_relation)
end
@@ -94,13 +124,13 @@ module Gitlab
issues.full_search(query)
end
- issues.order('updated_at DESC')
+ issues.reorder('updated_at DESC')
end
def milestones
milestones = Milestone.where(project_id: project_ids_relation)
milestones = milestones.search(query)
- milestones.order('updated_at DESC')
+ milestones.reorder('updated_at DESC')
end
def merge_requests
@@ -116,7 +146,7 @@ module Gitlab
merge_requests.full_search(query)
end
- merge_requests.order('updated_at DESC')
+ merge_requests.reorder('updated_at DESC')
end
def default_scope
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 94a481a0f2e..98f005cb61b 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -5,9 +5,15 @@ module DeliverNever
end
end
+module MuteNotifications
+ def new_note(note)
+ end
+end
+
module Gitlab
class Seeder
def self.quiet
+ mute_notifications
mute_mailer
SeedFu.quiet = true
@@ -18,6 +24,10 @@ module Gitlab
puts "\nOK".color(:green)
end
+ def self.mute_notifications
+ NotificationService.prepend(MuteNotifications)
+ end
+
def self.mute_mailer
ActionMailer::MessageDelivery.prepend(DeliverNever)
end
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index b85f70e450e..4f86b3e8f73 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -16,7 +16,7 @@ module Gitlab
when 'snippet_blobs'
snippet_blobs.page(page).per(per_page)
else
- super
+ super(scope, nil, false)
end
end
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index b3baaf036d8..fa22f0e37b2 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -27,6 +27,10 @@ module Gitlab
.gsub(/(\A-+|-+\z)/, '')
end
+ def remove_line_breaks(str)
+ str.gsub(/\r?\n/, '')
+ end
+
def to_boolean(value)
return value if [true, false].include?(value)
return true if value =~ /^(true|t|yes|y|1|on)$/i
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index 0de183858aa..633da44b22d 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -34,7 +34,10 @@ module Gitlab
feature_enabled = case action.to_s
when 'git_receive_pack'
- Gitlab::GitalyClient.feature_enabled?(:post_receive_pack)
+ Gitlab::GitalyClient.feature_enabled?(
+ :post_receive_pack,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT
+ )
when 'git_upload_pack'
true
when 'info_refs'
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 54f51d9d633..0e27a28ea6e 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -17,6 +17,8 @@
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab-workhorse {
+ # Gitlab socket file,
+ # for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -110,6 +112,8 @@ server {
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
+ # Location to the Gitlab's public directory,
+ # for Omnibus this would be: /opt/gitlab/embedded/service/gitlab-rails/public.
root /home/git/gitlab/public;
internal;
}
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index ed8131ef24f..8218d68f9ba 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -21,6 +21,8 @@
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab-workhorse {
+ # Gitlab socket file,
+ # for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -160,6 +162,8 @@ server {
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
+ # Location to the Gitlab's public directory,
+ # for Omnibus this would be: /opt/gitlab/embedded/service/gitlab-rails/public
root /home/git/gitlab/public;
internal;
}
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 31cd6bfe6e1..a584eb97cf5 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -427,7 +427,7 @@ namespace :gitlab do
namespace :user do
desc "GitLab | Check the integrity of a specific user's repositories"
task :check_repos, [:username] => :environment do |t, args|
- username = args[:username] || prompt("Check repository integrity for fsername? ".color(:blue))
+ username = args[:username] || prompt("Check repository integrity for username? ".color(:blue))
user = User.find_by(username: username)
if user
repo_dirs = user.authorized_projects.map do |p|
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index 04d56509ac6..ab601b0d66b 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -1,3 +1,5 @@
+# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/954
+#
namespace :gitlab do
namespace :cleanup do
HASHED_REPOSITORY_NAME = '@hashed'.freeze
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index a2e68c0471b..aece8893974 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -21,7 +21,11 @@ namespace :gitlab do
_, status = Gitlab::Popen.popen(%w[which gmake])
command << (status.zero? ? 'gmake' : 'make')
- command << 'BUNDLE_FLAGS=--no-deployment' if Rails.env.test?
+ if Rails.env.test?
+ command.push(
+ 'BUNDLE_FLAGS=--no-deployment',
+ "BUNDLE_PATH=#{Bundler.bundle_path}")
+ end
Gitlab::SetupHelper.create_gitaly_configuration(args.dir)
Dir.chdir(args.dir) do
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index c9e3eed82f2..c996537cfbe 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -7,6 +7,7 @@ require Rails.root.join('db/migrate/20170317203554_index_routes_path_for_like')
require Rails.root.join('db/migrate/20170724214302_add_lower_path_index_to_redirect_routes')
require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like')
require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb')
+require Rails.root.join('db/migrate/20180113220114_rework_redirect_routes_indexes.rb')
desc 'GitLab | Sets up PostgreSQL'
task setup_postgresql: :environment do
@@ -17,4 +18,5 @@ task setup_postgresql: :environment do
AddLowerPathIndexToRedirectRoutes.new.up
IndexRedirectRoutesPathForLike.new.up
AddIndexOnNamespacesLowerName.new.up
+ ReworkRedirectRoutesIndexes.new.up
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3ebc7859232..74d76caf47d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -186,13 +186,13 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
diff --git a/package.json b/package.json
index 35c145aaebe..c68cf648932 100644
--- a/package.json
+++ b/package.json
@@ -61,9 +61,10 @@
"pikaday": "^1.6.1",
"prismjs": "^1.6.0",
"raphael": "^2.2.7",
- "raven-js": "^3.14.0",
+ "raven-js": "^3.22.1",
"raw-loader": "^0.5.1",
"react-dev-utils": "^0.5.2",
+ "sanitize-html": "^1.16.1",
"select2": "3.5.2-browserify",
"sql.js": "^0.4.0",
"svg4everybody": "2.1.9",
@@ -74,11 +75,11 @@
"underscore": "^1.8.3",
"url-loader": "^0.5.8",
"visibilityjs": "^1.2.4",
- "vue": "^2.5.8",
- "vue-loader": "^13.5.0",
- "vue-resource": "^1.3.4",
+ "vue": "^2.5.13",
+ "vue-loader": "^13.7.0",
+ "vue-resource": "^1.3.5",
"vue-router": "^3.0.1",
- "vue-template-compiler": "^2.5.8",
+ "vue-template-compiler": "^2.5.13",
"vuex": "^3.0.1",
"webpack": "^3.5.5",
"webpack-bundle-analyzer": "^2.8.2",
@@ -86,7 +87,7 @@
"worker-loader": "^1.1.0"
},
"devDependencies": {
- "@gitlab-org/gitlab-svgs": "^1.5.0",
+ "@gitlab-org/gitlab-svgs": "^1.7.0",
"axios-mock-adapter": "^1.10.0",
"babel-plugin-istanbul": "^4.1.5",
"eslint": "^3.18.0",
@@ -99,15 +100,15 @@
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-vue": "^4.0.1",
"istanbul": "^0.4.5",
- "jasmine-core": "^2.6.3",
+ "jasmine-core": "^2.9.0",
"jasmine-jquery": "^2.1.1",
- "karma": "^1.7.0",
- "karma-chrome-launcher": "^2.1.1",
- "karma-coverage-istanbul-reporter": "^0.2.0",
- "karma-jasmine": "^1.1.0",
- "karma-mocha-reporter": "^2.2.2",
+ "karma": "^2.0.0",
+ "karma-chrome-launcher": "^2.2.0",
+ "karma-coverage-istanbul-reporter": "^1.3.3",
+ "karma-jasmine": "^1.1.1",
+ "karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
- "karma-webpack": "^2.0.4",
+ "karma-webpack": "2.0.7",
"nodemon": "^1.11.0",
"prettier": "1.9.2",
"webpack-dev-server": "^2.6.1"
diff --git a/qa/Gemfile b/qa/Gemfile
index 4c866a3f893..c3e61568f3d 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -6,3 +6,5 @@ gem 'capybara-screenshot', '~> 1.0.18'
gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.8.0'
+gem 'net-ssh', require: false
+gem 'airborne', '~> 0.2.13'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 88d5fe834a0..51d2e4d7a10 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -1,8 +1,19 @@
GEM
remote: https://rubygems.org/
specs:
+ activesupport (5.1.4)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (~> 0.7)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
+ airborne (0.2.13)
+ activesupport
+ rack
+ rack-test (~> 0.6, >= 0.6.2)
+ rest-client (>= 1.7.3, < 3.0)
+ rspec (~> 3.1)
byebug (9.1.0)
capybara (2.16.1)
addressable
@@ -17,13 +28,26 @@ GEM
childprocess (0.8.0)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.2)
+ concurrent-ruby (1.0.5)
diff-lcs (1.3)
+ domain_name (0.5.20170404)
+ unf (>= 0.0.5, < 1.0.0)
ffi (1.9.18)
+ http-cookie (1.0.3)
+ domain_name (~> 0.5)
+ i18n (0.9.1)
+ concurrent-ruby (~> 1.0)
launchy (2.4.3)
addressable (~> 2.3)
method_source (0.9.0)
+ mime-types (3.1)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2016.0521)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
+ minitest (5.11.1)
+ net-ssh (4.1.0)
+ netrc (0.11.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
pry (0.11.3)
@@ -37,11 +61,15 @@ GEM
rack-test (0.8.2)
rack (>= 1.0, < 3)
rake (12.3.0)
+ rest-client (2.0.2)
+ http-cookie (>= 1.0.2, < 2.0)
+ mime-types (>= 1.16, < 4.0)
+ netrc (~> 0.8)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
- rspec-core (3.7.0)
+ rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
@@ -54,6 +82,12 @@ GEM
selenium-webdriver (3.8.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.4)
+ thread_safe (~> 0.1)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.4)
xpath (2.1.0)
nokogiri (~> 1.3)
@@ -61,12 +95,14 @@ PLATFORMS
ruby
DEPENDENCIES
+ airborne (~> 0.2.13)
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
+ net-ssh
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
selenium-webdriver (~> 3.8.0)
BUNDLED WITH
- 1.16.0
+ 1.16.1
diff --git a/qa/qa.rb b/qa/qa.rb
index 4803432aeee..180ee778fd4 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -11,6 +11,9 @@ module QA
autoload :Scenario, 'qa/runtime/scenario'
autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env'
+ autoload :RSAKey, 'qa/runtime/rsa_key'
+ autoload :Address, 'qa/runtime/address'
+ autoload :API, 'qa/runtime/api'
end
##
@@ -25,7 +28,11 @@ module QA
autoload :Sandbox, 'qa/factory/resource/sandbox'
autoload :Group, 'qa/factory/resource/group'
autoload :Project, 'qa/factory/resource/project'
+ autoload :MergeRequest, 'qa/factory/resource/merge_request'
autoload :DeployKey, 'qa/factory/resource/deploy_key'
+ autoload :SecretVariable, 'qa/factory/resource/secret_variable'
+ autoload :Runner, 'qa/factory/resource/runner'
+ autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
end
module Repository
@@ -46,7 +53,7 @@ module QA
#
autoload :Bootable, 'qa/scenario/bootable'
autoload :Actable, 'qa/scenario/actable'
- autoload :Entrypoint, 'qa/scenario/entrypoint'
+ autoload :Taggable, 'qa/scenario/taggable'
autoload :Template, 'qa/scenario/template'
##
@@ -85,6 +92,7 @@ module QA
autoload :Main, 'qa/page/menu/main'
autoload :Side, 'qa/page/menu/side'
autoload :Admin, 'qa/page/menu/admin'
+ autoload :Profile, 'qa/page/menu/profile'
end
module Dashboard
@@ -100,14 +108,33 @@ module QA
module Project
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
+ autoload :Activity, 'qa/page/project/activity'
+
+ module Pipeline
+ autoload :Index, 'qa/page/project/pipeline/index'
+ autoload :Show, 'qa/page/project/pipeline/show'
+ end
module Settings
autoload :Common, 'qa/page/project/settings/common'
+ autoload :Advanced, 'qa/page/project/settings/advanced'
+ autoload :Main, 'qa/page/project/settings/main'
autoload :Repository, 'qa/page/project/settings/repository'
+ autoload :CICD, 'qa/page/project/settings/ci_cd'
autoload :DeployKeys, 'qa/page/project/settings/deploy_keys'
+ autoload :SecretVariables, 'qa/page/project/settings/secret_variables'
+ autoload :Runners, 'qa/page/project/settings/runners'
end
end
+ module Profile
+ autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
+ end
+
+ module MergeRequest
+ autoload :New, 'qa/page/merge_request/new'
+ end
+
module Admin
autoload :Settings, 'qa/page/admin/settings'
end
@@ -126,10 +153,13 @@ module QA
end
##
- # Classes describing shell interaction with GitLab
+ # Classes describing services being part of GitLab and how we can interact
+ # with these services, like through the shell.
#
- module Shell
- autoload :Omnibus, 'qa/shell/omnibus'
+ module Service
+ autoload :Shellout, 'qa/service/shellout'
+ autoload :Omnibus, 'qa/service/omnibus'
+ autoload :Runner, 'qa/service/runner'
end
##
diff --git a/qa/qa/factory/dependency.rb b/qa/qa/factory/dependency.rb
index d0e85a68237..fc5dc82ce29 100644
--- a/qa/qa/factory/dependency.rb
+++ b/qa/qa/factory/dependency.rb
@@ -16,20 +16,21 @@ module QA
def build!
return if overridden?
- Builder.new(@signature).fabricate!.tap do |product|
+ Builder.new(@signature, @factory).fabricate!.tap do |product|
@factory.public_send("#{@name}=", product)
end
end
class Builder
- def initialize(signature)
+ def initialize(signature, caller_factory)
@factory = signature.factory
@block = signature.block
+ @caller_factory = caller_factory
end
def fabricate!
@factory.fabricate! do |factory|
- @block&.call(factory)
+ @block&.call(factory, @caller_factory)
end
end
end
diff --git a/qa/qa/factory/resource/deploy_key.rb b/qa/qa/factory/resource/deploy_key.rb
index 7c58e70bcc4..ff0b4a46b77 100644
--- a/qa/qa/factory/resource/deploy_key.rb
+++ b/qa/qa/factory/resource/deploy_key.rb
@@ -4,6 +4,18 @@ module QA
class DeployKey < Factory::Base
attr_accessor :title, :key
+ product :title do
+ Page::Project::Settings::Repository.act do
+ expand_deploy_keys(&:key_title)
+ end
+ end
+
+ product :fingerprint do
+ Page::Project::Settings::Repository.act do
+ expand_deploy_keys(&:key_fingerprint)
+ end
+ end
+
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-to-deploy'
project.description = 'project for adding deploy key test'
@@ -13,7 +25,7 @@ module QA
project.visit!
Page::Menu::Side.act do
- click_repository_setting
+ click_repository_settings
end
Page::Project::Settings::Repository.perform do |setting|
diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb
new file mode 100644
index 00000000000..ce04e904aaf
--- /dev/null
+++ b/qa/qa/factory/resource/merge_request.rb
@@ -0,0 +1,49 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class MergeRequest < Factory::Base
+ attr_accessor :title,
+ :description,
+ :source_branch,
+ :target_branch
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-merge-request'
+ end
+
+ dependency Factory::Repository::Push, as: :target do |push, factory|
+ push.project = factory.project
+ push.branch_name = "master:#{factory.target_branch}"
+ end
+
+ dependency Factory::Repository::Push, as: :source do |push, factory|
+ push.project = factory.project
+ push.branch_name = "#{factory.target_branch}:#{factory.source_branch}"
+ push.file_name = "added_file.txt"
+ push.file_content = "File Added"
+ end
+
+ def initialize
+ @title = 'QA test - merge request'
+ @description = 'This is a test merge request'
+ @source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
+ @target_branch = "master"
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.act { new_merge_request }
+
+ Page::MergeRequest::New.perform do |page|
+ page.fill_title(@title)
+ page.fill_description(@description)
+ page.create_merge_request
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/personal_access_token.rb b/qa/qa/factory/resource/personal_access_token.rb
new file mode 100644
index 00000000000..514e3615d18
--- /dev/null
+++ b/qa/qa/factory/resource/personal_access_token.rb
@@ -0,0 +1,27 @@
+module QA
+ module Factory
+ module Resource
+ ##
+ # Create a personal access token that can be used by the api
+ #
+ class PersonalAccessToken < Factory::Base
+ attr_accessor :name
+
+ product :access_token do
+ Page::Profile::PersonalAccessTokens.act { created_access_token }
+ end
+
+ def fabricate!
+ Page::Menu::Main.act { go_to_profile_settings }
+ Page::Menu::Profile.act { click_access_tokens }
+
+ Page::Profile::PersonalAccessTokens.perform do |page|
+ page.fill_token_name(name || 'api-test-token')
+ page.check_api
+ page.create_token
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/runner.rb b/qa/qa/factory/resource/runner.rb
new file mode 100644
index 00000000000..5f37f8ac2e9
--- /dev/null
+++ b/qa/qa/factory/resource/runner.rb
@@ -0,0 +1,42 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class Runner < Factory::Base
+ attr_writer :name, :tags
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-ci-cd'
+ project.description = 'Project with CI/CD Pipelines'
+ end
+
+ def name
+ @name || "qa-runner-#{SecureRandom.hex(4)}"
+ end
+
+ def tags
+ @tags || %w[qa e2e]
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Menu::Side.act { click_ci_cd_settings }
+
+ Service::Runner.new(name).tap do |runner|
+ Page::Project::Settings::CICD.perform do |settings|
+ settings.expand_runners_settings do |runners|
+ runner.pull
+ runner.token = runners.registration_token
+ runner.address = runners.coordinator_address
+ runner.tags = tags
+ runner.register!
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/secret_variable.rb b/qa/qa/factory/resource/secret_variable.rb
new file mode 100644
index 00000000000..54ef4d8d964
--- /dev/null
+++ b/qa/qa/factory/resource/secret_variable.rb
@@ -0,0 +1,41 @@
+module QA
+ module Factory
+ module Resource
+ class SecretVariable < Factory::Base
+ attr_accessor :key, :value
+
+ product :key do
+ Page::Project::Settings::CICD.act do
+ expand_secret_variables(&:variable_key)
+ end
+ end
+
+ product :value do
+ Page::Project::Settings::CICD.act do
+ expand_secret_variables(&:variable_value)
+ end
+ end
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-secret-variables'
+ project.description = 'project for adding secret variable test'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Menu::Side.act { click_ci_cd_settings }
+
+ Page::Project::Settings::CICD.perform do |setting|
+ setting.expand_secret_variables do |page|
+ page.fill_variable_key(key)
+ page.fill_variable_value(value)
+
+ page.add_variable
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/README.md b/qa/qa/page/README.md
index f72fbfeafca..83710606d7c 100644
--- a/qa/qa/page/README.md
+++ b/qa/qa/page/README.md
@@ -77,7 +77,7 @@ module Page
view 'app/views/devise/sessions/_new_base.html.haml' do
element :login_field, 'text_field :login'
- element :passowrd_field, 'password_field :password'
+ element :password_field, 'password_field :password'
element :sign_in_button, 'submit "Sign in"'
end
@@ -103,6 +103,16 @@ view 'app/views/my/view.html.haml' do
end
```
+## Running the test locally
+
+During development, you can run the `qa:selectors` test by running
+
+```shell
+bin/qa Test::Sanity::Selectors
+```
+
+from within the `qa` directory.
+
## Where to ask for help?
If you need more information, ask for help on `#qa` channel on Slack (GitLab
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index ea4c920c82c..f472e8ccc7e 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -13,16 +13,18 @@ module QA
visit current_url
end
- def wait(css = '.application', time: 60)
- Time.now.tap do |start|
- while Time.now - start < time
- break if page.has_css?(css, wait: 5)
+ def wait(max: 60, time: 1, reload: true)
+ start = Time.now
- refresh
- end
+ while Time.now - start < max
+ return true if yield
+
+ sleep(time)
+
+ refresh if reload
end
- yield if block_given?
+ false
end
def scroll_to(selector, text: nil)
@@ -40,8 +42,26 @@ module QA
page.within(selector) { yield } if block_given?
end
+ def find_element(name)
+ find(element_selector_css(name))
+ end
+
def click_element(name)
- find(Page::Element.new(name).selector_css).click
+ find_element(name).click
+ end
+
+ def fill_element(name, content)
+ find_element(name).set(content)
+ end
+
+ def within_element(name)
+ page.within(element_selector_css(name)) do
+ yield
+ end
+ end
+
+ def element_selector_css(name)
+ Page::Element.new(name).selector_css
end
def self.path
diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb
index 71255b18362..73942cb856a 100644
--- a/qa/qa/page/dashboard/projects.rb
+++ b/qa/qa/page/dashboard/projects.rb
@@ -3,10 +3,21 @@ module QA
module Dashboard
class Projects < Page::Base
view 'app/views/dashboard/projects/index.html.haml'
+ view 'app/views/shared/projects/_search_form.html.haml' do
+ element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/
+ end
def go_to_project(name)
+ filter_by_name(name)
+
find_link(text: name).click
end
+
+ def filter_by_name(name)
+ page.within('form#project-filter-form') do
+ fill_in :name, with: name
+ end
+ end
end
end
end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 37ed3b35bce..f23294145dd 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -25,7 +25,12 @@ module QA
def go_to_new_subgroup
within '.new-project-subgroup' do
- find('.dropdown-toggle').click
+ # May need to click again because it is possible to click the button quicker than the JS is bound
+ wait(reload: false) do
+ find('.dropdown-toggle').click
+
+ page.has_css?("li[data-value='new-subgroup']")
+ end
find("li[data-value='new-subgroup']").click
end
@@ -34,7 +39,12 @@ module QA
def go_to_new_project
within '.new-project-subgroup' do
- find('.dropdown-toggle').click
+ # May need to click again because it is possible to click the button quicker than the JS is bound
+ wait(reload: false) do
+ find('.dropdown-toggle').click
+
+ page.has_css?("li[data-value='new-project']")
+ end
find("li[data-value='new-project']").click
end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 7b4c1603017..95880475ffa 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -10,24 +10,28 @@ module QA
view 'app/views/devise/sessions/_new_base.html.haml' do
element :login_field, 'text_field :login'
- element :passowrd_field, 'password_field :password'
+ element :password_field, 'password_field :password'
element :sign_in_button, 'submit "Sign in"'
end
def initialize
- wait('.application', time: 500)
+ wait(max: 500) do
+ page.has_css?('.application')
+ end
end
def sign_in_using_credentials
- if page.has_content?('Change your password')
+ using_wait_time 0 do
+ if page.has_content?('Change your password')
+ fill_in :user_password, with: Runtime::User.password
+ fill_in :user_password_confirmation, with: Runtime::User.password
+ click_button 'Change your password'
+ end
+
+ fill_in :user_login, with: Runtime::User.name
fill_in :user_password, with: Runtime::User.password
- fill_in :user_password_confirmation, with: Runtime::User.password
- click_button 'Change your password'
+ click_button 'Sign in'
end
-
- fill_in :user_login, with: Runtime::User.name
- fill_in :user_password, with: Runtime::User.password
- click_button 'Sign in'
end
def self.path
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/menu/main.rb
index f8978b8a5f7..df93a5fa2d2 100644
--- a/qa/qa/page/menu/main.rb
+++ b/qa/qa/page/menu/main.rb
@@ -7,6 +7,7 @@ module QA
element :user_avatar
element :user_menu, '.dropdown-menu-nav'
element :user_sign_out_link, 'link_to "Sign out"'
+ element :settings_link, 'link_to "Settings"'
end
view 'app/views/layouts/nav/_dashboard.html.haml' do
@@ -40,7 +41,13 @@ module QA
def sign_out
within_user_menu do
- click_link('Sign out')
+ click_link 'Sign out'
+ end
+ end
+
+ def go_to_profile_settings
+ within_user_menu do
+ click_link 'Settings'
end
end
diff --git a/qa/qa/page/menu/profile.rb b/qa/qa/page/menu/profile.rb
new file mode 100644
index 00000000000..95e88d863e4
--- /dev/null
+++ b/qa/qa/page/menu/profile.rb
@@ -0,0 +1,27 @@
+module QA
+ module Page
+ module Menu
+ class Profile < Page::Base
+ view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
+ element :access_token_link, 'link_to profile_personal_access_tokens_path'
+ element :access_token_title, 'Access Tokens'
+ element :top_level_items, '.sidebar-top-level-items'
+ end
+
+ def click_access_tokens
+ within_sidebar do
+ click_link('Access Tokens')
+ end
+ end
+
+ private
+
+ def within_sidebar
+ page.within('.sidebar-top-level-items') do
+ yield
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb
index 1df4e0c2429..b2738152907 100644
--- a/qa/qa/page/menu/side.rb
+++ b/qa/qa/page/menu/side.rb
@@ -4,19 +4,48 @@ module QA
class Side < Page::Base
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :settings_item
+ element :settings_link, 'link_to edit_project_path'
element :repository_link, "title: 'Repository'"
+ element :pipelines_settings_link, "title: 'CI / CD'"
element :top_level_items, '.sidebar-top-level-items'
+ element :activity_link, "title: 'Activity'"
end
- def click_repository_setting
- hover_setting do
- click_link('Repository')
+ view 'app/assets/javascripts/fly_out_nav.js' do
+ element :fly_out, "classList.add('fly-out-list')"
+ end
+
+ def click_repository_settings
+ hover_settings do
+ within_submenu do
+ click_link('Repository')
+ end
+ end
+ end
+
+ def click_ci_cd_settings
+ hover_settings do
+ within_submenu do
+ click_link('CI / CD')
+ end
+ end
+ end
+
+ def click_ci_cd_pipelines
+ within_sidebar do
+ click_link('CI / CD')
+ end
+ end
+
+ def go_to_settings
+ within_sidebar do
+ click_on 'Settings'
end
end
private
- def hover_setting
+ def hover_settings
within_sidebar do
find('.qa-settings-item').hover
@@ -29,6 +58,18 @@ module QA
yield
end
end
+
+ def go_to_activity
+ within_sidebar do
+ click_on 'Activity'
+ end
+ end
+
+ def within_submenu
+ page.within('.fly-out-list') do
+ yield
+ end
+ end
end
end
end
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
new file mode 100644
index 00000000000..ec94ff4ac98
--- /dev/null
+++ b/qa/qa/page/merge_request/new.rb
@@ -0,0 +1,31 @@
+module QA
+ module Page
+ module MergeRequest
+ class New < Page::Base
+ view 'app/views/shared/issuable/_form.html.haml' do
+ element :issuable_create_button
+ end
+
+ view 'app/views/shared/issuable/form/_title.html.haml' do
+ element :issuable_form_title
+ end
+
+ view 'app/views/shared/form_elements/_description.html.haml' do
+ element :issuable_form_description
+ end
+
+ def create_merge_request
+ click_element :issuable_create_button
+ end
+
+ def fill_title(title)
+ fill_element :issuable_form_title, title
+ end
+
+ def fill_description(description)
+ fill_element :issuable_form_description, description
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/profile/personal_access_tokens.rb b/qa/qa/page/profile/personal_access_tokens.rb
new file mode 100644
index 00000000000..f5ae47dadd0
--- /dev/null
+++ b/qa/qa/page/profile/personal_access_tokens.rb
@@ -0,0 +1,33 @@
+module QA
+ module Page
+ module Profile
+ class PersonalAccessTokens < Page::Base
+ view 'app/views/shared/_personal_access_tokens_form.html.haml' do
+ element :personal_access_token_name_field, 'text_field :name'
+ element :create_token_button, 'submit "Create #{type} token"' # rubocop:disable Lint/InterpolationCheck
+ element :scopes_api_radios, "label :scopes"
+ end
+
+ view 'app/views/profiles/personal_access_tokens/index.html.haml' do
+ element :create_token_field, "text_field_tag 'created-personal-access-token'"
+ end
+
+ def fill_token_name(name)
+ fill_in 'personal_access_token_name', with: name
+ end
+
+ def check_api
+ check 'personal_access_token_scopes_api'
+ end
+
+ def create_token
+ click_on 'Create personal access token'
+ end
+
+ def created_access_token
+ page.find('#created-personal-access-token').value
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/activity.rb b/qa/qa/page/project/activity.rb
new file mode 100644
index 00000000000..0196922c889
--- /dev/null
+++ b/qa/qa/page/project/activity.rb
@@ -0,0 +1,15 @@
+module QA
+ module Page
+ module Project
+ class Activity < Page::Base
+ view 'app/views/shared/_event_filter.html.haml' do
+ element :push_events, "event_filter_link EventFilter.push, _('Push events')"
+ end
+
+ def go_to_push_events
+ click_on 'Push events'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 9b1438f76d5..186a4724326 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -4,7 +4,7 @@ module QA
class New < Page::Base
view 'app/views/projects/_new_project_fields.html.haml' do
element :project_namespace_select
- element :project_namespace_field, 'select :namespace_id'
+ element :project_namespace_field, /select :namespace_id.*class: 'select2/
element :project_path, 'text_field :path'
element :project_description, 'text_area :description'
element :project_create_button, "submit 'Create project'"
@@ -13,7 +13,7 @@ module QA
def choose_test_namespace
click_element :project_namespace_select
- first('li', text: Runtime::Namespace.path).click
+ find('ul.select2-result-sub > li', text: Runtime::Namespace.path).click
end
def choose_name(name)
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
new file mode 100644
index 00000000000..32c108393b9
--- /dev/null
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -0,0 +1,13 @@
+module QA::Page
+ module Project::Pipeline
+ class Index < QA::Page::Base
+ view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do
+ element :pipeline_link, 'class="js-pipeline-url-link"'
+ end
+
+ def go_to_latest_pipeline
+ first('.js-pipeline-url-link').click
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
new file mode 100644
index 00000000000..0835173f1cd
--- /dev/null
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -0,0 +1,35 @@
+module QA::Page
+ module Project::Pipeline
+ class Show < QA::Page::Base
+ view 'app/assets/javascripts/vue_shared/components/header_ci_component.vue' do
+ element :pipeline_header, /header class.*ci-header-container.*/
+ end
+
+ view 'app/assets/javascripts/pipelines/components/graph/graph_component.vue' do
+ element :pipeline_graph, /class.*pipeline-graph.*/
+ end
+
+ view 'app/assets/javascripts/pipelines/components/graph/job_component.vue' do
+ element :job_component, /class.*ci-job-component.*/
+ end
+
+ view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
+ element :status_icon, 'ci-status-icon-${status}'
+ end
+
+ def running?
+ within('.ci-header-container') do
+ return page.has_content?('running')
+ end
+ end
+
+ def has_build?(name, status: :success)
+ within('.pipeline-graph') do
+ within('.ci-job-component', text: name) do
+ return has_selector?(".ci-status-icon-#{status}")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb
new file mode 100644
index 00000000000..5ef00504fdf
--- /dev/null
+++ b/qa/qa/page/project/settings/advanced.rb
@@ -0,0 +1,33 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class Advanced < Page::Base
+ view 'app/views/projects/edit.html.haml' do
+ element :project_path_field, 'f.text_field :path'
+ element :project_name_field, 'f.text_field :name'
+ element :rename_project_button, "f.submit 'Rename project'"
+ end
+
+ def rename_to(path)
+ fill_project_name(path)
+ fill_project_path(path)
+ rename_project!
+ end
+
+ def fill_project_path(path)
+ fill_in :project_path, with: path
+ end
+
+ def fill_project_name(name)
+ fill_in :project_name, with: name
+ end
+
+ def rename_project!
+ click_on 'Rename project'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb
new file mode 100644
index 00000000000..99be21bbe89
--- /dev/null
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -0,0 +1,28 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class CICD < Page::Base
+ include Common
+
+ view 'app/views/projects/settings/ci_cd/show.html.haml' do
+ element :runners_settings, 'Runners settings'
+ element :secret_variables, 'Secret variables'
+ end
+
+ def expand_runners_settings(&block)
+ expand_section('Runners settings') do
+ Settings::Runners.perform(&block)
+ end
+ end
+
+ def expand_secret_variables(&block)
+ expand_section('Secret variables') do
+ Settings::SecretVariables.perform(&block)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/common.rb b/qa/qa/page/project/settings/common.rb
index 5d1d5120929..319cb1045b6 100644
--- a/qa/qa/page/project/settings/common.rb
+++ b/qa/qa/page/project/settings/common.rb
@@ -3,11 +3,29 @@ module QA
module Project
module Settings
module Common
- def expand(selector)
+ def self.included(base)
+ base.class_eval do
+ view 'app/views/projects/edit.html.haml' do
+ element :advanced_settings_expand, "= expanded ? 'Collapse' : 'Expand'"
+ end
+ end
+ end
+
+ # Click the Expand button present in the specified section
+ #
+ # @param [String] name present in the container in the DOM
+ def expand_section(name)
page.within('#content-body') do
- find(selector).click
+ page.within('section', text: name) do
+ # Because it is possible to click the button before the JS toggle code is bound
+ wait(reload: false) do
+ click_button 'Expand' unless first('button', text: 'Collapse')
+
+ page.has_content?('Collapse')
+ end
- yield
+ yield if block_given?
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index a8d6f09777c..332e84724c7 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -3,12 +3,20 @@ module QA
module Project
module Settings
class DeployKeys < Page::Base
- ##
- # TODO, define all selectors required by this page object
- #
- # See gitlab-org/gitlab-qa#154
- #
- view 'app/views/projects/deploy_keys/edit.html.haml'
+ view 'app/views/projects/deploy_keys/_form.html.haml' do
+ element :deploy_key_title, 'text_field :title'
+ element :deploy_key_key, 'text_area :key'
+ end
+
+ view 'app/assets/javascripts/deploy_keys/components/app.vue' do
+ element :deploy_keys_section, /class=".*deploy\-keys.*"/
+ element :project_deploy_keys, 'class="qa-project-deploy-keys"'
+ end
+
+ view 'app/assets/javascripts/deploy_keys/components/key.vue' do
+ element :key_title, /class=".*qa-key-title.*"/
+ element :key_fingerprint, /class=".*qa-key-fingerprint.*"/
+ end
def fill_key_title(title)
fill_in 'deploy_key_title', with: title
@@ -22,9 +30,23 @@ module QA
click_on 'Add key'
end
- def has_key_title?(title)
- page.within('.deploy-keys') do
- page.find('.title', text: title)
+ def key_title
+ within_project_deploy_keys do
+ find_element(:key_title).text
+ end
+ end
+
+ def key_fingerprint
+ within_project_deploy_keys do
+ find_element(:key_fingerprint).text
+ end
+ end
+
+ private
+
+ def within_project_deploy_keys
+ within_element(:project_deploy_keys) do
+ yield
end
end
end
diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb
new file mode 100644
index 00000000000..5d743f4c9c8
--- /dev/null
+++ b/qa/qa/page/project/settings/main.rb
@@ -0,0 +1,21 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class Main < Page::Base
+ include Common
+
+ view 'app/views/projects/edit.html.haml' do
+ element :advanced_settings_section, 'Advanced settings'
+ end
+
+ def expand_advanced_settings(&block)
+ expand_section('Advanced settings') do
+ Advanced.perform(&block)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index 524d87c6be9..22362164a1a 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -5,15 +5,12 @@ module QA
class Repository < Page::Base
include Common
- ##
- # TODO, define all selectors required by this page object
- #
- # See gitlab-org/gitlab-qa#154
- #
- view 'app/views/projects/settings/repository/show.html.haml'
+ view 'app/views/projects/deploy_keys/_index.html.haml' do
+ element :deploy_keys_section, 'Deploy Keys'
+ end
def expand_deploy_keys(&block)
- expand('.qa-expand-deploy-keys') do
+ expand_section('Deploy Keys') do
DeployKeys.perform(&block)
end
end
diff --git a/qa/qa/page/project/settings/runners.rb b/qa/qa/page/project/settings/runners.rb
new file mode 100644
index 00000000000..b41668c94cd
--- /dev/null
+++ b/qa/qa/page/project/settings/runners.rb
@@ -0,0 +1,35 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class Runners < Page::Base
+ view 'app/views/ci/runner/_how_to_setup_runner.html.haml' do
+ element :registration_token, '%code#registration_token'
+ element :coordinator_address, '%code#coordinator_address'
+ end
+
+ ##
+ # TODO, phase-out CSS classes added in Ruby helpers.
+ #
+ view 'app/helpers/runners_helper.rb' do
+ # rubocop:disable Lint/InterpolationCheck
+ element :runner_status, 'runner-status-#{status}'
+ # rubocop:enable Lint/InterpolationCheck
+ end
+
+ def registration_token
+ find('code#registration_token').text
+ end
+
+ def coordinator_address
+ find('code#coordinator_address').text
+ end
+
+ def has_online_runner?
+ page.has_css?('.runner-status-online')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/secret_variables.rb b/qa/qa/page/project/settings/secret_variables.rb
new file mode 100644
index 00000000000..e3bfbfcf080
--- /dev/null
+++ b/qa/qa/page/project/settings/secret_variables.rb
@@ -0,0 +1,57 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class SecretVariables < Page::Base
+ include Common
+
+ view 'app/views/ci/variables/_table.html.haml' do
+ element :variable_key, '.variable-key'
+ element :variable_value, '.variable-value'
+ end
+
+ view 'app/views/ci/variables/_index.html.haml' do
+ element :add_new_variable, 'btn_text: "Add new variable"'
+ end
+
+ view 'app/assets/javascripts/behaviors/secret_values.js' do
+ element :reveal_value, 'Reveal value'
+ element :hide_value, 'Hide value'
+ end
+
+ def fill_variable_key(key)
+ fill_in 'variable_key', with: key
+ end
+
+ def fill_variable_value(value)
+ fill_in 'variable_value', with: value
+ end
+
+ def add_variable
+ click_on 'Add new variable'
+ end
+
+ def variable_key
+ page.find('.variable-key').text
+ end
+
+ def variable_value
+ reveal_value do
+ page.find('.variable-value').text
+ end
+ end
+
+ private
+
+ def reveal_value
+ click_button('Reveal value')
+
+ yield.tap do
+ click_button('Hide value')
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index c8af5ba6280..75308ae8a3c 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -3,12 +3,14 @@ module QA
module Project
class Show < Page::Base
view 'app/views/shared/_clone_panel.html.haml' do
+ element :clone_holder, '.git-clone-holder'
element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown'
+ element :project_repository_location, 'text_field_tag :project_clone'
end
- view 'app/views/shared/_clone_panel.html.haml' do
- element :project_repository_location, 'text_field_tag :project_clone'
+ view 'app/views/projects/_last_push.html.haml' do
+ element :create_merge_request
end
view 'app/views/projects/_home_panel.html.haml' do
@@ -16,10 +18,15 @@ module QA
end
def choose_repository_clone_http
- click_element :clone_dropdown
+ wait(reload: false) do
+ click_element :clone_dropdown
- page.within('.clone-options-dropdown') do
- click_link('HTTP')
+ page.within('.clone-options-dropdown') do
+ click_link('HTTP')
+ end
+
+ # Ensure git clone textbox was updated to http URI
+ page.has_css?('.git-clone-holder input#project_clone[value*="http"]')
end
end
@@ -31,8 +38,13 @@ module QA
find('.qa-project-name').text
end
+ def new_merge_request
+ click_element :create_merge_request
+ end
+
def wait_for_push
sleep 5
+ refresh
end
end
end
diff --git a/qa/qa/runtime/address.rb b/qa/qa/runtime/address.rb
new file mode 100644
index 00000000000..ffad3974b02
--- /dev/null
+++ b/qa/qa/runtime/address.rb
@@ -0,0 +1,20 @@
+module QA
+ module Runtime
+ class Address
+ attr_reader :address
+
+ def initialize(instance, page = nil)
+ @instance = instance
+ @address = host + (page.is_a?(String) ? page : page&.path)
+ end
+
+ def host
+ if @instance.is_a?(Symbol)
+ Runtime::Scenario.send("#{@instance}_address")
+ else
+ @instance.to_s
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/api.rb b/qa/qa/runtime/api.rb
new file mode 100644
index 00000000000..e2a096b971d
--- /dev/null
+++ b/qa/qa/runtime/api.rb
@@ -0,0 +1,82 @@
+require 'airborne'
+
+module QA
+ module Runtime
+ module API
+ class Client
+ attr_reader :address
+
+ def initialize(address = :gitlab)
+ @address = address
+ end
+
+ def personal_access_token
+ @personal_access_token ||= get_personal_access_token
+ end
+
+ def get_personal_access_token
+ # you can set the environment variable PERSONAL_ACCESS_TOKEN
+ # to use a specific access token rather than create one from the UI
+ if Runtime::Env.personal_access_token
+ Runtime::Env.personal_access_token
+ else
+ create_personal_access_token
+ end
+ end
+
+ private
+
+ def create_personal_access_token
+ Runtime::Browser.visit(@address, Page::Main::Login) do
+ Page::Main::Login.act { sign_in_using_credentials }
+ Factory::Resource::PersonalAccessToken.fabricate!.access_token
+ end
+ end
+ end
+
+ class Request
+ API_VERSION = 'v4'.freeze
+
+ def initialize(api_client, path, personal_access_token: nil)
+ personal_access_token ||= api_client.personal_access_token
+ request_path = request_path(path, personal_access_token: personal_access_token)
+ @session_address = Runtime::Address.new(api_client.address, request_path)
+ end
+
+ def url
+ @session_address.address
+ end
+
+ # Prepend a request path with the path to the API
+ #
+ # path - Path to append
+ #
+ # Examples
+ #
+ # >> request_path('/issues')
+ # => "/api/v4/issues"
+ #
+ # >> request_path('/issues', personal_access_token: 'sometoken)
+ # => "/api/v4/issues?private_token=..."
+ #
+ # Returns the relative path to the requested API resource
+ def request_path(path, version: API_VERSION, personal_access_token: nil, oauth_access_token: nil)
+ full_path = File.join('/api', version, path)
+
+ if oauth_access_token
+ query_string = "access_token=#{oauth_access_token}"
+ elsif personal_access_token
+ query_string = "private_token=#{personal_access_token}"
+ end
+
+ if query_string
+ full_path << (path.include?('?') ? '&' : '?')
+ full_path << query_string
+ end
+
+ full_path
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 14b2a488760..ce888b51ea5 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -23,13 +23,11 @@ module QA
# In case of an address that is a symbol we will try to guess address
# based on `Runtime::Scenario#something_address`.
#
- def visit(address, page, &block)
- Browser::Session.new(address, page).tap do |session|
- session.perform(&block)
- end
+ def visit(address, page = nil, &block)
+ Browser::Session.new(address, page).perform(&block)
end
- def self.visit(address, page, &block)
+ def self.visit(address, page = nil, &block)
new.visit(address, page, &block)
end
@@ -94,20 +92,15 @@ module QA
include Capybara::DSL
def initialize(instance, page = nil)
- @instance = instance
- @address = host + page&.path
+ @session_address = Runtime::Address.new(instance, page)
end
- def host
- if @instance.is_a?(Symbol)
- Runtime::Scenario.send("#{@instance}_address")
- else
- @instance.to_s
- end
+ def url
+ @session_address.address
end
def perform(&block)
- visit(@address)
+ visit(url)
yield if block_given?
rescue
@@ -130,7 +123,7 @@ module QA
# See gitlab-org/gitlab-qa#102
#
def clear!
- visit(@address)
+ visit(url)
reset_session!
end
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index d5c28e9a7db..56944e8b641 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -3,6 +3,7 @@ module QA
module Env
extend self
+ # set to 'false' to have Chrome run visibly instead of headless
def chrome_headless?
(ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i) != 0
end
@@ -10,6 +11,11 @@ module QA
def running_in_ci?
ENV['CI'] || ENV['CI_SERVER']
end
+
+ # specifies token that can be used for the api
+ def personal_access_token
+ ENV['PERSONAL_ACCESS_TOKEN']
+ end
end
end
end
diff --git a/qa/qa/runtime/rsa_key.rb b/qa/qa/runtime/rsa_key.rb
new file mode 100644
index 00000000000..d456062bce7
--- /dev/null
+++ b/qa/qa/runtime/rsa_key.rb
@@ -0,0 +1,21 @@
+require 'net/ssh'
+require 'forwardable'
+
+module QA
+ module Runtime
+ class RSAKey
+ extend Forwardable
+
+ attr_reader :key
+ def_delegators :@key, :fingerprint
+
+ def initialize(bits = 4096)
+ @key = OpenSSL::PKey::RSA.new(bits)
+ end
+
+ def public_key
+ @public_key ||= "#{key.ssh_type} #{[key.to_blob].pack('m0')}"
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index 2832439d9e0..60027c89ab1 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -10,17 +10,6 @@ module QA
def password
ENV['GITLAB_PASSWORD'] || '5iveL!fe'
end
-
- def ssh_key
- <<~KEY.delete("\n")
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9
- 6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5
- /jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7
- M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC
- rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0
- 5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com
- KEY
- end
end
end
end
diff --git a/qa/qa/scenario/entrypoint.rb b/qa/qa/scenario/entrypoint.rb
deleted file mode 100644
index ae099fd911e..00000000000
--- a/qa/qa/scenario/entrypoint.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module QA
- module Scenario
- ##
- # Base class for running the suite against any GitLab instance,
- # including staging and on-premises installation.
- #
- class Entrypoint < Template
- include Bootable
-
- def perform(address, *files)
- Runtime::Scenario.define(:gitlab_address, address)
-
- ##
- # Perform before hooks, which are different for CE and EE
- #
- Runtime::Release.perform_before_hooks
-
- Specs::Runner.perform do |specs|
- specs.tty = true
- specs.tags = self.class.get_tags
- specs.files = files.any? ? files : 'qa/specs/features'
- end
- end
-
- def self.tags(*tags)
- @tags = tags
- end
-
- def self.get_tags
- @tags
- end
- end
- end
-end
diff --git a/qa/qa/scenario/taggable.rb b/qa/qa/scenario/taggable.rb
new file mode 100644
index 00000000000..b1f24d742e0
--- /dev/null
+++ b/qa/qa/scenario/taggable.rb
@@ -0,0 +1,17 @@
+module QA
+ module Scenario
+ module Taggable
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+
+ def tags(*tags)
+ @tags = tags
+ end
+
+ def focus
+ @tags.to_a
+ end
+
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb
index e2a1f6bf2bd..993bbd723a3 100644
--- a/qa/qa/scenario/test/instance.rb
+++ b/qa/qa/scenario/test/instance.rb
@@ -2,11 +2,29 @@ module QA
module Scenario
module Test
##
- # Run test suite against any GitLab instance,
+ # Base class for running the suite against any GitLab instance,
# including staging and on-premises installation.
#
- class Instance < Entrypoint
+ class Instance < Template
+ include Bootable
+ extend Taggable
+
tags :core
+
+ def perform(address, *files)
+ Runtime::Scenario.define(:gitlab_address, address)
+
+ ##
+ # Perform before hooks, which are different for CE and EE
+ #
+ Runtime::Release.perform_before_hooks
+
+ Specs::Runner.perform do |specs|
+ specs.tty = true
+ specs.tags = self.class.focus
+ specs.files = files.any? ? files : 'qa/specs/features'
+ end
+ end
end
end
end
diff --git a/qa/qa/scenario/test/integration/mattermost.rb b/qa/qa/scenario/test/integration/mattermost.rb
index 7d0702afdb1..d939f52ab16 100644
--- a/qa/qa/scenario/test/integration/mattermost.rb
+++ b/qa/qa/scenario/test/integration/mattermost.rb
@@ -6,7 +6,7 @@ module QA
# Run test suite against any GitLab instance where mattermost is enabled,
# including staging and on-premises installation.
#
- class Mattermost < Scenario::Entrypoint
+ class Mattermost < Test::Instance
tags :core, :mattermost
def perform(address, mattermost, *files)
diff --git a/qa/qa/service/omnibus.rb b/qa/qa/service/omnibus.rb
new file mode 100644
index 00000000000..b5c06874e5c
--- /dev/null
+++ b/qa/qa/service/omnibus.rb
@@ -0,0 +1,20 @@
+module QA
+ module Service
+ class Omnibus
+ include Scenario::Actable
+ include Service::Shellout
+
+ def initialize(container)
+ @name = container
+ end
+
+ def gitlab_ctl(command, input: nil)
+ if input.nil?
+ shell "docker exec #{@name} gitlab-ctl #{command}"
+ else
+ shell "docker exec #{@name} bash -c '#{input} | gitlab-ctl #{command}'"
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/runner.rb b/qa/qa/service/runner.rb
new file mode 100644
index 00000000000..d0ee33c69f2
--- /dev/null
+++ b/qa/qa/service/runner.rb
@@ -0,0 +1,41 @@
+require 'securerandom'
+
+module QA
+ module Service
+ class Runner
+ include Scenario::Actable
+ include Service::Shellout
+
+ attr_accessor :token, :address, :tags, :image
+
+ def initialize(name)
+ @image = 'gitlab/gitlab-runner:alpine'
+ @name = name || "qa-runner-#{SecureRandom.hex(4)}"
+ @network = Runtime::Scenario.attributes[:network] || 'test'
+ @tags = %w[qa test]
+ end
+
+ def pull
+ shell "docker pull #{@image}"
+ end
+
+ def register!
+ shell <<~CMD.tr("\n", ' ')
+ docker run -d --rm --entrypoint=/bin/sh
+ --network #{@network} --name #{@name}
+ -e CI_SERVER_URL=#{@address}
+ -e REGISTER_NON_INTERACTIVE=true
+ -e REGISTRATION_TOKEN=#{@token}
+ -e RUNNER_EXECUTOR=shell
+ -e RUNNER_TAG_LIST=#{@tags.join(',')}
+ -e RUNNER_NAME=#{@name}
+ #{@image} -c 'gitlab-runner register && gitlab-runner run'
+ CMD
+ end
+
+ def remove!
+ shell "docker rm -f #{@name}"
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/shellout.rb b/qa/qa/service/shellout.rb
new file mode 100644
index 00000000000..898febde63c
--- /dev/null
+++ b/qa/qa/service/shellout.rb
@@ -0,0 +1,23 @@
+require 'open3'
+
+module QA
+ module Service
+ module Shellout
+ ##
+ # TODO, make it possible to use generic QA framework classes
+ # as a library - gitlab-org/gitlab-qa#94
+ #
+ def shell(command)
+ puts "Executing `#{command}`"
+
+ Open3.popen2e(command) do |_in, out, wait|
+ out.each { |line| puts line }
+
+ if wait.value.exited? && wait.value.exitstatus.nonzero?
+ raise "Command `#{command}` failed!"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/shell/omnibus.rb b/qa/qa/shell/omnibus.rb
deleted file mode 100644
index 6b3628d3109..00000000000
--- a/qa/qa/shell/omnibus.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'open3'
-
-module QA
- module Shell
- class Omnibus
- include Scenario::Actable
-
- def initialize(container)
- @name = container
- end
-
- def gitlab_ctl(command, input: nil)
- if input.nil?
- shell "docker exec #{@name} gitlab-ctl #{command}"
- else
- shell "docker exec #{@name} bash -c '#{input} | gitlab-ctl #{command}'"
- end
- end
-
- private
-
- ##
- # TODO, make it possible to use generic QA framework classes
- # as a library - gitlab-org/gitlab-qa#94
- #
- def shell(command)
- puts "Executing `#{command}`"
-
- Open3.popen2e(command) do |_in, out, wait|
- out.each { |line| puts line }
-
- if wait.value.exited? && wait.value.exitstatus.nonzero?
- raise "Docker command `#{command}` failed!"
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb
new file mode 100644
index 00000000000..9d039590a0e
--- /dev/null
+++ b/qa/qa/specs/features/api/users_spec.rb
@@ -0,0 +1,42 @@
+module QA
+ feature 'API users', :core do
+ before(:context) do
+ @api_client = Runtime::API::Client.new(:gitlab)
+ end
+
+ context 'when authenticated' do
+ let(:request) { Runtime::API::Request.new(@api_client, '/users') }
+
+ scenario 'get list of users' do
+ get request.url
+
+ expect_status(200)
+ end
+
+ scenario 'submit request with a valid user name' do
+ get request.url, { params: { username: 'root' } }
+
+ expect_status(200)
+ expect(json_body).to be_an Array
+ expect(json_body.size).to eq(1)
+ expect(json_body.first[:username]).to eq Runtime::User.name
+ end
+
+ scenario 'submit request with an invalid user name' do
+ get request.url, { params: { username: 'invalid' } }
+
+ expect_status(200)
+ expect(json_body).to be_an Array
+ expect(json_body.size).to eq(0)
+ end
+ end
+
+ scenario 'submit request with an invalid token' do
+ request = Runtime::API::Request.new(@api_client, '/users', personal_access_token: 'invalid')
+
+ get request.url
+
+ expect_status(401)
+ end
+ end
+end
diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb
new file mode 100644
index 00000000000..fbf9a4d17e5
--- /dev/null
+++ b/qa/qa/specs/features/merge_request/create_spec.rb
@@ -0,0 +1,17 @@
+module QA
+ feature 'creates a merge request', :core do
+ scenario 'user creates a new merge request' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Factory::Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.title = 'This is a merge request'
+ merge_request.description = 'Great feature'
+ end
+
+ expect(page).to have_content('This is a merge request')
+ expect(page).to have_content('Great feature')
+ expect(page).to have_content('Opened less than a minute ago')
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/activity_spec.rb b/qa/qa/specs/features/project/activity_spec.rb
new file mode 100644
index 00000000000..ba94ce8cf28
--- /dev/null
+++ b/qa/qa/specs/features/project/activity_spec.rb
@@ -0,0 +1,20 @@
+module QA
+ feature 'activity page', :core do
+ scenario 'push creates an event in the activity page' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Factory::Repository::Push.fabricate! do |push|
+ push.file_name = 'README.md'
+ push.file_content = '# This is a test project'
+ push.commit_message = 'Add README.md'
+ end
+
+ Page::Menu::Side.act { go_to_activity }
+
+ Page::Project::Activity.act { go_to_push_events }
+
+ expect(page).to have_content('pushed new branch master')
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb
index 43a85213501..b9998dda895 100644
--- a/qa/qa/specs/features/project/add_deploy_key_spec.rb
+++ b/qa/qa/specs/features/project/add_deploy_key_spec.rb
@@ -1,22 +1,20 @@
module QA
feature 'deploy keys support', :core do
- given(:deploy_key_title) { 'deploy key title' }
- given(:deploy_key_value) { Runtime::User.ssh_key }
-
scenario 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
- Factory::Resource::DeployKey.fabricate! do |deploy_key|
- deploy_key.title = deploy_key_title
- deploy_key.key = deploy_key_value
- end
+ key = Runtime::RSAKey.new
+ deploy_key_title = 'deploy key title'
+ deploy_key_value = key.public_key
- Page::Project::Settings::Repository.perform do |setting|
- setting.expand_deploy_keys do |page|
- expect(page).to have_key_title(deploy_key_title)
- end
+ deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
+ resource.title = deploy_key_title
+ resource.key = deploy_key_value
end
+
+ expect(deploy_key.title).to eq(deploy_key_title)
+ expect(deploy_key.fingerprint).to eq(key.fingerprint)
end
end
end
diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb
new file mode 100644
index 00000000000..36422a92afc
--- /dev/null
+++ b/qa/qa/specs/features/project/add_secret_variable_spec.rb
@@ -0,0 +1,19 @@
+module QA
+ feature 'secret variables support', :core do
+ scenario 'user adds a secret variable' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ variable_key = 'VARIABLE_KEY'
+ variable_value = 'variable value'
+
+ variable = Factory::Resource::SecretVariable.fabricate! do |resource|
+ resource.key = variable_key
+ resource.value = variable_value
+ end
+
+ expect(variable.key).to eq(variable_key)
+ expect(variable.value).to eq(variable_value)
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/pipelines_spec.rb b/qa/qa/specs/features/project/pipelines_spec.rb
new file mode 100644
index 00000000000..1bb7730e06c
--- /dev/null
+++ b/qa/qa/specs/features/project/pipelines_spec.rb
@@ -0,0 +1,102 @@
+module QA
+ feature 'CI/CD Pipelines', :core, :docker do
+ let(:executor) { "qa-runner-#{Time.now.to_i}" }
+
+ after do
+ Service::Runner.new(executor).remove!
+ end
+
+ scenario 'user registers a new specific runner' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Factory::Resource::Runner.fabricate! do |runner|
+ runner.name = executor
+ end
+
+ Page::Project::Settings::CICD.perform do |settings|
+ sleep 5 # Runner should register within 5 seconds
+ settings.refresh
+
+ settings.expand_runners_settings do |page|
+ expect(page).to have_content(executor)
+ expect(page).to have_online_runner
+ end
+ end
+ end
+
+ scenario 'users creates a new pipeline' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Factory::Resource::Project.fabricate! do |project|
+ project.name = 'project-with-pipelines'
+ project.description = 'Project with CI/CD Pipelines.'
+ end
+
+ Factory::Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = %w[qa test]
+ end
+
+ Factory::Repository::Push.fabricate! do |push|
+ push.project = project
+ push.file_name = '.gitlab-ci.yml'
+ push.commit_message = 'Add .gitlab-ci.yml'
+ push.file_content = <<~EOF
+ test-success:
+ tags:
+ - qa
+ - test
+ script: echo 'OK'
+
+ test-failure:
+ tags:
+ - qa
+ - test
+ script:
+ - echo 'FAILURE'
+ - exit 1
+
+ test-tags:
+ tags:
+ - qa
+ - docker
+ script: echo 'NOOP'
+
+ test-artifacts:
+ tags:
+ - qa
+ - test
+ script: echo "CONTENTS" > my-artifacts/artifact.txt
+ artifacts:
+ paths:
+ - my-artifacts/
+ EOF
+ end
+
+ Page::Project::Show.act { wait_for_push }
+
+ expect(page).to have_content('Add .gitlab-ci.yml')
+
+ Page::Menu::Side.act { click_ci_cd_pipelines }
+
+ expect(page).to have_content('All 1')
+ expect(page).to have_content('Add .gitlab-ci.yml')
+
+ puts 'Waiting for the runner to process the pipeline'
+ sleep 15 # Runner should process all jobs within 15 seconds.
+
+ Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to be_running
+ expect(pipeline).to have_build('test-success', status: :success)
+ expect(pipeline).to have_build('test-failure', status: :failed)
+ expect(pipeline).to have_build('test-tags', status: :pending)
+ expect(pipeline).to have_build('test-artifacts', status: :failed)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/repository/push_spec.rb b/qa/qa/specs/features/repository/push_spec.rb
index 4f6ffe14c9f..51d9c2c7fd2 100644
--- a/qa/qa/specs/features/repository/push_spec.rb
+++ b/qa/qa/specs/features/repository/push_spec.rb
@@ -11,10 +11,7 @@ module QA
push.commit_message = 'Add README.md'
end
- Page::Project::Show.act do
- wait_for_push
- refresh
- end
+ Page::Project::Show.act { wait_for_push }
expect(page).to have_content('README.md')
expect(page).to have_content('This is a test project')
diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb
index 90dd58e20fd..c5663049be8 100644
--- a/qa/spec/factory/base_spec.rb
+++ b/qa/spec/factory/base_spec.rb
@@ -19,7 +19,6 @@ describe QA::Factory::Base do
it 'returns fabrication product' do
allow(subject).to receive(:new).and_return(factory)
- allow(factory).to receive(:fabricate!).and_return('something')
result = subject.fabricate!('something')
diff --git a/qa/spec/factory/dependency_spec.rb b/qa/spec/factory/dependency_spec.rb
index 32405415126..8aaa6665a18 100644
--- a/qa/spec/factory/dependency_spec.rb
+++ b/qa/spec/factory/dependency_spec.rb
@@ -54,6 +54,19 @@ describe QA::Factory::Dependency do
expect(factory).to have_received(:mydep=).with(dependency)
end
+
+ context 'when receives a caller factory as block argument' do
+ let(:dependency) { QA::Factory::Base }
+
+ it 'calls given block with dependency factory and caller factory' do
+ allow_any_instance_of(QA::Factory::Base).to receive(:fabricate!).and_return(factory)
+ allow(QA::Factory::Product).to receive(:populate!).and_return(spy('any'))
+
+ subject.build!
+
+ expect(block).to have_received(:call).with(an_instance_of(QA::Factory::Base), factory)
+ end
+ end
end
end
end
diff --git a/qa/spec/runtime/api_client_spec.rb b/qa/spec/runtime/api_client_spec.rb
new file mode 100644
index 00000000000..d497d8839b8
--- /dev/null
+++ b/qa/spec/runtime/api_client_spec.rb
@@ -0,0 +1,30 @@
+describe QA::Runtime::API::Client do
+ include Support::StubENV
+
+ describe 'initialization' do
+ it 'defaults to :gitlab address' do
+ expect(described_class.new.address).to eq :gitlab
+ end
+
+ it 'uses specified address' do
+ client = described_class.new('http:///example.com')
+
+ expect(client.address).to eq 'http:///example.com'
+ end
+ end
+
+ describe '#get_personal_access_token' do
+ it 'returns specified token from env' do
+ stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
+
+ expect(described_class.new.get_personal_access_token).to eq 'a_token'
+ end
+
+ it 'returns a created token' do
+ allow_any_instance_of(described_class)
+ .to receive(:create_personal_access_token).and_return('created_token')
+
+ expect(described_class.new.get_personal_access_token).to eq 'created_token'
+ end
+ end
+end
diff --git a/qa/spec/runtime/api_request_spec.rb b/qa/spec/runtime/api_request_spec.rb
new file mode 100644
index 00000000000..9a1ed8a7a46
--- /dev/null
+++ b/qa/spec/runtime/api_request_spec.rb
@@ -0,0 +1,42 @@
+describe QA::Runtime::API::Request do
+ include Support::StubENV
+
+ before do
+ stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
+ end
+
+ let(:client) { QA::Runtime::API::Client.new('http://example.com') }
+ let(:request) { described_class.new(client, '/users') }
+
+ describe '#url' do
+ it 'returns the full api request url' do
+ expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token'
+ end
+ end
+
+ describe '#request_path' do
+ it 'prepends the api path' do
+ expect(request.request_path('/users')).to eq '/api/v4/users'
+ end
+
+ it 'adds the personal access token' do
+ expect(request.request_path('/users', personal_access_token: 'token'))
+ .to eq '/api/v4/users?private_token=token'
+ end
+
+ it 'adds the oauth access token' do
+ expect(request.request_path('/users', oauth_access_token: 'otoken'))
+ .to eq '/api/v4/users?access_token=otoken'
+ end
+
+ it 'respects query parameters' do
+ expect(request.request_path('/users?page=1')).to eq '/api/v4/users?page=1'
+ expect(request.request_path('/users?page=1', personal_access_token: 'token'))
+ .to eq '/api/v4/users?page=1&private_token=token'
+ end
+
+ it 'uses a different api version' do
+ expect(request.request_path('/users', version: 'v3')).to eq '/api/v3/users'
+ end
+ end
+end
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 57a72a04507..103573db6be 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -1,7 +1,5 @@
describe QA::Runtime::Env do
- before do
- allow(ENV).to receive(:[]).and_call_original
- end
+ include Support::StubENV
describe '.chrome_headless?' do
context 'when there is an env variable set' do
@@ -57,8 +55,4 @@ describe QA::Runtime::Env do
end
end
end
-
- def stub_env(name, value)
- allow(ENV).to receive(:[]).with(name).and_return(value)
- end
end
diff --git a/qa/spec/runtime/rsa_key.rb b/qa/spec/runtime/rsa_key.rb
new file mode 100644
index 00000000000..ff277b9077b
--- /dev/null
+++ b/qa/spec/runtime/rsa_key.rb
@@ -0,0 +1,9 @@
+describe QA::Runtime::RSAKey do
+ describe '#public_key' do
+ subject { described_class.new.public_key }
+
+ it 'generates a public RSA key' do
+ expect(subject).to match(/\Assh\-rsa AAAA[0-9A-Za-z+\/]+={0,3}\z/)
+ end
+ end
+end
diff --git a/qa/spec/scenario/entrypoint_spec.rb b/qa/spec/scenario/test/instance_spec.rb
index aec79dcea04..1824db54c9b 100644
--- a/qa/spec/scenario/entrypoint_spec.rb
+++ b/qa/spec/scenario/test/instance_spec.rb
@@ -1,6 +1,6 @@
-describe QA::Scenario::Entrypoint do
+describe QA::Scenario::Test::Instance do
subject do
- Class.new(QA::Scenario::Entrypoint) do
+ Class.new(described_class) do
tags :rspec
end
end
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 64d06ef6558..c2c6cf95406 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -1,5 +1,7 @@
require_relative '../qa'
+Dir[File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f }
+
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
diff --git a/qa/spec/support/stub_env.rb b/qa/spec/support/stub_env.rb
new file mode 100644
index 00000000000..bc8f3a5e22e
--- /dev/null
+++ b/qa/spec/support/stub_env.rb
@@ -0,0 +1,38 @@
+# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
+module Support
+ module StubENV
+ def stub_env(key_or_hash, value = nil)
+ init_stub unless env_stubbed?
+
+ if key_or_hash.is_a? Hash
+ key_or_hash.each { |k, v| add_stubbed_value(k, v) }
+ else
+ add_stubbed_value key_or_hash, value
+ end
+ end
+
+ private
+
+ STUBBED_KEY = '__STUBBED__'.freeze
+
+ def add_stubbed_value(key, value)
+ allow(ENV).to receive(:[]).with(key).and_return(value)
+ allow(ENV).to receive(:key?).with(key).and_return(true)
+ allow(ENV).to receive(:fetch).with(key).and_return(value)
+ allow(ENV).to receive(:fetch).with(key, anything()) do |_, default_val|
+ value || default_val
+ end
+ end
+
+ def env_stubbed?
+ ENV[STUBBED_KEY]
+ end
+
+ def init_stub
+ allow(ENV).to receive(:[]).and_call_original
+ allow(ENV).to receive(:key?).and_call_original
+ allow(ENV).to receive(:fetch).and_call_original
+ add_stubbed_value(STUBBED_KEY, true)
+ end
+ end
+end
diff --git a/scripts/add-code-formatters b/scripts/add-code-formatters
deleted file mode 100755
index 56bb8754d80..00000000000
--- a/scripts/add-code-formatters
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-# Check if file exists with -f. Check if in in the gdk rook directory.
-if [ ! -f ../GDK_ROOT ]; then
- echo "Please run script from gitlab (e.g. gitlab-development-kit/gitlab) root directory."
- exit 1
-fi
-
-PRECOMMIT=$(git rev-parse --git-dir)/hooks/pre-commit
-
-# Check if symlink exists with -L. Check if script was already installed.
-if [ -L $PRECOMMIT ]; then
- echo "Pre-commit script already installed."
- exit 1
-fi
-
-ln -s ./pre-commit $PRECOMMIT
-echo "Pre-commit script installed successfully"
diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build
index 95d9fe0f176..b42ae2a2595 100755
--- a/scripts/gitaly-test-build
+++ b/scripts/gitaly-test-build
@@ -9,11 +9,21 @@ require 'fileutils'
# called 'bundle install' using a different Gemfile, as happens with
# gitlab-ce and gitaly.
-dir = 'tmp/tests/gitaly'
+tmp_tests_gitaly_dir = File.expand_path('../tmp/tests/gitaly', __dir__)
-abort 'gitaly build failed' unless system('make', chdir: dir)
+# Use the top-level bundle vendor folder so that we don't reinstall gems twice
+bundle_vendor_path = File.expand_path('../vendor', __dir__)
+
+env = {
+ # This ensure the `clean` config set in `scripts/prepare_build.sh` isn't taken into account
+ 'BUNDLE_IGNORE_CONFIG' => 'true',
+ 'BUNDLE_GEMFILE' => File.join(tmp_tests_gitaly_dir, 'ruby', 'Gemfile'),
+ 'BUNDLE_FLAGS' => "--jobs=4 --path=#{bundle_vendor_path} --retry=3"
+}
+
+abort 'gitaly build failed' unless system(env, 'make', chdir: tmp_tests_gitaly_dir)
# Make the 'gitaly' executable look newer than 'GITALY_SERVER_VERSION'.
# Without this a gitaly executable created in the setup-test-env job
# will look stale compared to GITALY_SERVER_VERSION.
-FileUtils.touch(File.join(dir, 'gitaly'), mtime: Time.now + (1 << 24))
+FileUtils.touch(File.join(tmp_tests_gitaly_dir, 'gitaly'), mtime: Time.now + (1 << 24))
diff --git a/scripts/lint-rugged b/scripts/lint-rugged
new file mode 100755
index 00000000000..3f8fcb558e3
--- /dev/null
+++ b/scripts/lint-rugged
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+
+ALLOWED = [
+ # Can be deleted (?) once rugged is no longer used in production. Doesn't make Rugged calls.
+ 'config/initializers/8_metrics.rb',
+
+ # Can be deleted once wiki's are fully (mandatory) migrated
+ 'config/initializers/gollum.rb',
+
+ # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/953
+ 'lib/gitlab/bare_repository_import/repository.rb',
+
+ # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/954
+ 'lib/tasks/gitlab/cleanup.rake',
+
+ # https://gitlab.com/gitlab-org/gitaly/issues/961
+ 'app/models/repository.rb',
+
+ # The only place where Rugged code is still allowed in production
+ 'lib/gitlab/git/'
+].freeze
+
+rugged_lines = IO.popen(%w[git grep -i -n rugged -- app config lib], &:read).lines
+rugged_lines = rugged_lines.reject { |l| l.start_with?(*ALLOWED) }
+rugged_lines = rugged_lines.reject do |line|
+ code, _comment = line.split('# ', 2)
+ code !~ /rugged/i
+end
+
+exit if rugged_lines.empty?
+
+puts "Using Rugged is only allowed in test and #{ALLOWED}\n\n"
+
+puts rugged_lines
+
+exit(false)
diff --git a/scripts/pre-commit b/scripts/pre-commit
deleted file mode 100644
index 48935e90a87..00000000000
--- a/scripts/pre-commit
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-# Check if file exists with -f. Check if in in the gdk rook directory.
-if [ ! -f ../GDK_ROOT ]; then
- echo "Please run pre-commit from gitlab (e.g. gitlab-development-kit/gitlab) root directory."
- exit 1
-fi
-
-jsfiles=$(git diff --cached --name-only --diff-filter=ACM "*.js" | tr '\n' ' ')
-[ -z "$jsfiles" ] && exit 0
-
-# Prettify all staged .js files
-echo "$jsfiles" | xargs ./node_modules/.bin/prettier --write
-
-# Add back the modified/prettified files to staging
-echo "$jsfiles" | xargs git add
-
-exit 0
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index ea406aadf39..206d62dbc78 100644
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -3,7 +3,7 @@
export SETUP_DB=${SETUP_DB:-true}
export CREATE_DB_USER=${CREATE_DB_USER:-$SETUP_DB}
export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true}
-export BUNDLE_INSTALL_FLAGS="--without production --jobs $(nproc) --path vendor --retry 3 --quiet"
+export BUNDLE_INSTALL_FLAGS="--without=production --jobs=$(nproc) --path=vendor --retry=3 --quiet"
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 9690b42c788..96d08287ded 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -13,7 +13,8 @@ tasks = [
%w[bundle exec rake gettext:lint],
%w[bundle exec rake lint:static_verification],
%w[scripts/lint-changelog-yaml],
- %w[scripts/lint-conflicts.sh]
+ %w[scripts/lint-conflicts.sh],
+ %w[scripts/lint-rugged]
]
failed_tasks = tasks.reduce({}) do |failures, task|
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index 2565622f8df..cc1b1e5039e 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -51,6 +51,13 @@ describe Admin::ApplicationSettingsController do
sign_in(admin)
end
+ it 'updates the password_authentication_enabled_for_git setting' do
+ put :update, application_setting: { password_authentication_enabled_for_git: "0" }
+
+ expect(response).to redirect_to(admin_application_settings_path)
+ expect(ApplicationSetting.current.password_authentication_enabled_for_git).to eq(false)
+ end
+
it 'updates the default_project_visibility for string value' do
put :update, application_setting: { default_project_visibility: "20" }
diff --git a/spec/controllers/admin/hooks_controller_spec.rb b/spec/controllers/admin/hooks_controller_spec.rb
index e6ba596117a..d2c1e634930 100644
--- a/spec/controllers/admin/hooks_controller_spec.rb
+++ b/spec/controllers/admin/hooks_controller_spec.rb
@@ -11,11 +11,13 @@ describe Admin::HooksController do
it 'sets all parameters' do
hook_params = {
enable_ssl_verification: true,
+ token: "TEST TOKEN",
+ url: "http://example.com",
+
push_events: true,
tag_push_events: true,
repository_update_events: true,
- token: "TEST TOKEN",
- url: "http://example.com"
+ merge_requests_events: true
}
post :create, hook: hook_params
diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb
index fb9d3efbac0..7f2eaf95165 100644
--- a/spec/controllers/dashboard/groups_controller_spec.rb
+++ b/spec/controllers/dashboard/groups_controller_spec.rb
@@ -20,4 +20,24 @@ describe Dashboard::GroupsController do
expect(assigns(:groups)).to contain_exactly(member_of_group)
end
+
+ context 'when rendering an expanded hierarchy with public groups you are not a member of', :nested_groups do
+ let!(:top_level_result) { create(:group, name: 'chef-top') }
+ let!(:top_level_a) { create(:group, name: 'top-a') }
+ let!(:sub_level_result_a) { create(:group, name: 'chef-sub-a', parent: top_level_a) }
+ let!(:other_group) { create(:group, name: 'other') }
+
+ before do
+ top_level_result.add_master(user)
+ top_level_a.add_master(user)
+ end
+
+ it 'renders only groups the user is a member of when searching hierarchy correctly' do
+ get :index, filter: 'chef', format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ all_groups = [top_level_result, top_level_a, sub_level_result_a]
+ expect(assigns(:groups)).to contain_exactly(*all_groups)
+ end
+ end
end
diff --git a/spec/controllers/groups/children_controller_spec.rb b/spec/controllers/groups/children_controller_spec.rb
index cb1b460fc0e..22d3076c269 100644
--- a/spec/controllers/groups/children_controller_spec.rb
+++ b/spec/controllers/groups/children_controller_spec.rb
@@ -160,6 +160,30 @@ describe Groups::ChildrenController do
expect(json_response).to eq([])
end
+ it 'succeeds if multiple pages contain matching subgroups' do
+ create(:group, parent: group, name: 'subgroup-filter-1')
+ create(:group, parent: group, name: 'subgroup-filter-2')
+
+ # Creating the group-to-nest first so it would be loaded into the
+ # relation first before it's parents, this is what would cause the
+ # crash in: https://gitlab.com/gitlab-org/gitlab-ce/issues/40785.
+ #
+ # If we create the parent groups first, those would be loaded into the
+ # collection first, and the pagination would cut off the actual search
+ # result. In this case the hierarchy can be rendered without crashing,
+ # it's just incomplete.
+ group_to_nest = create(:group, parent: group, name: 'subsubgroup-filter-3')
+ subgroup = create(:group, parent: group)
+ 3.times do |i|
+ subgroup = create(:group, parent: subgroup)
+ end
+ group_to_nest.update!(parent: subgroup)
+
+ get :index, group_id: group.to_param, filter: 'filter', per_page: 3, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
it 'includes pagination headers' do
2.times { |i| create(:group, :public, parent: public_subgroup, name: "filterme#{i}") }
diff --git a/spec/controllers/import/gitlab_projects_controller_spec.rb b/spec/controllers/import/gitlab_projects_controller_spec.rb
new file mode 100644
index 00000000000..8759d3c0b97
--- /dev/null
+++ b/spec/controllers/import/gitlab_projects_controller_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe Import::GitlabProjectsController do
+ set(:namespace) { create(:namespace) }
+ set(:user) { namespace.owner }
+ let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'POST create' do
+ context 'with an invalid path' do
+ it 'redirects with an error' do
+ post :create, namespace_id: namespace.id, path: '/test', file: file
+
+ expect(flash[:alert]).to start_with('Project could not be imported')
+ expect(response).to have_gitlab_http_status(302)
+ end
+
+ it 'redirects with an error when a relative path is used' do
+ post :create, namespace_id: namespace.id, path: '../test', file: file
+
+ expect(flash[:alert]).to start_with('Project could not be imported')
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'with a valid path' do
+ it 'redirects to the new project path' do
+ post :create, namespace_id: namespace.id, path: 'test', file: file
+
+ expect(flash[:notice]).to include('is being imported')
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
new file mode 100644
index 00000000000..c639ad32ec6
--- /dev/null
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe OmniauthCallbacksController do
+ include LoginHelpers
+
+ let(:user) { create(:omniauth_user, extern_uid: 'my-uid', provider: provider) }
+ let(:provider) { :github }
+
+ before do
+ mock_auth_hash(provider.to_s, 'my-uid', user.email)
+ stub_omniauth_provider(provider, context: request)
+ end
+
+ it 'allows sign in' do
+ post provider
+
+ expect(request.env['warden']).to be_authenticated
+ end
+
+ shared_context 'sign_up' do
+ let(:user) { double(email: 'new@example.com') }
+
+ before do
+ stub_omniauth_setting(block_auto_created_users: false)
+ end
+ end
+
+ context 'sign up' do
+ include_context 'sign_up'
+
+ it 'is allowed' do
+ post provider
+
+ expect(request.env['warden']).to be_authenticated
+ end
+ end
+
+ context 'when OAuth is disabled' do
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ settings = Gitlab::CurrentSettings.current_application_settings
+ settings.update(disabled_oauth_sign_in_sources: [provider.to_s])
+ end
+
+ it 'prevents login via POST' do
+ post provider
+
+ expect(request.env['warden']).not_to be_authenticated
+ end
+
+ it 'shows warning when attempting login' do
+ post provider
+
+ expect(response).to redirect_to new_user_session_path
+ expect(flash[:alert]).to eq('Signing in using GitHub has been disabled')
+ end
+
+ it 'allows linking the disabled provider' do
+ user.identities.destroy_all
+ sign_in(user)
+
+ expect { post provider }.to change { user.reload.identities.count }.by(1)
+ end
+
+ context 'sign up' do
+ include_context 'sign_up'
+
+ it 'is prevented' do
+ post provider
+
+ expect(request.env['warden']).not_to be_authenticated
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index 3bbe168f6d5..6a41c4d23ea 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::AvatarsController do
- let(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:project) { create(:project, :repository, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
let(:user) { create(:user) }
before do
@@ -10,6 +10,12 @@ describe Projects::AvatarsController do
controller.instance_variable_set(:@project, project)
end
+ it 'GET #show' do
+ get :show, namespace_id: project.namespace.id, project_id: project.id
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
it 'removes avatar from DB by calling destroy' do
delete :destroy, namespace_id: project.namespace.id, project_id: project.id
expect(project.avatar.present?).to be_falsey
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index 73fb90d73ec..55ed276f96b 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -41,15 +41,21 @@ describe Projects::CommitsController do
context "when the ref name ends in .atom" do
context "when the ref does not exist with the suffix" do
- it "renders as atom" do
+ before do
get(:show,
namespace_id: project.namespace,
project_id: project,
id: "master.atom")
+ end
+ it "renders as atom" do
expect(response).to be_success
expect(response.content_type).to eq('application/atom+xml')
end
+
+ it 'renders summary with type=html' do
+ expect(response.body).to include('<summary type="html">')
+ end
end
context "when the ref exists with the suffix" do
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index aba70c6d4c1..2d473d5bf52 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -18,4 +18,30 @@ describe Projects::HooksController do
)
end
end
+
+ describe '#create' do
+ it 'sets all parameters' do
+ hook_params = {
+ enable_ssl_verification: true,
+ token: "TEST TOKEN",
+ url: "http://example.com",
+
+ push_events: true,
+ tag_push_events: true,
+ merge_requests_events: true,
+ issues_events: true,
+ confidential_issues_events: true,
+ note_events: true,
+ job_events: true,
+ pipeline_events: true,
+ wiki_page_events: true
+ }
+
+ post :create, namespace_id: project.namespace, project_id: project, hook: hook_params
+
+ expect(response).to have_http_status(302)
+ expect(ProjectHook.all.size).to eq(1)
+ expect(ProjectHook.first).to have_attributes(hook_params)
+ end
+ end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 6b7db947216..4a2998b4ccd 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -301,6 +301,53 @@ describe Projects::IssuesController do
end
end
+ describe 'GET #realtime_changes' do
+ def go(id:)
+ get :realtime_changes,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: id
+ end
+
+ context 'when an issue was edited' do
+ before do
+ project.add_developer(user)
+
+ issue.update!(last_edited_by: user, last_edited_at: issue.created_at + 1.minute)
+
+ sign_in(user)
+ end
+
+ it 'returns last edited time' do
+ go(id: issue.iid)
+
+ data = JSON.parse(response.body)
+
+ expect(data).to include('updated_at')
+ expect(data['updated_at']).to eq(issue.last_edited_at.to_time.iso8601)
+ end
+ end
+
+ context 'when an issue was edited by a deleted user' do
+ let(:deleted_user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+
+ issue.update!(last_edited_by: deleted_user, last_edited_at: Time.now)
+
+ deleted_user.destroy
+ sign_in(user)
+ end
+
+ it 'returns 200' do
+ go(id: issue.iid)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+ end
+
describe 'Confidential Issues' do
let(:project) { create(:project_empty_repo, :public) }
let(:assignee) { create(:assignee) }
@@ -589,25 +636,6 @@ describe Projects::IssuesController do
project_id: project,
id: id
end
-
- context 'when an issue was edited by a deleted user' do
- let(:deleted_user) { create(:user) }
-
- before do
- project.add_developer(user)
-
- issue.update!(last_edited_by: deleted_user, last_edited_at: Time.now)
-
- deleted_user.destroy
- sign_in(user)
- end
-
- it 'returns 200' do
- go(id: issue.iid)
-
- expect(response).to have_gitlab_http_status(200)
- end
- end
end
describe 'GET #edit' do
diff --git a/spec/factories/deploy_keys_projects.rb b/spec/factories/deploy_keys_projects.rb
index 30a6d468ed3..4350652fb79 100644
--- a/spec/factories/deploy_keys_projects.rb
+++ b/spec/factories/deploy_keys_projects.rb
@@ -2,5 +2,9 @@ FactoryBot.define do
factory :deploy_keys_project do
deploy_key
project
+
+ trait :write_access do
+ can_push true
+ end
end
end
diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb
index 552b4b7e06e..f0c43f3d6f5 100644
--- a/spec/factories/keys.rb
+++ b/spec/factories/keys.rb
@@ -15,10 +15,6 @@ FactoryBot.define do
factory :another_deploy_key, class: 'DeployKey'
end
- factory :write_access_key, class: 'DeployKey' do
- can_push true
- end
-
factory :rsa_key_2048 do
key do
<<~KEY.delete("\n")
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index e020579f71e..51b42d1b43b 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -21,7 +21,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'All')
expect(page).to have_selector('.row-content-block', text: 'All jobs')
expect(page.all('.build-link').size).to eq(4)
- expect(page).to have_link 'Cancel all'
+ expect(page).to have_button 'Stop all jobs'
end
end
@@ -31,7 +31,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'All')
expect(page).to have_content 'No jobs to show'
- expect(page).not_to have_link 'Cancel all'
+ expect(page).not_to have_button 'Stop all jobs'
end
end
end
@@ -51,7 +51,7 @@ describe 'Admin Builds' do
expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).not_to have_content(build3.id)
expect(page.find('.build-link')).not_to have_content(build4.id)
- expect(page).to have_link 'Cancel all'
+ expect(page).to have_button 'Stop all jobs'
end
end
@@ -63,7 +63,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'Pending')
expect(page).to have_content 'No jobs to show'
- expect(page).not_to have_link 'Cancel all'
+ expect(page).not_to have_button 'Stop all jobs'
end
end
end
@@ -83,7 +83,7 @@ describe 'Admin Builds' do
expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).not_to have_content(build3.id)
expect(page.find('.build-link')).not_to have_content(build4.id)
- expect(page).to have_link 'Cancel all'
+ expect(page).to have_button 'Stop all jobs'
end
end
@@ -95,7 +95,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'Running')
expect(page).to have_content 'No jobs to show'
- expect(page).not_to have_link 'Cancel all'
+ expect(page).not_to have_button 'Stop all jobs'
end
end
end
@@ -113,7 +113,7 @@ describe 'Admin Builds' do
expect(page.find('.build-link')).not_to have_content(build1.id)
expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).to have_content(build3.id)
- expect(page).to have_link 'Cancel all'
+ expect(page).to have_button 'Stop all jobs'
end
end
@@ -125,7 +125,7 @@ describe 'Admin Builds' do
expect(page).to have_selector('.nav-links li.active', text: 'Finished')
expect(page).to have_content 'No jobs to show'
- expect(page).to have_link 'Cancel all'
+ expect(page).to have_button 'Stop all jobs'
end
end
end
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index 241c7cbc34e..cb96830cb7c 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -17,6 +17,16 @@ RSpec.describe 'admin deploy keys' do
end
end
+ it 'shows all the projects the deploy key has write access' do
+ write_key = create(:deploy_keys_project, :write_access, deploy_key: deploy_key)
+
+ visit admin_deploy_keys_path
+
+ page.within(find('.deploy-keys-list', match: :first)) do
+ expect(page).to have_content(write_key.project.full_name)
+ end
+ end
+
describe 'create a new deploy key' do
let(:new_ssh_key) { attributes_for(:key)[:key] }
@@ -28,14 +38,12 @@ RSpec.describe 'admin deploy keys' do
it 'creates a new deploy key' do
fill_in 'deploy_key_title', with: 'laptop'
fill_in 'deploy_key_key', with: new_ssh_key
- check 'deploy_key_can_push'
click_button 'Create'
expect(current_path).to eq admin_deploy_keys_path
page.within(find('.deploy-keys-list', match: :first)) do
expect(page).to have_content('laptop')
- expect(page).to have_content('Yes')
end
end
end
@@ -48,14 +56,12 @@ RSpec.describe 'admin deploy keys' do
it 'updates an existing deploy key' do
fill_in 'deploy_key_title', with: 'new-title'
- check 'deploy_key_can_push'
click_button 'Save changes'
expect(current_path).to eq admin_deploy_keys_path
page.within(find('.deploy-keys-list', match: :first)) do
expect(page).to have_content('new-title')
- expect(page).to have_content('Yes')
end
end
end
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index eec44549a03..f266f2ecc54 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -1,11 +1,10 @@
require 'spec_helper'
-describe 'Admin::Hooks', :js do
- before do
- @project = create(:project)
- sign_in(create(:admin))
+describe 'Admin::Hooks' do
+ let(:user) { create(:admin) }
- @system_hook = create(:system_hook)
+ before do
+ sign_in(user)
end
describe 'GET /admin/hooks' do
@@ -13,15 +12,17 @@ describe 'Admin::Hooks', :js do
visit admin_root_path
page.within '.nav-sidebar' do
- click_on 'Hooks'
+ click_on 'System Hooks', match: :first
end
expect(current_path).to eq(admin_hooks_path)
end
it 'has hooks list' do
+ system_hook = create(:system_hook)
+
visit admin_hooks_path
- expect(page).to have_content(@system_hook.url)
+ expect(page).to have_content(system_hook.url)
end
end
@@ -43,6 +44,10 @@ describe 'Admin::Hooks', :js do
describe 'Update existing hook' do
let(:new_url) { generate(:url) }
+ before do
+ create(:system_hook)
+ end
+
it 'updates existing hook' do
visit admin_hooks_path
@@ -57,7 +62,11 @@ describe 'Admin::Hooks', :js do
end
end
- describe 'Remove existing hook' do
+ describe 'Remove existing hook', :js do
+ before do
+ create(:system_hook)
+ end
+
context 'removes existing hook' do
it 'from hooks list page' do
visit admin_hooks_path
@@ -76,7 +85,8 @@ describe 'Admin::Hooks', :js do
describe 'Test', :js do
before do
- WebMock.stub_request(:post, @system_hook.url)
+ system_hook = create(:system_hook)
+ WebMock.stub_request(:post, system_hook.url)
visit admin_hooks_path
find('.hook-test-button.dropdown').click
@@ -85,4 +95,41 @@ describe 'Admin::Hooks', :js do
it { expect(current_path).to eq(admin_hooks_path) }
end
+
+ context 'Merge request hook' do
+ describe 'New Hook' do
+ let(:url) { generate(:url) }
+
+ it 'adds new hook' do
+ visit admin_hooks_path
+
+ fill_in 'hook_url', with: url
+ uncheck 'Repository update events'
+ check 'Merge request events'
+
+ expect { click_button 'Add system hook' }.to change(SystemHook, :count).by(1)
+ expect(current_path).to eq(admin_hooks_path)
+ expect(page).to have_content(url)
+ end
+ end
+
+ describe 'Test', :js do
+ before do
+ system_hook = create(:system_hook)
+ WebMock.stub_request(:post, system_hook.url)
+ end
+
+ it 'succeeds if the user has a repository with a merge request' do
+ project = create(:project, :repository)
+ create(:project_member, user: user, project: project)
+ create(:merge_request, source_project: project)
+
+ visit admin_hooks_path
+ find('.hook-test-button.dropdown').click
+ click_link 'Merge requests events'
+
+ expect(page).to have_content 'Hook executed successfully'
+ end
+ end
+ end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 3876d1c76d7..3d13f806b11 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -69,6 +69,7 @@ describe 'Issue Boards', :js do
let!(:backlog) { create(:label, project: project, name: 'Backlog') }
let!(:closed) { create(:label, project: project, name: 'Closed') }
let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
+ let!(:a_plus) { create(:label, project: project, name: 'A+') }
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) }
@@ -83,6 +84,7 @@ describe 'Issue Boards', :js do
let!(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
let!(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
let!(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
+ let!(:issue10) { create(:labeled_issue, project: project, title: 'issue +', description: 'A+ great issue', labels: [a_plus]) }
before do
visit project_board_path(project, board)
@@ -400,6 +402,15 @@ describe 'Issue Boards', :js do
wait_for_empty_boards((3..4))
end
+ it 'filters by label with encoded character' do
+ set_filter("label", a_plus.title)
+ click_filter_link(a_plus.title)
+ submit_filter
+
+ wait_for_board_cards(1, 1)
+ wait_for_empty_boards((2..4))
+ end
+
it 'filters by label with space after reload' do
set_filter("label", "\"#{accepting.title}")
click_filter_link(accepting.title)
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index a28b8905b65..62a2ec55b00 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -194,7 +194,7 @@ describe 'Commits' do
end
it 'includes the committed_date for each commit' do
- commits = project.repository.commits(branch_name)
+ commits = project.repository.commits(branch_name, limit: 40)
commits.each do |commit|
expect(page).to have_content("authored #{commit.authored_date.strftime("%b %d, %Y")}")
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index d36954954b6..510677ecf56 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -113,6 +113,7 @@ feature 'Cycle Analytics', :js do
context "as a guest" do
before do
+ project.add_developer(user)
project.add_guest(guest)
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 4f575613848..f8c4db1403c 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -22,7 +22,7 @@ feature 'Global search' do
click_button "Go"
select_filter("Issues")
- expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(page).to have_selector('.gl-pagination .next')
end
end
end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index a5c9d0bde5d..64b4f9e7e67 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -8,6 +8,7 @@ feature 'Issue Sidebar' do
let(:issue) { create(:issue, project: project) }
let!(:user) { create(:user)}
let!(:label) { create(:label, project: project, title: 'bug') }
+ let!(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
before do
sign_in(user)
@@ -99,6 +100,14 @@ feature 'Issue Sidebar' do
restore_window_size
open_issue_sidebar
end
+
+ it 'escapes XSS when viewing issue labels' do
+ page.within('.block.labels') do
+ find('.edit-link').click
+
+ expect(page).to have_content '<script>alert("xss");</script>'
+ end
+ end
end
context 'editing issue labels', :js do
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 53706ef84bc..c7cfd01f588 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -34,6 +34,9 @@ describe 'New issue', :js do
click_button 'Submit issue'
+ # reCAPTCHA alerts when it can't contact the server, so just accept it and move on
+ page.driver.browser.switch_to.alert.accept
+
# it is impossible to test recaptcha automatically and there is no possibility to fill in recaptcha
# recaptcha verification is skipped in test environment and it always returns true
expect(page).not_to have_content('issue title')
diff --git a/spec/features/merge_request/user_assigns_themselves_spec.rb b/spec/features/merge_request/user_assigns_themselves_spec.rb
new file mode 100644
index 00000000000..b6b38186a22
--- /dev/null
+++ b/spec/features/merge_request/user_assigns_themselves_spec.rb
@@ -0,0 +1,49 @@
+require 'rails_helper'
+
+describe 'Merge request > User assigns themselves' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:issue1) { create(:issue, project: project) }
+ let(:issue2) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "fixes #{issue1.to_reference} and #{issue2.to_reference}") }
+
+ context 'logged in as a member of the project' do
+ before do
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'updates related issues', :js do
+ click_link 'Assign yourself to these issues'
+
+ expect(page).to have_content '2 issues have been assigned to you'
+ end
+
+ it 'returns user to the merge request', :js do
+ click_link 'Assign yourself to these issues'
+
+ expect(page).to have_content merge_request.description
+ end
+
+ context 'when related issues are already assigned' do
+ before do
+ [issue1, issue2].each { |issue| issue.update!(assignees: [user]) }
+ end
+
+ it 'does not display if related issues are already assigned' do
+ expect(page).not_to have_content 'Assign yourself'
+ end
+ end
+ end
+
+ context 'logged in as a non-member of the project' do
+ before do
+ sign_in(create(:user))
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'does not not show assignment link' do
+ expect(page).not_to have_content 'Assign yourself'
+ end
+ end
+end
diff --git a/spec/features/merge_requests/award_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb
index a24464f2556..15a0878fb16 100644
--- a/spec/features/merge_requests/award_spec.rb
+++ b/spec/features/merge_request/user_awards_emoji_spec.rb
@@ -1,8 +1,8 @@
require 'rails_helper'
-feature 'Merge request awards', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User awards emoji', :js do
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
describe 'logged in' do
diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_request/user_cherry_picks_spec.rb
index 205e38337d1..494096b21c0 100644
--- a/spec/features/merge_requests/cherry_pick_spec.rb
+++ b/spec/features/merge_request/user_cherry_picks_spec.rb
@@ -1,17 +1,17 @@
-require 'spec_helper'
+require 'rails_helper'
-describe 'Cherry-pick Merge Requests', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User cherry-picks', :js do
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
+ let(:user) { project.creator }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
before do
- sign_in user
project.add_master(user)
+ sign_in(user)
end
- context "Viewing a merged merge request" do
+ context 'Viewing a merged merge request' do
before do
service = MergeRequests::MergeService.new(project, user)
@@ -21,24 +21,24 @@ describe 'Cherry-pick Merge Requests', :js do
end
# Fast-forward merge, or merged before GitLab 8.5.
- context "Without a merge commit" do
+ context 'Without a merge commit' do
before do
merge_request.merge_commit_sha = nil
merge_request.save
end
- it "doesn't show a Cherry-pick button" do
+ it 'does not show a Cherry-pick button' do
visit project_merge_request_path(project, merge_request)
- expect(page).not_to have_link "Cherry-pick"
+ expect(page).not_to have_link 'Cherry-pick'
end
end
- context "With a merge commit" do
- it "shows a Cherry-pick button" do
+ context 'With a merge commit' do
+ it 'shows a Cherry-pick button' do
visit project_merge_request_path(project, merge_request)
- expect(page).to have_link "Cherry-pick"
+ expect(page).to have_link 'Cherry-pick'
end
end
end
diff --git a/spec/features/merge_requests/image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index d0f8da4e6cd..7c4fd25bb39 100644
--- a/spec/features/merge_requests/image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -1,14 +1,13 @@
require 'spec_helper'
-feature 'image diff notes', :js do
+feature 'Merge request > User creates image diff notes', :js do
include NoteInteractionHelpers
- let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
before do
- project.add_master(user)
- sign_in user
+ sign_in(user)
# Stub helper to return any blob file as image from public app folder.
# This is necessary to run this specs since we don't display repo images in capybara.
@@ -25,20 +24,14 @@ feature 'image diff notes', :js do
create_image_diff_note
end
- it 'shows indicator badge on image diff' do
+ it 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes' do
indicator = find('.js-image-badge')
-
- expect(indicator).to have_content('1')
- end
-
- it 'shows the avatar badge on the new note' do
badge = find('.image-diff-avatar-link .badge')
+ expect(indicator).to have_content('1')
expect(badge).to have_content('1')
- end
- it 'allows collapsing/expanding the discussion notes' do
- find('.js-diff-notes-toggle', :first).click
+ find('.js-diff-notes-toggle').click
expect(page).not_to have_content('image diff test comment')
@@ -86,15 +79,9 @@ feature 'image diff notes', :js do
wait_for_requests
end
- it 'render diff indicators within the image diff frame' do
+ it 'render diff indicators within the image diff frame, diff notes, and avatar badge numbers' do
expect(page).to have_css('.js-image-badge', count: 2)
- end
-
- it 'shows the diff notes' do
expect(page).to have_css('.diff-content .note', count: 2)
- end
-
- it 'shows the diff notes with correct avatar badge numbers' do
expect(page).to have_css('.image-diff-avatar-link', text: 1)
expect(page).to have_css('.image-diff-avatar-link', text: 2)
end
@@ -127,19 +114,13 @@ feature 'image diff notes', :js do
create_image_diff_note
end
- it 'shows indicator badge on image diff' do
+ it 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes' do
indicator = find('.js-image-badge', match: :first)
-
- expect(indicator).to have_content('1')
- end
-
- it 'shows the avatar badge on the new note' do
badge = find('.image-diff-avatar-link .badge', match: :first)
+ expect(indicator).to have_content('1')
expect(badge).to have_content('1')
- end
- it 'allows expanding/collapsing the discussion notes' do
page.all('.js-diff-notes-toggle')[0].click
page.all('.js-diff-notes-toggle')[1].click
@@ -154,7 +135,7 @@ feature 'image diff notes', :js do
end
end
- describe 'discussion tab polling', :js do
+ describe 'discussion tab polling' do
let(:merge_request) { create(:merge_request_with_diffs, :with_image_diffs, source_project: project, author: user) }
let(:path) { "files/images/ee_repo_logo.png" }
diff --git a/spec/features/merge_request/user_creates_mr_spec.rb b/spec/features/merge_request/user_creates_mr_spec.rb
new file mode 100644
index 00000000000..1ac31de62cb
--- /dev/null
+++ b/spec/features/merge_request/user_creates_mr_spec.rb
@@ -0,0 +1,31 @@
+require 'rails_helper'
+
+describe 'Merge request > User creates MR' do
+ it_behaves_like 'a creatable merge request'
+
+ context 'from a forked project' do
+ include ProjectForksHelper
+
+ let(:canonical_project) { create(:project, :public, :repository) }
+
+ let(:source_project) do
+ fork_project(canonical_project, user,
+ repository: true,
+ namespace: user.namespace)
+ end
+
+ context 'to canonical project' do
+ it_behaves_like 'a creatable merge request'
+ end
+
+ context 'to another forked project' do
+ let(:target_project) do
+ fork_project(canonical_project, user,
+ repository: true,
+ namespace: user.namespace)
+ end
+
+ it_behaves_like 'a creatable merge request'
+ end
+ end
+end
diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
index ddd034e1376..e1e70b6d260 100644
--- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
+++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
@@ -1,8 +1,8 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Clicking toggle commit message link', :js do
- let(:user) { create(:user) }
+describe 'Merge request < User customizes merge commit message', :js do
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
let(:issue_1) { create(:issue, project: project)}
let(:issue_2) { create(:issue, project: project)}
let(:merge_request) do
@@ -33,17 +33,14 @@ feature 'Clicking toggle commit message link', :js do
before do
project.add_master(user)
-
- sign_in user
-
+ sign_in(user)
visit project_merge_request_path(project, merge_request)
+ end
+ it 'toggles commit message between message with description and without description' do
expect(page).not_to have_selector('.js-commit-message')
click_button "Modify commit message"
expect(textbox).to be_visible
- end
-
- it "toggles commit message between message with description and without description " do
expect(textbox.value).to eq(default_message)
click_link "Include description in commit message"
diff --git a/spec/features/merge_request/user_edits_mr_spec.rb b/spec/features/merge_request/user_edits_mr_spec.rb
new file mode 100644
index 00000000000..8c9e782aa76
--- /dev/null
+++ b/spec/features/merge_request/user_edits_mr_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+describe 'Merge request > User edits MR' do
+ it_behaves_like 'an editable merge request'
+
+ context 'for a forked project' do
+ it_behaves_like 'an editable merge request' do
+ let(:source_project) { create(:project, :repository, forked_from_project: target_project) }
+ end
+ end
+end
diff --git a/spec/features/merge_requests/discussion_lock_spec.rb b/spec/features/merge_request/user_locks_discussion_spec.rb
index 7bbd3b1e69e..a68df872334 100644
--- a/spec/features/merge_requests/discussion_lock_spec.rb
+++ b/spec/features/merge_request/user_locks_discussion_spec.rb
@@ -1,9 +1,9 @@
-require 'spec_helper'
+require 'rails_helper'
-describe 'Discussion Lock', :js do
+describe 'Merge request > User locks discussion', :js do
let(:user) { create(:user) }
- let(:merge_request) { create(:merge_request, source_project: project, author: user) }
let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
before do
sign_in(user)
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb
index e1317b33ad1..b16fc9bfc89 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_request/user_merges_immediately_spec.rb
@@ -1,9 +1,8 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge immediately', :js do
- let(:user) { create(:user) }
+describe 'Merge requests > User merges immediately', :js do
let(:project) { create(:project, :public, :repository) }
-
+ let(:user) { project.creator }
let!(:merge_request) do
create(:merge_request_with_diffs, source_project: project,
author: user,
@@ -11,25 +10,18 @@ feature 'Merge immediately', :js do
head_pipeline: pipeline,
source_branch: pipeline.ref)
end
-
let(:pipeline) do
create(:ci_pipeline, project: project,
ref: 'master',
sha: project.repository.commit('master').id)
end
- before do
- project.add_master(user)
- end
-
context 'when there is active pipeline for merge request' do
- background do
- create(:ci_build, pipeline: pipeline)
- end
-
before do
- sign_in user
- visit project_merge_request_path(merge_request.project, merge_request)
+ create(:ci_build, pipeline: pipeline)
+ project.add_master(user)
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
end
it 'enables merge immediately' do
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index 7d9282b932b..a045791f6b4 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -1,18 +1,17 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Only allow merge requests to be merged if the pipeline succeeds', :js do
+describe 'Merge request > User merges only if pipeline succeeds', :js do
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
before do
- sign_in merge_request.author
-
project.add_master(merge_request.author)
+ sign_in(merge_request.author)
end
- context 'project does not have CI enabled', :js do
+ context 'project does not have CI enabled' do
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -20,8 +19,8 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
end
- context 'when project has CI enabled', :js do
- given!(:pipeline) do
+ context 'when project has CI enabled' do
+ let!(:pipeline) do
create(:ci_empty_pipeline,
project: project,
sha: merge_request.diff_head_sha,
@@ -35,10 +34,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI is running' do
- given(:status) { :running }
+ let(:status) { :running }
it 'does not allow to merge immediately' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -48,10 +47,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI failed' do
- given(:status) { :failed }
+ let(:status) { :failed }
it 'does not allow MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -61,10 +60,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI canceled' do
- given(:status) { :canceled }
+ let(:status) { :canceled }
it 'does not allow MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -74,10 +73,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI succeeded' do
- given(:status) { :success }
+ let(:status) { :success }
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -86,10 +85,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI skipped' do
- given(:status) { :skipped }
+ let(:status) { :skipped }
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -104,10 +103,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI is running' do
- given(:status) { :running }
+ let(:status) { :running }
it 'allows MR to be merged immediately' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -119,10 +118,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI failed' do
- given(:status) { :failed }
+ let(:status) { :failed }
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -131,10 +130,10 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
context 'when CI succeeded' do
- given(:status) { :success }
+ let(:status) { :success }
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -143,8 +142,4 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
end
end
end
-
- def visit_merge_request(merge_request)
- visit project_merge_request_path(merge_request.project, merge_request)
- end
end
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index ac46cc1f0e4..890774922aa 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -1,16 +1,14 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge When Pipeline Succeeds', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User merges when pipeline succeeds', :js do
let(:project) { create(:project, :public, :repository) }
-
+ let(:user) { project.creator }
let(:merge_request) do
create(:merge_request_with_diffs, source_project: project,
author: user,
title: 'Bug NS-04',
merge_params: { force_remove_source_branch: '1' })
end
-
let(:pipeline) do
create(:ci_pipeline, project: project,
sha: merge_request.diff_head_sha,
@@ -23,17 +21,10 @@ feature 'Merge When Pipeline Succeeds', :js do
end
context 'when there is active pipeline for merge request' do
- background do
- create(:ci_build, pipeline: pipeline)
- end
-
before do
- sign_in user
- visit_merge_request(merge_request)
- end
-
- it 'displays the Merge when pipeline succeeds button' do
- expect(page).to have_button "Merge when pipeline succeeds"
+ create(:ci_build, pipeline: pipeline)
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
end
describe 'enabling Merge when pipeline succeeds' do
@@ -44,7 +35,7 @@ feature 'Merge When Pipeline Succeeds', :js do
expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds"
expect(page).to have_content "The source branch will not be removed"
expect(page).to have_selector ".js-cancel-auto-merge"
- visit_merge_request(merge_request) # Needed to refresh the page
+ visit project_merge_request_path(project, merge_request) # Needed to refresh the page
expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i
end
end
@@ -115,14 +106,13 @@ feature 'Merge When Pipeline Succeeds', :js do
title: 'MepMep',
merge_when_pipeline_succeeds: true)
end
-
let!(:build) do
create(:ci_build, pipeline: pipeline)
end
before do
sign_in user
- visit_merge_request(merge_request)
+ visit project_merge_request_path(project, merge_request)
end
it 'allows to cancel the automatic merge' do
@@ -130,31 +120,67 @@ feature 'Merge When Pipeline Succeeds', :js do
expect(page).to have_button "Merge when pipeline succeeds"
- visit_merge_request(merge_request) # refresh the page
+ refresh
+
expect(page).to have_content "canceled the automatic merge"
end
context 'when pipeline succeeds' do
- background { build.success }
+ before do
+ build.success
+ refresh
+ end
it 'merges merge request' do
- visit_merge_request(merge_request) # refresh the page
-
expect(page).to have_content 'The changes were merged'
expect(merge_request.reload).to be_merged
end
end
+
+ context 'view merge request with MWPS enabled but automatically merge fails' do
+ before do
+ merge_request.update(
+ merge_user: merge_request.author,
+ merge_error: 'Something went wrong'
+ )
+ refresh
+ end
+
+ it 'shows information about the merge error' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_requests
+
+ page.within('.mr-widget-body') do
+ expect(page).to have_content('Something went wrong')
+ end
+ end
+ end
+
+ context 'view merge request with MWPS enabled but automatically merge fails' do
+ before do
+ merge_request.update(
+ merge_user: merge_request.author,
+ merge_error: 'Something went wrong'
+ )
+ refresh
+ end
+
+ it 'shows information about the merge error' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_requests
+
+ page.within('.mr-widget-body') do
+ expect(page).to have_content('Something went wrong')
+ end
+ end
+ end
end
context 'when pipeline is not active' do
- it "does not allow to enable merge when pipeline succeeds" do
- visit_merge_request(merge_request)
+ it 'does not allow to enable merge when pipeline succeeds' do
+ visit project_merge_request_path(project, merge_request)
expect(page).not_to have_link 'Merge when pipeline succeeds'
end
end
-
- def visit_merge_request(merge_request)
- visit project_merge_request_path(merge_request.project, merge_request)
- end
end
diff --git a/spec/features/merge_requests/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index d44eb23d7f4..2b4623d6dc9 100644
--- a/spec/features/merge_requests/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -1,11 +1,15 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge requests > User posts diff notes', :js do
+describe 'Merge request > User posts diff notes', :js do
include MergeRequestDiffHelpers
- let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.source_project }
+ let(:user) { project.creator }
+ let(:comment_button_class) { '.add-diff-note' }
+ let(:notes_holder_input_class) { 'js-temp-notes-holder' }
+ let(:notes_holder_input_xpath) { './following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
+ let(:test_note_comment) { 'this is a test note!' }
before do
set_cookie('sidebar_collapsed', 'true')
@@ -14,11 +18,6 @@ feature 'Merge requests > User posts diff notes', :js do
sign_in(user)
end
- let(:comment_button_class) { '.add-diff-note' }
- let(:notes_holder_input_class) { 'js-temp-notes-holder' }
- let(:notes_holder_input_xpath) { './following-sibling::*[contains(concat(" ", @class, " "), " notes_holder ")]' }
- let(:test_note_comment) { 'this is a test note!' }
-
context 'when hovering over a parallel view diff file' do
before do
visit diffs_project_merge_request_path(project, merge_request, view: 'parallel')
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index e17e9c2ccf5..50d06565fc0 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -1,9 +1,10 @@
-require 'spec_helper'
+require 'rails_helper'
-describe 'Merge requests > User posts notes', :js do
+describe 'Merge request > User posts notes', :js do
include NoteInteractionHelpers
let(:project) { create(:project, :repository) }
+ let(:user) { project.creator }
let(:merge_request) do
create(:merge_request, source_project: project, target_project: project)
end
@@ -13,7 +14,8 @@ describe 'Merge requests > User posts notes', :js do
end
before do
- sign_in(create(:admin))
+ project.add_master(user)
+ sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb
index 05d99a2dff2..61861d33952 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb
@@ -1,8 +1,8 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge request conflict resolution', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User resolves conflicts', :js do
let(:project) { create(:project, :repository) }
+ let(:user) { project.creator }
before do
# In order to have the diffs collapsed, we need to disable the increase feature
@@ -177,7 +177,6 @@ feature 'Merge request conflict resolution', :js do
before do
project.add_developer(user)
sign_in(user)
-
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 9d4194d8ca0..3e83a549682 100644
--- a/spec/features/merge_requests/diff_notes_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -1,10 +1,11 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Diff notes resolve', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User resolves diff notes and discussions', :js do
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:guest) { create(:user) }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") }
- let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) }
+ let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) }
let(:path) { "files/ruby/popen.rb" }
let(:position) do
Gitlab::Diff::Position.new(
@@ -19,7 +20,7 @@ feature 'Diff notes resolve', :js do
context 'no discussions' do
before do
project.add_master(user)
- sign_in user
+ sign_in(user)
note.destroy
visit_merge_request
end
@@ -33,7 +34,7 @@ feature 'Diff notes resolve', :js do
context 'as authorized user' do
before do
project.add_master(user)
- sign_in user
+ sign_in(user)
visit_merge_request
end
@@ -67,6 +68,8 @@ feature 'Diff notes resolve', :js do
click_button 'Resolve discussion'
end
+ expect(page).to have_selector('.discussion-body', visible: false)
+
page.within '.diff-content .note' do
expect(page).to have_selector('.line-resolve-btn.is-active')
end
@@ -105,7 +108,7 @@ feature 'Diff notes resolve', :js do
it 'shows resolved discussion when toggled' do
find(".timeline-content .discussion[data-discussion-id='#{note.discussion_id}'] .discussion-toggle-button").click
- expect(page.find(".timeline-content #note_#{note.noteable_id}")).to be_visible
+ expect(page.find(".timeline-content #note_#{note.id}")).to be_visible
end
end
@@ -318,9 +321,7 @@ feature 'Diff notes resolve', :js do
end
it 'shows jump to next discussion button' do
- page.all('.discussion-reply-holder').each do |holder|
- expect(holder).to have_selector('.discussion-next-btn')
- end
+ expect(page.all('.discussion-reply-holder')).to all(have_selector('.discussion-next-btn'))
end
it 'displays next discussion even if hidden' do
@@ -426,11 +427,9 @@ feature 'Diff notes resolve', :js do
end
context 'as a guest' do
- let(:guest) { create(:user) }
-
before do
project.add_guest(guest)
- sign_in guest
+ sign_in(guest)
end
context 'someone elses merge request' do
@@ -456,10 +455,10 @@ feature 'Diff notes resolve', :js do
end
context 'guest users merge request' do
+ let(:user) { guest }
+
before do
- mr = create(:merge_request_with_diffs, source_project: project, source_branch: 'markdown', author: guest, title: "Bug")
- create(:diff_note_on_merge_request, project: project, noteable: mr)
- visit_merge_request(mr)
+ visit_merge_request
end
it 'allows user to mark a note as resolved' do
@@ -521,7 +520,7 @@ feature 'Diff notes resolve', :js do
end
def visit_merge_request(mr = nil)
- mr = mr || merge_request
+ mr ||= merge_request
visit project_merge_request_path(mr.project, mr)
end
end
diff --git a/spec/features/merge_requests/resolve_outdated_diff_discussions.rb b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
index 25abbb469ab..9ba9e8b9585 100644
--- a/spec/features/merge_requests/resolve_outdated_diff_discussions.rb
+++ b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Resolve outdated diff discussions', :js do
+feature 'Merge request > User resolves outdated diff discussions', :js do
let(:project) { create(:project, :repository, :public) }
let(:merge_request) do
diff --git a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
new file mode 100644
index 00000000000..8a834adbf17
--- /dev/null
+++ b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
@@ -0,0 +1,26 @@
+require 'rails_helper'
+
+describe 'Merge request > User scrolls to note on load', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, source_project: project, author: user) }
+ let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
+ let(:fragment_id) { "#note_#{note.id}" }
+
+ before do
+ sign_in(user)
+ page.current_window.resize_to(1000, 300)
+ visit "#{project_merge_request_path(project, merge_request)}#{fragment_id}"
+ end
+
+ it 'scrolls down to fragment' do
+ page_height = page.current_window.size[1]
+ page_scroll_y = page.evaluate_script("window.scrollY")
+ fragment_position_top = page.evaluate_script("Math.round($('#{fragment_id}').offset().top)")
+
+ expect(find('.js-toggle-content').visible?).to eq true
+ expect(find(fragment_id).visible?).to eq true
+ expect(fragment_position_top).to be >= page_scroll_y
+ expect(fragment_position_top).to be < (page_scroll_y + page_height)
+ end
+end
diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index ef8f314cc03..9c0a04405a6 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -1,10 +1,10 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Diff note avatars', :js do
+describe 'Merge request > User sees avatars on diff notes', :js do
include NoteInteractionHelpers
- let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") }
let(:path) { "files/ruby/popen.rb" }
let(:position) do
@@ -151,7 +151,6 @@ feature 'Diff note avatars', :js do
page.within '.js-discussion-note-form' do
find('.js-note-text').native.send_keys('Test')
-
find('.js-comment-button').click
wait_for_requests
@@ -169,7 +168,6 @@ feature 'Diff note avatars', :js do
context 'multiple comments' do
before do
create_list(:diff_note_on_merge_request, 3, project: project, noteable: merge_request, in_reply_to: note)
-
visit diffs_project_merge_request_path(project, merge_request, view: view)
wait_for_requests
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
index 55de9a01ed5..726f35557a7 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
@@ -1,8 +1,8 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge Request closing issues message', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User sees closing issues message', :js do
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
let(:issue_1) { create(:issue, project: project)}
let(:issue_2) { create(:issue, project: project)}
let(:merge_request) do
@@ -19,9 +19,7 @@ feature 'Merge Request closing issues message', :js do
before do
project.add_master(user)
-
- sign_in user
-
+ sign_in(user)
visit project_merge_request_path(project, merge_request)
wait_for_requests
end
diff --git a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
new file mode 100644
index 00000000000..01115318370
--- /dev/null
+++ b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
@@ -0,0 +1,22 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees deleted target branch', :js do
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:user) { project.creator }
+
+ before do
+ project.add_master(user)
+ DeleteBranchService.new(project, user).execute('feature')
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows a message about missing target branch' do
+ expect(page).to have_content('Target branch does not exist')
+ end
+
+ it 'does not show link to target branch' do
+ expect(page).not_to have_selector('.mr-widget-body .js-branch-text a')
+ end
+end
diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
new file mode 100644
index 00000000000..3abe363d523
--- /dev/null
+++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
@@ -0,0 +1,56 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees deployment widget', :js do
+ describe 'when deployed to an environment' do
+ let(:user) { create(:user) }
+ let(:project) { merge_request.target_project }
+ let(:merge_request) { create(:merge_request, :merged) }
+ let(:environment) { create(:environment, project: project) }
+ let(:role) { :developer }
+ let(:sha) { project.commit('master').id }
+ let!(:deployment) { create(:deployment, environment: environment, sha: sha) }
+ let!(:manual) { }
+
+ before do
+ project.add_user(user, role)
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+ end
+
+ it 'displays that the environment is deployed' do
+ wait_for_requests
+
+ expect(page).to have_content("Deployed to #{environment.name}")
+ expect(find('.js-deploy-time')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
+ end
+
+ context 'with stop action' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
+ let(:deployment) do
+ create(:deployment, environment: environment, ref: merge_request.target_branch,
+ sha: sha, deployable: build, on_stop: 'close_app')
+ end
+
+ before do
+ wait_for_requests
+ end
+
+ it 'does start build when stop button clicked' do
+ accept_confirm { click_button('Stop environment') }
+
+ expect(page).to have_content('close_app')
+ end
+
+ context 'for reporter' do
+ let(:role) { :reporter }
+
+ it 'does not show stop button' do
+ expect(page).not_to have_button('Stop environment')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 1bf77296ae6..a9063f2bcb3 100644
--- a/spec/features/merge_requests/diffs_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -1,6 +1,6 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Diffs URL', :js do
+describe 'Merge request > User sees diff', :js do
include ProjectForksHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb
index 05789bbd31d..d6e8c8e86ba 100644
--- a/spec/features/merge_requests/discussion_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_spec.rb
@@ -1,19 +1,20 @@
-require 'spec_helper'
+require 'rails_helper'
+
+describe 'Merge request > User sees discussions' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, source_project: project) }
-feature 'Merge Request Discussions' do
before do
- sign_in(create(:admin))
+ project.add_master(user)
+ sign_in(user)
end
describe "Diff discussions" do
- let(:merge_request) { create(:merge_request, importing: true) }
- let(:project) { merge_request.source_project }
let!(:old_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: outdated_diff_refs) }
let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create }
-
let!(:outdated_discussion) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: outdated_position).to_discussion }
let!(:active_discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
-
let(:outdated_position) do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
@@ -23,7 +24,6 @@ feature 'Merge Request Discussions' do
diff_refs: outdated_diff_refs
)
end
-
let(:outdated_diff_refs) { project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e").diff_refs }
before do
@@ -50,9 +50,6 @@ feature 'Merge Request Discussions' do
end
describe 'Commit comments displayed in MR context', :js do
- let(:merge_request) { create(:merge_request) }
- let(:project) { merge_request.project }
-
shared_examples 'a functional discussion' do
let(:discussion_id) { note.discussion_id(merge_request) }
diff --git a/spec/features/merge_request/user_sees_empty_state_spec.rb b/spec/features/merge_request/user_sees_empty_state_spec.rb
new file mode 100644
index 00000000000..a939c7e9001
--- /dev/null
+++ b/spec/features/merge_request/user_sees_empty_state_spec.rb
@@ -0,0 +1,30 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees empty state' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+ end
+
+ it 'shows an empty state and a "New merge request" button' do
+ visit project_merge_requests_path(project)
+
+ expect(page).to have_selector('.empty-state')
+ expect(page).to have_link 'New merge request', href: project_new_merge_request_path(project)
+ end
+
+ context 'if there are merge requests' do
+ before do
+ create(:merge_request, source_project: project)
+
+ visit project_merge_requests_path(project)
+ end
+
+ it 'does not show an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+end
diff --git a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
index 892c32c8806..85df43df38e 100644
--- a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
@@ -1,24 +1,23 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Check if mergeable with unresolved discussions', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User sees merge button depending on unresolved discussions', :js do
let(:project) { create(:project, :repository) }
+ let(:user) { project.creator }
let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) }
before do
- sign_in user
project.add_master(user)
+ sign_in(user)
end
context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do
before do
project.update_column(:only_allow_merge_if_all_discussions_are_resolved, true)
+ visit project_merge_request_path(project, merge_request)
end
context 'with unresolved discussions' do
it 'does not allow to merge' do
- visit_merge_request(merge_request)
-
expect(page).not_to have_button 'Merge'
expect(page).to have_content('There are unresolved discussions.')
end
@@ -27,11 +26,10 @@ feature 'Check if mergeable with unresolved discussions', :js do
context 'with all discussions resolved' do
before do
merge_request.discussions.each { |d| d.resolve!(user) }
+ visit project_merge_request_path(project, merge_request)
end
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
-
expect(page).to have_button 'Merge'
end
end
@@ -40,12 +38,11 @@ feature 'Check if mergeable with unresolved discussions', :js do
context 'when project.only_allow_merge_if_all_discussions_are_resolved == false' do
before do
project.update_column(:only_allow_merge_if_all_discussions_are_resolved, false)
+ visit project_merge_request_path(project, merge_request)
end
context 'with unresolved discussions' do
it 'does not allow to merge' do
- visit_merge_request(merge_request)
-
expect(page).to have_button 'Merge'
end
end
@@ -53,17 +50,12 @@ feature 'Check if mergeable with unresolved discussions', :js do
context 'with all discussions resolved' do
before do
merge_request.discussions.each { |d| d.resolve!(user) }
+ visit project_merge_request_path(project, merge_request)
end
it 'allows MR to be merged' do
- visit_merge_request(merge_request)
-
expect(page).to have_button 'Merge'
end
end
end
-
- def visit_merge_request(merge_request)
- visit project_merge_request_path(merge_request.project, merge_request)
- end
end
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 8970586a160..56224e505d9 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -1,9 +1,9 @@
require 'rails_helper'
-describe 'Merge request', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User sees merge widget', :js do
let(:project) { create(:project, :repository) }
let(:project_only_mwps) { create(:project, :repository, only_allow_merge_if_pipeline_succeeds: true) }
+ let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:merge_request_in_only_mwps_project) { create(:merge_request, source_project: project_only_mwps) }
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
index a7e7c0eeff6..a43ba05c64c 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
@@ -1,16 +1,14 @@
require 'rails_helper'
-feature 'Mini Pipeline Graph', :js do
- let(:user) { create(:user) }
+describe 'Merge request < User sees mini pipeline graph', :js do
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project, head_pipeline: pipeline) }
-
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running', sha: project.commit.id) }
let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', commands: 'test') }
before do
build.run
-
sign_in(user)
visit_merge_request
end
@@ -19,13 +17,13 @@ feature 'Mini Pipeline Graph', :js do
visit project_merge_request_path(project, merge_request, format: format, serializer: serializer)
end
- it 'should display a mini pipeline graph' do
+ it 'displays a mini pipeline graph' do
expect(page).to have_selector('.mr-widget-pipeline-graph')
end
context 'as json' do
- let(:artifacts_file1) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
- let(:artifacts_file2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') }
+ let(:artifacts_file1) { fixture_file_upload(Rails.root.join('spec/fixtures/banana_sample.gif'), 'image/gif') }
+ let(:artifacts_file2) { fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') }
before do
create(:ci_build, pipeline: pipeline, legacy_artifacts_file: artifacts_file1)
@@ -51,7 +49,7 @@ feature 'Mini Pipeline Graph', :js do
first('.mini-pipeline-graph-dropdown-toggle')
end
- it 'should expand when hovered' do
+ it 'expands when hovered' do
find('.mini-pipeline-graph-dropdown-toggle')
before_width = evaluate_script("$('.mini-pipeline-graph-dropdown-toggle:visible').outerWidth();")
@@ -63,13 +61,13 @@ feature 'Mini Pipeline Graph', :js do
expect(before_width).to be < after_width
end
- it 'should show dropdown caret when hovered' do
+ it 'shows dropdown caret when hovered' do
toggle.hover
expect(toggle).to have_selector('.fa-caret-down')
end
- it 'should show tooltip when hovered' do
+ it 'shows tooltip when hovered' do
toggle.hover
expect(page).to have_selector('.tooltip')
@@ -87,17 +85,17 @@ feature 'Mini Pipeline Graph', :js do
wait_for_requests
end
- it 'should open when toggle is clicked' do
+ it 'pens when toggle is clicked' do
expect(toggle.find(:xpath, '..')).to have_selector('.mini-pipeline-graph-dropdown-menu')
end
- it 'should close when toggle is clicked again' do
+ it 'closes when toggle is clicked again' do
toggle.click
expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu')
end
- it 'should close when clicking somewhere else' do
+ it 'closes when clicking somewhere else' do
find('body').click
expect(toggle.find(:xpath, '..')).not_to have_selector('.mini-pipeline-graph-dropdown-menu')
@@ -109,14 +107,14 @@ feature 'Mini Pipeline Graph', :js do
first('.mini-pipeline-graph-dropdown-item')
end
- it 'should visit the build page when clicked' do
+ it 'visits the build page when clicked' do
build_item.click
find('.build-page')
expect(current_path).to eql(project_job_path(project, build))
end
- it 'should show tooltip when hovered' do
+ it 'shows tooltip when hovered' do
build_item.hover
expect(page).to have_selector('.tooltip')
diff --git a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
new file mode 100644
index 00000000000..029b66b5e8e
--- /dev/null
+++ b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb
@@ -0,0 +1,24 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees MR from deleted forked project', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:fork_project) { create(:project, :public, :repository, forked_from_project: project) }
+ let!(:merge_request) do
+ create(:merge_request_with_diffs, source_project: fork_project,
+ target_project: project,
+ description: 'Test merge request')
+ end
+
+ before do
+ MergeRequests::MergeService.new(project, user).execute(merge_request)
+ fork_project.destroy!
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'user can access merge request' do
+ expect(page).to have_content 'Test merge request'
+ expect(page).to have_content "(removed):#{merge_request.source_branch}"
+ end
+end
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
index 56aa0b2ede2..c1608be402a 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
@@ -1,23 +1,21 @@
-require 'spec_helper'
+require 'rails_helper'
# This test serves as a regression test for a bug that caused an error
# message to be shown by JavaScript when the source branch was deleted.
-# Please do not remove "js: true".
-describe 'Deleted source branch', :js do
- let(:user) { create(:user) }
- let(:merge_request) { create(:merge_request) }
+# Please do not remove ":js".
+describe 'Merge request > User sees MR with deleted source branch', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:user) { project.creator }
before do
- sign_in user
- merge_request.project.add_master(user)
merge_request.update!(source_branch: 'this-branch-does-not-exist')
- visit project_merge_request_path(merge_request.project, merge_request)
+ sign_in(user)
+ visit project_merge_request_path(project, merge_request)
end
it 'shows a message about missing source branch' do
- expect(page).to have_content(
- 'Source branch does not exist.'
- )
+ expect(page).to have_content('Source branch does not exist.')
end
it 'still contains Discussion, Commits and Changes tabs' do
diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
new file mode 100644
index 00000000000..b4cda269852
--- /dev/null
+++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb
@@ -0,0 +1,35 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees notes from forked project', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:fork_project) { create(:project, :public, :repository, forked_from_project: project) }
+ let!(:merge_request) do
+ create(:merge_request_with_diffs, source_project: fork_project,
+ target_project: project,
+ description: 'Test merge request')
+ end
+
+ before do
+ create(:note_on_commit, note: 'A commit comment',
+ project: fork_project,
+ commit_id: merge_request.commit_shas.first)
+ sign_in(user)
+ end
+
+ it 'user can reply to the comment' do
+ visit project_merge_request_path(project, merge_request)
+
+ expect(page).to have_content('A commit comment')
+
+ page.within('.discussion-notes') do
+ find('.btn-text-field').click
+ find('#note_note').send_keys('A reply comment')
+ find('.comment-btn').click
+ end
+
+ wait_for_requests
+
+ expect(page).to have_content('A reply comment')
+ end
+end
diff --git a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
new file mode 100644
index 00000000000..d30dcefc6aa
--- /dev/null
+++ b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb
@@ -0,0 +1,34 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees pipelines from forked project', :js do
+ let(:target_project) { create(:project, :public, :repository) }
+ let(:user) { target_project.creator }
+ let(:fork_project) { create(:project, :repository, forked_from_project: target_project) }
+ let!(:merge_request) do
+ create(:merge_request_with_diffs, source_project: fork_project,
+ target_project: target_project,
+ description: 'Test merge request')
+ end
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: fork_project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch)
+ end
+
+ before do
+ create(:ci_build, pipeline: pipeline, name: 'rspec')
+ create(:ci_build, pipeline: pipeline, name: 'spinach')
+
+ sign_in(user)
+ visit project_merge_request_path(target_project, merge_request)
+ end
+
+ it 'user visits a pipelines page' do
+ page.within('.merge-request-tabs') { click_link 'Pipelines' }
+
+ page.within('.ci-table') do
+ expect(page).to have_content(pipeline.id)
+ end
+ end
+end
diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index 04e3f4bdcf1..a42c016392b 100644
--- a/spec/features/merge_requests/pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -1,14 +1,14 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Pipelines for Merge Requests', :js do
+describe 'Merge request > User sees pipelines', :js do
describe 'pipeline tab' do
- given(:user) { create(:user) }
- given(:merge_request) { create(:merge_request) }
- given(:project) { merge_request.target_project }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.target_project }
+ let(:user) { project.creator }
before do
project.add_master(user)
- sign_in user
+ sign_in(user)
end
context 'with pipelines' do
@@ -23,7 +23,7 @@ feature 'Pipelines for Merge Requests', :js do
merge_request.update_attribute(:head_pipeline_id, pipeline.id)
end
- scenario 'user visits merge request pipelines tab' do
+ it 'user visits merge request pipelines tab' do
visit project_merge_request_path(project, merge_request)
expect(page.find('.ci-widget')).to have_content('pending')
@@ -36,7 +36,7 @@ feature 'Pipelines for Merge Requests', :js do
expect(page).to have_selector('.stage-cell')
end
- scenario 'pipeline sha does not equal last commit sha' do
+ it 'pipeline sha does not equal last commit sha' do
pipeline.update_attribute(:sha, '19e2e9b4ef76b422ce1154af39a91323ccc57434')
visit project_merge_request_path(project, merge_request)
wait_for_requests
@@ -51,7 +51,7 @@ feature 'Pipelines for Merge Requests', :js do
visit project_merge_request_path(project, merge_request)
end
- scenario 'user visits merge request page' do
+ it 'user visits merge request page' do
page.within('.merge-request-tabs') do
expect(page).to have_no_link('Pipelines')
end
@@ -60,22 +60,22 @@ feature 'Pipelines for Merge Requests', :js do
end
describe 'race condition' do
- given(:project) { create(:project, :repository) }
- given(:user) { create(:user) }
- given(:build_push_data) { { ref: 'feature', checkout_sha: TestEnv::BRANCH_SHA['feature'] } }
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:build_push_data) { { ref: 'feature', checkout_sha: TestEnv::BRANCH_SHA['feature'] } }
- given(:merge_request_params) do
+ let(:merge_request_params) do
{ "source_branch" => "feature", "source_project_id" => project.id,
"target_branch" => "master", "target_project_id" => project.id, "title" => "A" }
end
- background do
+ before do
project.add_master(user)
sign_in user
end
context 'when pipeline and merge request were created simultaneously' do
- background do
+ before do
stub_ci_pipeline_to_return_yaml_file
threads = []
@@ -91,7 +91,7 @@ feature 'Pipelines for Merge Requests', :js do
threads.each { |thr| thr.join }
end
- scenario 'user sees pipeline in merge request widget' do
+ it 'user sees pipeline in merge request widget' do
visit project_merge_request_path(project, @merge_request)
expect(page.find(".ci-widget")).to have_content(TestEnv::BRANCH_SHA['feature'])
diff --git a/spec/features/merge_requests/user_sees_system_notes_spec.rb b/spec/features/merge_request/user_sees_system_notes_spec.rb
index 03dc61c2efa..a00a682757d 100644
--- a/spec/features/merge_requests/user_sees_system_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_system_notes_spec.rb
@@ -1,15 +1,15 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge requests > User sees system notes' do
+describe 'Merge request > User sees system notes' do
let(:public_project) { create(:project, :public, :repository) }
let(:private_project) { create(:project, :private, :repository) }
+ let(:user) { private_project.creator }
let(:issue) { create(:issue, project: private_project) }
let(:merge_request) { create(:merge_request, source_project: public_project, source_branch: 'markdown') }
let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: public_project, note: "mentioned in #{issue.to_reference(public_project)}") }
context 'when logged-in as a member of the private project' do
before do
- user = create(:user)
private_project.add_developer(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 482f2e51c8b..3a15d70979a 100644
--- a/spec/features/merge_requests/versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -1,15 +1,17 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Merge Request versions', :js do
+describe 'Merge request > User sees versions', :js do
let(:merge_request) { create(:merge_request, importing: true) }
let(:project) { merge_request.source_project }
+ let(:user) { project.creator }
let!(:merge_request_diff1) { merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
let!(:merge_request_diff2) { merge_request.merge_request_diffs.create(head_commit_sha: nil) }
let!(:merge_request_diff3) { merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
- let!(:params) { Hash.new }
+ let!(:params) { {} }
before do
- sign_in(create(:admin))
+ project.add_master(user)
+ sign_in(user)
visit diffs_project_merge_request_path(project, merge_request, params)
end
@@ -62,19 +64,10 @@ feature 'Merge Request versions', :js do
end
end
- it 'should show older version' do
- page.within '.mr-version-dropdown' do
- expect(page).to have_content 'version 1'
- end
-
+ it 'shows comments that were last relevant at that version' do
expect(page).to have_content '5 changed files'
- end
-
- it 'show the message about comments' do
expect(page).to have_content 'Not all comments are displayed'
- end
- it 'shows comments that were last relevant at that version' do
position = Gitlab::Diff::Position.new(
old_path: ".gitmodules",
new_path: ".gitmodules",
@@ -86,7 +79,7 @@ feature 'Merge Request versions', :js do
outdated_diff_note.position = outdated_diff_note.original_position
outdated_diff_note.save!
- visit current_url
+ refresh
expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
end
@@ -110,26 +103,16 @@ feature 'Merge Request versions', :js do
end
end
- it 'has a path with comparison context' do
+ it 'has a path with comparison context and shows comments that were last relevant at that version' do
expect(page).to have_current_path diffs_project_merge_request_path(
project,
merge_request.iid,
diff_id: merge_request_diff3.id,
start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
)
- end
-
- it 'should have correct value in the compare dropdown' do
- page.within '.mr-version-compare-dropdown' do
- expect(page).to have_content 'version 1'
- end
- end
-
- it 'show the message about comments' do
+ expect(page).to have_content '4 changed files with 15 additions and 6 deletions'
expect(page).to have_content 'Not all comments are displayed'
- end
- it 'shows comments that were last relevant at that version' do
position = Gitlab::Diff::Position.new(
old_path: ".gitmodules",
new_path: ".gitmodules",
@@ -141,7 +124,7 @@ feature 'Merge Request versions', :js do
outdated_diff_note.position = outdated_diff_note.original_position
outdated_diff_note.save!
- visit current_url
+ refresh
wait_for_requests
expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
@@ -151,7 +134,7 @@ feature 'Merge Request versions', :js do
expect(page).to have_content '4 changed files with 15 additions and 6 deletions'
end
- it 'should return to latest version when "Show latest version" button is clicked' do
+ it 'returns to latest version when "Show latest version" button is clicked' do
click_link 'Show latest version'
page.within '.mr-version-dropdown' do
expect(page).to have_content 'latest version'
@@ -173,7 +156,7 @@ feature 'Merge Request versions', :js do
end
end
- it 'should have 0 chages between versions' do
+ it 'has 0 chages between versions' do
page.within '.mr-version-compare-dropdown' do
expect(find('.dropdown-toggle')).to have_content 'version 1'
end
@@ -194,7 +177,7 @@ feature 'Merge Request versions', :js do
end
end
- it 'should set the compared versions to be the same' do
+ it 'sets the compared versions to be the same' do
page.within '.mr-version-compare-dropdown' do
expect(find('.dropdown-toggle')).to have_content 'version 2'
end
diff --git a/spec/features/merge_requests/wip_message_spec.rb b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
index 2617e735c25..bc25243244e 100644
--- a/spec/features/merge_requests/wip_message_spec.rb
+++ b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
@@ -1,8 +1,8 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Work In Progress help message' do
- let!(:project) { create(:project, :public, :repository) }
- let!(:user) { create(:user) }
+describe 'Merge request > User sees WIP help message' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
before do
project.add_master(user)
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index 486555ed5cd..fb73ab05f87 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -1,13 +1,12 @@
-require 'spec_helper'
+require 'rails_helper'
-feature 'Create New Merge Request', :js do
- let(:user) { create(:user) }
+describe 'Merge request > User selects branches for new MR', :js do
let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
before do
project.add_master(user)
-
- sign_in user
+ sign_in(user)
end
it 'selects the source branch sha when a tag with the same name exists' do
diff --git a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
index fa3d988b27a..2e95a628013 100644
--- a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
+++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
@@ -1,10 +1,13 @@
-require 'spec_helper'
+require 'rails_helper'
+
+describe 'Merge request > User toggles whitespace changes', :js do
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:user) { project.creator }
-feature 'Toggle Whitespace Changes', :js do
before do
- sign_in(create(:admin))
- merge_request = create(:merge_request)
- project = merge_request.source_project
+ project.add_master(user)
+ sign_in(user)
visit diffs_project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_request/user_uses_slash_commands_spec.rb
index 5874bf5e187..bd739e69d6c 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_request/user_uses_slash_commands_spec.rb
@@ -1,8 +1,14 @@
require 'rails_helper'
-feature 'Merge Requests > User uses quick actions', :js do
+describe 'Merge request > User uses quick actions', :js do
include QuickActionsHelpers
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:guest) { create(:user) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
+
it_behaves_like 'issuable record that supports quick actions in its description and notes', :merge_request do
let(:issuable) { create(:merge_request, source_project: project) }
let(:new_url_opts) { { merge_request: { source_branch: 'feature', target_branch: 'master' } } }
@@ -20,15 +26,7 @@ feature 'Merge Requests > User uses quick actions', :js do
visit project_merge_request_path(project, merge_request)
end
- after do
- wait_for_requests
- end
-
describe 'time tracking' do
- before do
- visit project_merge_request_path(project, merge_request)
- end
-
it_behaves_like 'issuable time tracker'
end
@@ -56,7 +54,6 @@ feature 'Merge Requests > User uses quick actions', :js do
end
context 'when the current user cannot toggle the WIP prefix' do
- let(:guest) { create(:user) }
before do
project.add_guest(guest)
sign_out(:user)
@@ -102,7 +99,6 @@ feature 'Merge Requests > User uses quick actions', :js do
end
context 'when the current user cannot merge the MR' do
- let(:guest) { create(:user) }
before do
project.add_guest(guest)
sign_out(:user)
@@ -186,7 +182,6 @@ feature 'Merge Requests > User uses quick actions', :js do
end
context 'when current user can not change target branch' do
- let(:guest) { create(:user) }
before do
project.add_guest(guest)
sign_out(:user)
diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb
deleted file mode 100644
index b2d64a62b4f..00000000000
--- a/spec/features/merge_requests/assign_issues_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'rails_helper'
-
-feature 'Merge request issue assignment', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public, :repository) }
- let(:issue1) { create(:issue, project: project) }
- let(:issue2) { create(:issue, project: project) }
- let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "fixes #{issue1.to_reference} and #{issue2.to_reference}") }
- let(:service) { MergeRequests::AssignIssuesService.new(merge_request, user, user, project) }
-
- before do
- project.add_developer(user)
- end
-
- def visit_merge_request(current_user = nil)
- sign_in(current_user || user)
- visit project_merge_request_path(project, merge_request)
- end
-
- context 'logged in as author' do
- it 'updates related issues' do
- visit_merge_request
- click_link "Assign yourself to these issues"
-
- expect(page).to have_content "2 issues have been assigned to you"
- end
-
- it 'returns user to the merge request' do
- visit_merge_request
- click_link "Assign yourself to these issues"
-
- expect(page).to have_content merge_request.description
- end
-
- it "doesn't display if related issues are already assigned" do
- [issue1, issue2].each { |issue| issue.update!(assignees: [user]) }
-
- visit_merge_request
-
- expect(page).not_to have_content "Assign yourself"
- end
- end
-
- context 'not MR author' do
- it "doesn't not show assignment link" do
- visit_merge_request(create(:user))
-
- expect(page).not_to have_content "Assign yourself"
- end
- end
-end
diff --git a/spec/features/merge_requests/create_new_mr_from_fork_spec.rb b/spec/features/merge_requests/create_new_mr_from_fork_spec.rb
deleted file mode 100644
index 93c40ff6443..00000000000
--- a/spec/features/merge_requests/create_new_mr_from_fork_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'spec_helper'
-
-feature 'Creating a merge request from a fork', :js do
- include ProjectForksHelper
-
- let(:user) { create(:user) }
- let(:project) { create(:project, :public, :repository) }
- let!(:source_project) do
- fork_project(project, user,
- repository: true,
- namespace: user.namespace)
- end
-
- before do
- source_project.add_master(user)
-
- sign_in(user)
- end
-
- shared_examples 'create merge request to other project' do
- it 'has all possible target projects' do
- visit project_new_merge_request_path(source_project)
-
- first('.js-target-project').click
-
- within('.dropdown-target-project .dropdown-content') do
- expect(page).to have_content(project.full_path)
- expect(page).to have_content(target_project.full_path)
- expect(page).to have_content(source_project.full_path)
- end
- end
-
- it 'allows creating the merge request to another target project' do
- visit project_merge_requests_path(source_project)
-
- page.within '.content' do
- click_link 'New merge request'
- end
-
- find('.js-source-branch', match: :first).click
- find('.dropdown-source-branch .dropdown-content a', match: :first).click
-
- first('.js-target-project').click
- find('.dropdown-target-project .dropdown-content a', text: target_project.full_path).click
-
- click_button 'Compare branches and continue'
-
- wait_for_requests
-
- expect { click_button 'Submit merge request' }
- .to change { target_project.merge_requests.reload.size }.by(1)
- end
-
- it 'updates the branches when selecting a new target project' do
- target_project_member = target_project.owner
- CreateBranchService.new(target_project, target_project_member)
- .execute('a-brand-new-branch-to-test', 'master')
- visit project_new_merge_request_path(source_project)
-
- first('.js-target-project').click
- find('.dropdown-target-project .dropdown-content a', text: target_project.full_path).click
-
- wait_for_requests
-
- first('.js-target-branch').click
-
- within('.dropdown-target-branch .dropdown-content') do
- expect(page).to have_content('a-brand-new-branch-to-test')
- end
- end
- end
-
- context 'creating to the source of a fork' do
- let!(:target_project) { project }
-
- it_behaves_like('create merge request to other project')
- end
-
- context 'creating to a sibling of a fork' do
- let!(:target_project) do
- other_user = create(:user)
- fork_project(project, other_user,
- repository: true,
- namespace: other_user.namespace)
- end
-
- it_behaves_like('create merge request to other project')
- end
-end
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
deleted file mode 100644
index 53b62caf743..00000000000
--- a/spec/features/merge_requests/created_from_fork_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-require 'spec_helper'
-
-feature 'Merge request created from fork' do
- include ProjectForksHelper
-
- given(:user) { create(:user) }
- given(:project) { create(:project, :public, :repository) }
- given(:forked_project) { fork_project(project, user, repository: true) }
-
- given!(:merge_request) do
- create(:merge_request_with_diffs, source_project: forked_project,
- target_project: project,
- description: 'Test merge request')
- end
-
- background do
- forked_project.add_master(user)
- sign_in user
- end
-
- scenario 'user can access merge request' do
- visit_merge_request(merge_request)
-
- expect(page).to have_content 'Test merge request'
- end
-
- context 'when a commit comment exists on the merge request' do
- given(:comment) { 'A commit comment' }
- given(:reply) { 'A reply comment' }
-
- background do
- create(:note_on_commit, note: comment,
- project: forked_project,
- commit_id: merge_request.commit_shas.first)
- end
-
- scenario 'user can reply to the comment', :js do
- visit_merge_request(merge_request)
-
- expect(page).to have_content(comment)
-
- page.within('.discussion-notes') do
- find('.btn-text-field').click
- find('#note_note').send_keys(reply)
- find('.comment-btn').click
- end
-
- wait_for_requests
-
- expect(page).to have_content(reply)
- end
- end
-
- context 'source project is deleted' do
- background do
- MergeRequests::MergeService.new(project, user).execute(merge_request)
- forked_project.destroy!
- end
-
- scenario 'user can access merge request', :js do
- visit_merge_request(merge_request)
-
- expect(page).to have_content 'Test merge request'
- expect(page).to have_content "(removed):#{merge_request.source_branch}"
- end
- end
-
- context 'pipeline present in source project' do
- given(:pipeline) do
- create(:ci_pipeline,
- project: forked_project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch)
- end
-
- background do
- create(:ci_build, pipeline: pipeline, name: 'rspec')
- create(:ci_build, pipeline: pipeline, name: 'spinach')
- end
-
- scenario 'user visits a pipelines page', :js do
- visit_merge_request(merge_request)
- page.within('.merge-request-tabs') { click_link 'Pipelines' }
-
- page.within('.ci-table') do
- expect(page).to have_content pipeline.id
- end
- end
- end
-
- def visit_merge_request(mr)
- visit project_merge_request_path(project, mr)
- end
-end
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
deleted file mode 100644
index 79be2fbf945..00000000000
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-require 'spec_helper'
-
-feature 'Edit Merge Request' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public, :repository) }
- let(:merge_request) { create(:merge_request, :simple, source_project: project) }
-
- before do
- project.add_master(user)
-
- sign_in user
-
- visit edit_project_merge_request_path(project, merge_request)
- end
-
- context 'editing a MR' do
- it 'has class js-quick-submit in form' do
- expect(page).to have_selector('.js-quick-submit')
- end
-
- it 'warns about version conflict' do
- merge_request.update(title: "New title")
-
- fill_in 'merge_request_title', with: 'bug 345'
- fill_in 'merge_request_description', with: 'bug description'
-
- click_button 'Save changes'
-
- expect(page).to have_content 'Someone edited the merge request the same time you did'
- end
-
- it 'allows to unselect "Remove source branch"', :js do
- merge_request.update(merge_params: { 'force_remove_source_branch' => '1' })
- expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
-
- visit edit_project_merge_request_path(project, merge_request)
- uncheck 'Remove source branch when merge request is accepted'
-
- click_button 'Save changes'
-
- expect(page).to have_unchecked_field 'remove-source-branch-input'
- expect(page).to have_content 'Remove source branch'
- end
-
- it 'should preserve description textarea height', :js do
- long_description = %q(
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ac ornare ligula, ut tempus arcu. Etiam ultricies accumsan dolor vitae faucibus. Donec at elit lacus. Mauris orci ante, aliquam quis lorem eget, convallis faucibus arcu. Aenean at pulvinar lacus. Ut viverra quam massa, molestie ornare tortor dignissim a. Suspendisse tristique pellentesque tellus, id lacinia metus elementum id. Nam tristique, arcu rhoncus faucibus viverra, lacus ipsum sagittis ligula, vitae convallis odio lacus a nibh. Ut tincidunt est purus, ac vestibulum augue maximus in. Suspendisse vel erat et mi ultricies semper. Pellentesque volutpat pellentesque consequat.
-
- Cras congue nec ligula tristique viverra. Curabitur fringilla fringilla fringilla. Donec rhoncus dignissim orci ut accumsan. Ut rutrum urna a rhoncus varius. Maecenas blandit, mauris nec accumsan gravida, augue nibh finibus magna, sed maximus turpis libero nec neque. Suspendisse at semper est. Nunc imperdiet dapibus dui, varius sollicitudin erat luctus non. Sed pellentesque ligula eget posuere facilisis. Donec dictum commodo volutpat. Donec egestas dui ac magna sollicitudin bibendum. Vivamus purus neque, ullamcorper ac feugiat et, tempus sit amet metus. Praesent quis viverra neque. Sed bibendum viverra est, eu aliquam mi ornare vitae. Proin et dapibus ipsum. Nunc tortor diam, malesuada nec interdum vel, placerat quis justo. Ut viverra at erat eu laoreet.
-
- Pellentesque commodo, diam sit amet dignissim condimentum, tortor justo pretium est, non venenatis metus eros ut nunc. Etiam ut neque eget sem dapibus aliquam. Curabitur vel elit lorem. Nulla nec enim elit. Sed ut ex id justo facilisis convallis at ac augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nullam cursus egestas turpis non tristique. Suspendisse in erat sem. Fusce libero elit, fermentum gravida mauris id, auctor iaculis felis. Nullam vulputate tempor laoreet.
-
- Nam tempor et magna sed convallis. Fusce sit amet sollicitudin risus, a ullamcorper lacus. Morbi gravida quis sem eget porttitor. Donec eu egestas mauris, in elementum tortor. Sed eget ex mi. Mauris iaculis tortor ut est auctor, nec dignissim quam sagittis. Suspendisse vel metus non quam suscipit tincidunt. Cras molestie lacus non justo finibus sodales quis vitae erat. In a porttitor nisi, id sollicitudin urna. Ut at felis tellus. Suspendisse potenti.
-
- Maecenas leo ligula, varius at neque vitae, ornare maximus justo. Nullam convallis luctus risus et vulputate. Duis suscipit faucibus iaculis. Etiam quis tortor faucibus, tristique tellus sit amet, sodales neque. Nulla dapibus nisi vel aliquet consequat. Etiam faucibus, metus eget condimentum iaculis, enim urna lobortis sem, id efficitur eros sapien nec nisi. Aenean ut finibus ex.
- )
-
- fill_in 'merge_request_description', with: long_description
-
- height = get_textarea_height
- find('.js-md-preview-button').click
- find('.js-md-write-button').click
- new_height = get_textarea_height
-
- expect(height).to eq(new_height)
- end
-
- def get_textarea_height
- find('#merge_request_description')
- page.evaluate_script('document.getElementById("merge_request_description").offsetHeight')
- end
- end
-end
diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb
deleted file mode 100644
index 7adae08e499..00000000000
--- a/spec/features/merge_requests/filter_by_labels_spec.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-require 'rails_helper'
-
-feature 'Merge Request filtering by Labels', :js do
- include FilteredSearchHelpers
- include MergeRequestHelpers
-
- let(:project) { create(:project, :public, :repository) }
- let!(:user) { create(:user) }
- let!(:label) { create(:label, project: project) }
-
- let!(:bug) { create(:label, project: project, title: 'bug') }
- let!(:feature) { create(:label, project: project, title: 'feature') }
- let!(:enhancement) { create(:label, project: project, title: 'enhancement') }
-
- let!(:mr1) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "fix") }
- let!(:mr2) { create(:merge_request, title: "Bugfix2", source_project: project, target_project: project, source_branch: "wip") }
- let!(:mr3) { create(:merge_request, title: "Feature1", source_project: project, target_project: project, source_branch: "improve/awesome") }
-
- before do
- mr1.labels << bug
-
- mr2.labels << bug
- mr2.labels << enhancement
-
- mr3.title = "Feature1"
- mr3.labels << feature
-
- project.add_master(user)
- sign_in(user)
-
- visit project_merge_requests_path(project)
- end
-
- context 'filter by label bug' do
- before do
- input_filtered_search('label:~bug')
- end
-
- it 'apply the filter' do
- expect(page).to have_content "Bugfix1"
- expect(page).to have_content "Bugfix2"
- expect(page).not_to have_content "Feature1"
- end
- end
-
- context 'filter by label feature' do
- before do
- input_filtered_search('label:~feature')
- end
-
- it 'applies the filter' do
- expect(page).to have_content "Feature1"
- expect(page).not_to have_content "Bugfix2"
- expect(page).not_to have_content "Bugfix1"
- end
- end
-
- context 'filter by label enhancement' do
- before do
- input_filtered_search('label:~enhancement')
- end
-
- it 'applies the filter' do
- expect(page).to have_content "Bugfix2"
- expect(page).not_to have_content "Feature1"
- expect(page).not_to have_content "Bugfix1"
- end
- end
-
- context 'filter by label enhancement and bug in issues list' do
- before do
- input_filtered_search('label:~bug label:~enhancement')
- end
-
- it 'applies the filters' do
- expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
- expect(page).to have_content "Bugfix2"
- expect(page).not_to have_content "Feature1"
- end
- end
-
- context 'filter dropdown' do
- it 'filters by label name' do
- init_label_search
- filtered_search.send_keys('~bug')
-
- page.within '.filter-dropdown' do
- expect(page).not_to have_content 'enhancement'
- expect(page).to have_content 'bug'
- end
- end
- end
-end
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
deleted file mode 100644
index 8db94352f73..00000000000
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-require 'rails_helper'
-
-feature 'Merge Request filtering by Milestone' do
- include FilteredSearchHelpers
- include MergeRequestHelpers
-
- let(:project) { create(:project, :public, :repository) }
- let!(:user) { create(:user)}
- let(:milestone) { create(:milestone, project: project) }
-
- def filter_by_milestone(title)
- find(".js-milestone-select").click
- find(".milestone-filter a", text: title).click
- end
-
- before do
- project.add_master(user)
- sign_in(user)
- end
-
- scenario 'filters by no Milestone', :js do
- create(:merge_request, :with_diffs, source_project: project)
- create(:merge_request, :simple, source_project: project, milestone: milestone)
-
- visit_merge_requests(project)
- input_filtered_search('milestone:none')
-
- expect_tokens([milestone_token('none', false)])
- expect_filtered_search_input_empty
-
- expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
- expect(page).to have_css('.merge-request', count: 1)
- end
-
- context 'filters by upcoming milestone', :js do
- it 'does not show merge requests with no expiry' do
- create(:merge_request, :with_diffs, source_project: project)
- create(:merge_request, :simple, source_project: project, milestone: milestone)
-
- visit_merge_requests(project)
- input_filtered_search('milestone:upcoming')
-
- expect(page).to have_css('.merge-request', count: 0)
- end
-
- it 'shows merge requests in future' do
- milestone = create(:milestone, project: project, due_date: Date.tomorrow)
- create(:merge_request, :with_diffs, source_project: project)
- create(:merge_request, :simple, source_project: project, milestone: milestone)
-
- visit_merge_requests(project)
- input_filtered_search('milestone:upcoming')
-
- expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
- expect(page).to have_css('.merge-request', count: 1)
- end
-
- it 'does not show merge requests in past' do
- milestone = create(:milestone, project: project, due_date: Date.yesterday)
- create(:merge_request, :with_diffs, source_project: project)
- create(:merge_request, :simple, source_project: project, milestone: milestone)
-
- visit_merge_requests(project)
- input_filtered_search('milestone:upcoming')
-
- expect(page).to have_css('.merge-request', count: 0)
- end
- end
-
- scenario 'filters by a specific Milestone', :js do
- create(:merge_request, :with_diffs, source_project: project, milestone: milestone)
- create(:merge_request, :simple, source_project: project)
-
- visit_merge_requests(project)
- input_filtered_search("milestone:%'#{milestone.title}'")
-
- expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
- expect(page).to have_css('.merge-request', count: 1)
- end
-
- context 'when milestone has single quotes in title' do
- background do
- milestone.update(name: "rock 'n' roll")
- end
-
- scenario 'filters by a specific Milestone', :js do
- create(:merge_request, :with_diffs, source_project: project, milestone: milestone)
- create(:merge_request, :simple, source_project: project)
-
- visit_merge_requests(project)
- input_filtered_search("milestone:%\"#{milestone.title}\"")
-
- expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
- expect(page).to have_css('.merge-request', count: 1)
- end
- end
-end
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
deleted file mode 100644
index aac295ab940..00000000000
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ /dev/null
@@ -1,337 +0,0 @@
-require 'rails_helper'
-
-describe 'Filter merge requests' do
- include FilteredSearchHelpers
- include MergeRequestHelpers
-
- let!(:project) { create(:project, :repository) }
- let!(:group) { create(:group) }
- let!(:user) { create(:user) }
- let!(:milestone) { create(:milestone, project: project) }
- let!(:label) { create(:label, project: project) }
- let!(:wontfix) { create(:label, project: project, title: "Won't fix") }
-
- before do
- project.add_master(user)
- group.add_developer(user)
- sign_in(user)
- create(:merge_request, source_project: project, target_project: project)
-
- visit project_merge_requests_path(project)
- end
-
- describe 'for assignee from mr#index' do
- let(:search_query) { "assignee:@#{user.username}" }
-
- def expect_assignee_visual_tokens
- wait_for_requests
-
- expect_tokens([assignee_token(user.name)])
- expect_filtered_search_input_empty
- end
-
- before do
- input_filtered_search(search_query)
-
- expect_mr_list_count(0)
- end
-
- context 'assignee', :js do
- it 'updates to current user' do
- expect_assignee_visual_tokens()
- end
-
- it 'does not change when closed link is clicked' do
- find('.issues-state-filters [data-state="closed"]').click
-
- expect_assignee_visual_tokens()
- end
-
- it 'does not change when all link is clicked' do
- find('.issues-state-filters [data-state="all"]').click
-
- expect_assignee_visual_tokens()
- end
- end
- end
-
- describe 'for milestone from mr#index' do
- let(:search_query) { "milestone:%\"#{milestone.title}\"" }
-
- def expect_milestone_visual_tokens
- expect_tokens([milestone_token("\"#{milestone.title}\"")])
- expect_filtered_search_input_empty
- end
-
- before do
- input_filtered_search(search_query)
-
- expect_mr_list_count(0)
- end
-
- context 'milestone', :js do
- it 'updates to current milestone' do
- expect_milestone_visual_tokens()
- end
-
- it 'does not change when closed link is clicked' do
- find('.issues-state-filters [data-state="closed"]').click
-
- expect_milestone_visual_tokens()
- end
-
- it 'does not change when all link is clicked' do
- find('.issues-state-filters [data-state="all"]').click
-
- expect_milestone_visual_tokens()
- end
- end
- end
-
- describe 'for label from mr#index', :js do
- it 'filters by no label' do
- input_filtered_search('label:none')
-
- expect_mr_list_count(1)
- expect_tokens([label_token('none', false)])
- expect_filtered_search_input_empty
- end
-
- it 'filters by a label' do
- input_filtered_search("label:~#{label.title}")
-
- expect_mr_list_count(0)
- expect_tokens([label_token(label.title)])
- expect_filtered_search_input_empty
- end
-
- it "filters by `won't fix` and another label" do
- input_filtered_search("label:~\"#{wontfix.title}\" label:~#{label.title}")
-
- expect_mr_list_count(0)
- expect_tokens([label_token("\"#{wontfix.title}\""), label_token(label.title)])
- expect_filtered_search_input_empty
- end
-
- it "filters by `won't fix` label followed by another label after page load" do
- input_filtered_search("label:~\"#{wontfix.title}\"")
-
- expect_mr_list_count(0)
- expect_tokens([label_token("\"#{wontfix.title}\"")])
- expect_filtered_search_input_empty
-
- input_filtered_search_keys("label:~#{label.title}")
-
- expect_mr_list_count(0)
- expect_tokens([label_token("\"#{wontfix.title}\""), label_token(label.title)])
- expect_filtered_search_input_empty
- end
- end
-
- describe 'for assignee and label from mr#index' do
- let(:search_query) { "assignee:@#{user.username} label:~#{label.title}" }
-
- before do
- input_filtered_search(search_query)
-
- expect_mr_list_count(0)
- end
-
- context 'assignee and label', :js do
- def expect_assignee_label_visual_tokens
- wait_for_requests
-
- expect_tokens([assignee_token(user.name), label_token(label.title)])
- expect_filtered_search_input_empty
- end
-
- it 'updates to current assignee and label' do
- expect_assignee_label_visual_tokens()
- end
-
- it 'does not change when closed link is clicked' do
- find('.issues-state-filters [data-state="closed"]').click
-
- expect_assignee_label_visual_tokens()
- end
-
- it 'does not change when all link is clicked' do
- find('.issues-state-filters [data-state="all"]').click
-
- expect_assignee_label_visual_tokens()
- end
- end
- end
-
- describe 'filter merge requests by text' do
- before do
- create(:merge_request, title: "Bug", source_project: project, target_project: project, source_branch: "wip")
-
- bug_label = create(:label, project: project, title: 'bug')
- milestone = create(:milestone, title: "8", project: project)
-
- mr = create(:merge_request,
- title: "Bug 2",
- source_project: project,
- target_project: project,
- source_branch: "fix",
- milestone: milestone,
- author: user,
- assignee: user)
- mr.labels << bug_label
-
- visit project_merge_requests_path(project)
- end
-
- context 'only text', :js do
- it 'filters merge requests by searched text' do
- input_filtered_search('bug')
-
- expect_mr_list_count(2)
- end
-
- it 'does not show any merge requests' do
- input_filtered_search('testing')
-
- page.within '.mr-list' do
- expect(page).not_to have_selector('.merge-request')
- end
- end
- end
-
- context 'filters and searches', :js do
- it 'filters by text and label' do
- input_filtered_search('Bug')
-
- expect_mr_list_count(2)
- expect_filtered_search_input('Bug')
-
- input_filtered_search_keys(' label:~bug')
-
- expect_mr_list_count(1)
- expect_tokens([label_token('bug')])
- expect_filtered_search_input('Bug')
- end
-
- it 'filters by text and milestone' do
- input_filtered_search('Bug')
-
- expect_mr_list_count(2)
- expect_filtered_search_input('Bug')
-
- input_filtered_search_keys(' milestone:%8')
-
- expect_mr_list_count(1)
- expect_tokens([milestone_token('8')])
- expect_filtered_search_input('Bug')
- end
-
- it 'filters by text and assignee' do
- input_filtered_search('Bug')
-
- expect_mr_list_count(2)
- expect_filtered_search_input('Bug')
-
- input_filtered_search_keys(" assignee:@#{user.username}")
-
- expect_mr_list_count(1)
-
- wait_for_requests
-
- expect_tokens([assignee_token(user.name)])
- expect_filtered_search_input('Bug')
- end
-
- it 'filters by text and author' do
- input_filtered_search('Bug')
-
- expect_mr_list_count(2)
- expect_filtered_search_input('Bug')
-
- input_filtered_search_keys(" author:@#{user.username}")
-
- wait_for_requests
-
- expect_mr_list_count(1)
- expect_tokens([author_token(user.name)])
- expect_filtered_search_input('Bug')
- end
- end
- end
-
- describe 'filter merge requests and sort', :js do
- before do
- bug_label = create(:label, project: project, title: 'bug')
-
- mr1 = create(:merge_request, title: "Frontend", source_project: project, target_project: project, source_branch: "wip")
- mr2 = create(:merge_request, title: "Bug 2", source_project: project, target_project: project, source_branch: "fix")
-
- mr1.labels << bug_label
- mr2.labels << bug_label
-
- visit project_merge_requests_path(project)
- end
-
- it 'is able to filter and sort merge requests' do
- input_filtered_search('label:~bug')
-
- expect_mr_list_count(2)
-
- click_button 'Created date'
- page.within '.dropdown-menu-sort' do
- click_link 'Priority'
- end
- wait_for_requests
-
- page.within '.mr-list' do
- expect(page).to have_content('Frontend')
- end
- end
- end
-
- describe 'filter by assignee id', :js do
- it 'filter by current user' do
- visit project_merge_requests_path(project, assignee_id: user.id)
-
- wait_for_requests
-
- expect_tokens([assignee_token(user.name)])
- expect_filtered_search_input_empty
- end
-
- it 'filter by new user' do
- new_user = create(:user)
- project.add_developer(new_user)
-
- visit project_merge_requests_path(project, assignee_id: new_user.id)
-
- wait_for_requests
-
- expect_tokens([assignee_token(new_user.name)])
- expect_filtered_search_input_empty
- end
- end
-
- describe 'filter by author id', :js do
- it 'filter by current user' do
- visit project_merge_requests_path(project, author_id: user.id)
-
- wait_for_requests
-
- expect_tokens([author_token(user.name)])
- expect_filtered_search_input_empty
- end
-
- it 'filter by new user' do
- new_user = create(:user)
- project.add_developer(new_user)
-
- visit project_merge_requests_path(project, author_id: new_user.id)
-
- wait_for_requests
-
- expect_tokens([author_token(new_user.name)])
- expect_filtered_search_input_empty
- end
- end
-end
diff --git a/spec/features/merge_requests/filters_generic_behavior_spec.rb b/spec/features/merge_requests/filters_generic_behavior_spec.rb
new file mode 100644
index 00000000000..0e7fac6b409
--- /dev/null
+++ b/spec/features/merge_requests/filters_generic_behavior_spec.rb
@@ -0,0 +1,81 @@
+require 'rails_helper'
+
+describe 'Merge Requests > Filters generic behavior', :js do
+ include FilteredSearchHelpers
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:bug) { create(:label, project: project, title: 'bug') }
+ let(:open_mr) { create(:merge_request, title: 'Bugfix1', source_project: project, target_project: project, source_branch: 'bugfix1') }
+ let(:merged_mr) { create(:merge_request, :merged, title: 'Bugfix2', source_project: project, target_project: project, source_branch: 'bugfix2') }
+ let(:closed_mr) { create(:merge_request, :closed, title: 'Feature', source_project: project, target_project: project, source_branch: 'improve/awesome') }
+
+ before do
+ open_mr.labels << bug
+ merged_mr.labels << bug
+ closed_mr.labels << bug
+
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ end
+
+ context 'when filtered by a label' do
+ before do
+ input_filtered_search('label:~bug')
+ end
+
+ describe 'state tabs' do
+ it 'does not change when state tabs are clicked' do
+ expect(page).to have_issuable_counts(open: 1, merged: 1, closed: 1, all: 3)
+ expect(page).to have_content 'Bugfix1'
+ expect(page).not_to have_content 'Bugfix2'
+ expect(page).not_to have_content 'Feature'
+
+ find('.issues-state-filters [data-state="merged"]').click
+
+ expect(page).to have_issuable_counts(open: 1, merged: 1, closed: 1, all: 3)
+ expect(page).not_to have_content 'Bugfix1'
+ expect(page).to have_content 'Bugfix2'
+ expect(page).not_to have_content 'Feature'
+
+ find('.issues-state-filters [data-state="closed"]').click
+
+ expect(page).to have_issuable_counts(open: 1, merged: 1, closed: 1, all: 3)
+ expect(page).not_to have_content 'Bugfix1'
+ expect(page).not_to have_content 'Bugfix2'
+ expect(page).to have_content 'Feature'
+
+ find('.issues-state-filters [data-state="all"]').click
+
+ expect(page).to have_issuable_counts(open: 1, merged: 1, closed: 1, all: 3)
+ expect(page).to have_content 'Bugfix1'
+ expect(page).to have_content 'Bugfix2'
+ expect(page).to have_content 'Feature'
+ end
+ end
+
+ describe 'clear button' do
+ it 'allows user to remove filtered labels' do
+ first('.clear-search').click
+ filtered_search.send_keys(:enter)
+
+ expect(page).to have_issuable_counts(open: 1, merged: 1, closed: 1, all: 3)
+ expect(page).to have_content 'Bugfix1'
+ expect(page).not_to have_content 'Bugfix2'
+ expect(page).not_to have_content 'Feature'
+ end
+ end
+ end
+
+ context 'filter dropdown' do
+ it 'filters by label name' do
+ init_label_search
+ filtered_search.send_keys('~bug')
+
+ page.within '.filter-dropdown' do
+ expect(page).not_to have_content 'enhancement'
+ expect(page).to have_content 'bug'
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
deleted file mode 100644
index 1ebf762a006..00000000000
--- a/spec/features/merge_requests/form_spec.rb
+++ /dev/null
@@ -1,301 +0,0 @@
-require 'rails_helper'
-
-describe 'New/edit merge request', :js do
- include ProjectForksHelper
-
- let!(:project) { create(:project, :public, :repository) }
- let(:forked_project) { fork_project(project, nil, repository: true) }
- let!(:user) { create(:user) }
- let!(:user2) { create(:user) }
- let!(:milestone) { create(:milestone, project: project) }
- let!(:label) { create(:label, project: project) }
- let!(:label2) { create(:label, project: project) }
-
- before do
- project.add_master(user)
- project.add_master(user2)
- end
-
- context 'owned projects' do
- before do
- sign_in(user)
- end
-
- context 'new merge request' do
- before do
- visit project_new_merge_request_path(
- project,
- merge_request: {
- source_project_id: project.id,
- target_project_id: project.id,
- source_branch: 'fix',
- target_branch: 'master'
- })
- end
-
- it 'creates new merge request' do
- click_button 'Assignee'
- page.within '.dropdown-menu-user' do
- click_link user2.name
- end
- expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user2.id.to_s)
- page.within '.js-assignee-search' do
- expect(page).to have_content user2.name
- end
-
- find('a', text: 'Assign to me').click
- expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
- page.within '.js-assignee-search' do
- expect(page).to have_content user.name
- end
-
- click_button 'Milestone'
- page.within '.issue-milestone' do
- click_link milestone.title
- end
- expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
- page.within '.js-milestone-select' do
- expect(page).to have_content milestone.title
- end
-
- click_button 'Labels'
- page.within '.dropdown-menu-labels' do
- click_link label.title
- click_link label2.title
- end
- page.within '.js-label-select' do
- expect(page).to have_content label.title
- end
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
-
- click_button 'Submit merge request'
-
- page.within '.issuable-sidebar' do
- page.within '.assignee' do
- expect(page).to have_content user.name
- end
-
- page.within '.milestone' do
- expect(page).to have_content milestone.title
- end
-
- page.within '.labels' do
- expect(page).to have_content label.title
- expect(page).to have_content label2.title
- end
- end
-
- page.within '.breadcrumbs' do
- merge_request = MergeRequest.find_by(source_branch: 'fix')
-
- expect(page).to have_text("Merge Requests #{merge_request.to_reference}")
- end
- end
-
- it 'description has autocomplete' do
- find('#merge_request_description').native.send_keys('')
- fill_in 'merge_request_description', with: '@'
-
- expect(page).to have_selector('.atwho-view')
- end
- end
-
- context 'edit merge request' do
- before do
- merge_request = create(:merge_request,
- source_project: project,
- target_project: project,
- source_branch: 'fix',
- target_branch: 'master'
- )
-
- visit edit_project_merge_request_path(project, merge_request)
- end
-
- it 'updates merge request' do
- click_button 'Assignee'
- page.within '.dropdown-menu-user' do
- click_link user.name
- end
- expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
- page.within '.js-assignee-search' do
- expect(page).to have_content user.name
- end
-
- click_button 'Milestone'
- page.within '.issue-milestone' do
- click_link milestone.title
- end
- expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
- page.within '.js-milestone-select' do
- expect(page).to have_content milestone.title
- end
-
- click_button 'Labels'
- page.within '.dropdown-menu-labels' do
- click_link label.title
- click_link label2.title
- end
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
- page.within '.js-label-select' do
- expect(page).to have_content label.title
- end
-
- click_button 'Save changes'
-
- page.within '.issuable-sidebar' do
- page.within '.assignee' do
- expect(page).to have_content user.name
- end
-
- page.within '.milestone' do
- expect(page).to have_content milestone.title
- end
-
- page.within '.labels' do
- expect(page).to have_content label.title
- expect(page).to have_content label2.title
- end
- end
- end
-
- it 'description has autocomplete' do
- find('#merge_request_description').native.send_keys('')
- fill_in 'merge_request_description', with: '@'
-
- expect(page).to have_selector('.atwho-view')
- end
- end
- end
-
- context 'forked project' do
- before do
- forked_project.add_master(user)
- sign_in(user)
- end
-
- context 'new merge request' do
- before do
- visit project_new_merge_request_path(
- forked_project,
- merge_request: {
- source_project_id: forked_project.id,
- target_project_id: project.id,
- source_branch: 'fix',
- target_branch: 'master'
- })
- end
-
- it 'creates new merge request' do
- click_button 'Assignee'
- page.within '.dropdown-menu-user' do
- click_link user.name
- end
- expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
- page.within '.js-assignee-search' do
- expect(page).to have_content user.name
- end
-
- click_button 'Milestone'
- page.within '.issue-milestone' do
- click_link milestone.title
- end
- expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
- page.within '.js-milestone-select' do
- expect(page).to have_content milestone.title
- end
-
- click_button 'Labels'
- page.within '.dropdown-menu-labels' do
- click_link label.title
- click_link label2.title
- end
- page.within '.js-label-select' do
- expect(page).to have_content label.title
- end
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
-
- click_button 'Submit merge request'
-
- page.within '.issuable-sidebar' do
- page.within '.assignee' do
- expect(page).to have_content user.name
- end
-
- page.within '.milestone' do
- expect(page).to have_content milestone.title
- end
-
- page.within '.labels' do
- expect(page).to have_content label.title
- expect(page).to have_content label2.title
- end
- end
- end
- end
-
- context 'edit merge request' do
- before do
- merge_request = create(:merge_request,
- source_project: forked_project,
- target_project: project,
- source_branch: 'fix',
- target_branch: 'master'
- )
-
- visit edit_project_merge_request_path(project, merge_request)
- end
-
- it 'should update merge request' do
- click_button 'Assignee'
- page.within '.dropdown-menu-user' do
- click_link user.name
- end
- expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
- page.within '.js-assignee-search' do
- expect(page).to have_content user.name
- end
-
- click_button 'Milestone'
- page.within '.issue-milestone' do
- click_link milestone.title
- end
- expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
- page.within '.js-milestone-select' do
- expect(page).to have_content milestone.title
- end
-
- click_button 'Labels'
- page.within '.dropdown-menu-labels' do
- click_link label.title
- click_link label2.title
- end
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
- expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
- page.within '.js-label-select' do
- expect(page).to have_content label.title
- end
-
- click_button 'Save changes'
-
- page.within '.issuable-sidebar' do
- page.within '.assignee' do
- expect(page).to have_content user.name
- end
-
- page.within '.milestone' do
- expect(page).to have_content milestone.title
- end
-
- page.within '.labels' do
- expect(page).to have_content label.title
- expect(page).to have_content label2.title
- end
- end
- end
- end
- end
-end
diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb
deleted file mode 100644
index daca4422bf1..00000000000
--- a/spec/features/merge_requests/reset_filters_spec.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-require 'rails_helper'
-
-feature 'Merge requests filter clear button', :js do
- include FilteredSearchHelpers
- include MergeRequestHelpers
- include IssueHelpers
-
- let!(:project) { create(:project, :public, :repository) }
- let!(:user) { create(:user) }
- let!(:milestone) { create(:milestone, project: project) }
- let!(:bug) { create(:label, project: project, name: 'bug')}
- let!(:mr1) { create(:merge_request, title: "Feature", source_project: project, target_project: project, source_branch: "improve/awesome", milestone: milestone, author: user, assignee: user) }
- let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "fix") }
-
- let(:merge_request_css) { '.merge-request' }
- let(:clear_search_css) { '.filtered-search-box .clear-search' }
-
- before do
- mr2.labels << bug
- project.add_developer(user)
- end
-
- context 'when a milestone filter has been applied' do
- it 'resets the milestone filter' do
- visit_merge_requests(project, milestone_title: milestone.title)
-
- expect(page).to have_css(merge_request_css, count: 1)
- expect(get_filtered_search_placeholder).to eq('')
-
- reset_filters
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- end
- end
-
- context 'when a label filter has been applied' do
- it 'resets the label filter' do
- visit_merge_requests(project, label_name: bug.name)
-
- expect(page).to have_css(merge_request_css, count: 1)
- expect(get_filtered_search_placeholder).to eq('')
-
- reset_filters
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- end
- end
-
- context 'when multiple label filters have been applied' do
- let!(:label) { create(:label, project: project, name: 'Frontend') }
- let(:filter_dropdown) { find("#js-dropdown-label .filter-dropdown") }
-
- before do
- visit_merge_requests(project)
- init_label_search
- end
-
- it 'filters bug label' do
- filtered_search.set('~bug')
-
- filter_dropdown.find('.filter-dropdown-item', text: bug.title).click
- init_label_search
-
- expect(filter_dropdown.find('.filter-dropdown-item', text: bug.title)).to be_visible
- expect(filter_dropdown.find('.filter-dropdown-item', text: label.title)).to be_visible
- end
- end
-
- context 'when a text search has been conducted' do
- it 'resets the text search filter' do
- visit_merge_requests(project, search: 'Bug')
-
- expect(page).to have_css(merge_request_css, count: 1)
- expect(get_filtered_search_placeholder).to eq('')
-
- reset_filters
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- end
- end
-
- context 'when author filter has been applied' do
- it 'resets the author filter' do
- visit_merge_requests(project, author_username: user.username)
-
- expect(page).to have_css(merge_request_css, count: 1)
- expect(get_filtered_search_placeholder).to eq('')
-
- reset_filters
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- end
- end
-
- context 'when assignee filter has been applied' do
- it 'resets the assignee filter' do
- visit_merge_requests(project, assignee_username: user.username)
-
- expect(page).to have_css(merge_request_css, count: 1)
- expect(get_filtered_search_placeholder).to eq('')
-
- reset_filters
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- end
- end
-
- context 'when all filters have been applied' do
- it 'clears all filters' do
- visit_merge_requests(project, assignee_username: user.username, author_username: user.username, milestone_title: milestone.title, label_name: bug.name, search: 'Bug')
-
- expect(page).to have_css(merge_request_css, count: 0)
- expect(get_filtered_search_placeholder).to eq('')
-
- reset_filters
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- end
- end
-
- context 'when no filters have been applied' do
- it 'the clear button should not be visible' do
- visit_merge_requests(project)
-
- expect(page).to have_css(merge_request_css, count: 2)
- expect(get_filtered_search_placeholder).to eq(default_placeholder)
- expect(page).not_to have_css(clear_search_css)
- end
- end
-end
diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb
deleted file mode 100644
index d9f7a056dea..00000000000
--- a/spec/features/merge_requests/target_branch_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'spec_helper'
-
-describe 'Target branch', :js do
- let(:user) { create(:user) }
- let(:merge_request) { create(:merge_request) }
- let(:project) { merge_request.project }
-
- def path_to_merge_request
- project_merge_request_path(project, merge_request)
- end
-
- before do
- sign_in user
- project.add_master(user)
- end
-
- context 'when branch was deleted' do
- before do
- DeleteBranchService.new(project, user).execute('feature')
- visit path_to_merge_request
- end
-
- it 'shows a message about missing target branch' do
- expect(page).to have_content(
- 'Target branch does not exist'
- )
- end
-
- it 'does not show link to target branch' do
- expect(page).not_to have_selector('.mr-widget-body .js-branch-text a')
- end
- end
-end
diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb
deleted file mode 100644
index cd92ad22267..00000000000
--- a/spec/features/merge_requests/toggler_behavior_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require 'spec_helper'
-
-feature 'toggler_behavior', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, source_project: project, author: user) }
- let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
- let(:fragment_id) { "#note_#{note.id}" }
-
- before do
- sign_in(create(:admin))
- project = merge_request.source_project
- page.current_window.resize_to(1000, 300)
- visit "#{project_merge_request_path(project, merge_request)}#{fragment_id}"
- end
-
- describe 'scroll position' do
- it 'should be scrolled down to fragment' do
- page_height = page.current_window.size[1]
- page_scroll_y = page.evaluate_script("window.scrollY")
- fragment_position_top = page.evaluate_script("Math.round($('#{fragment_id}').offset().top)")
- expect(find('.js-toggle-content').visible?).to eq true
- expect(find(fragment_id).visible?).to eq true
- expect(fragment_position_top).to be >= page_scroll_y
- expect(fragment_position_top).to be < (page_scroll_y + page_height)
- end
- end
-end
diff --git a/spec/features/merge_requests/user_filters_by_assignees_spec.rb b/spec/features/merge_requests/user_filters_by_assignees_spec.rb
new file mode 100644
index 00000000000..d6c770c93f1
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_assignees_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+describe 'Merge Requests > User filters by assignees', :js do
+ include FilteredSearchHelpers
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+
+ before do
+ create(:merge_request, assignee: user, title: 'Bugfix1', source_project: project, target_project: project, source_branch: 'bugfix1')
+ create(:merge_request, title: 'Bugfix2', source_project: project, target_project: project, source_branch: 'bugfix2')
+
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ end
+
+ context 'filtering by assignee:none' do
+ it 'applies the filter' do
+ input_filtered_search('assignee:none')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).not_to have_content 'Bugfix1'
+ expect(page).to have_content 'Bugfix2'
+ end
+ end
+
+ context 'filtering by assignee:@username' do
+ it 'applies the filter' do
+ input_filtered_search("assignee:@#{user.username}")
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_content 'Bugfix1'
+ expect(page).not_to have_content 'Bugfix2'
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_filters_by_labels_spec.rb b/spec/features/merge_requests/user_filters_by_labels_spec.rb
new file mode 100644
index 00000000000..08d741af93d
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_labels_spec.rb
@@ -0,0 +1,49 @@
+require 'rails_helper'
+
+describe 'Merge Requests > User filters by labels', :js do
+ include FilteredSearchHelpers
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:mr1) { create(:merge_request, title: 'Bugfix1', source_project: project, target_project: project, source_branch: 'bugfix1') }
+ let(:mr2) { create(:merge_request, title: 'Bugfix2', source_project: project, target_project: project, source_branch: 'bugfix2') }
+
+ before do
+ bug_label = create(:label, project: project, title: 'bug')
+ enhancement_label = create(:label, project: project, title: 'enhancement')
+ mr1.labels << bug_label
+ mr2.labels << bug_label << enhancement_label
+
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ end
+
+ context 'filtering by label:none' do
+ it 'applies the filter' do
+ input_filtered_search('label:none')
+
+ expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0)
+ expect(page).not_to have_content 'Bugfix1'
+ expect(page).not_to have_content 'Bugfix2'
+ end
+ end
+
+ context 'filtering by label:~enhancement' do
+ it 'applies the filter' do
+ input_filtered_search('label:~enhancement')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_content 'Bugfix2'
+ expect(page).not_to have_content 'Bugfix1'
+ end
+ end
+
+ context 'filtering by label:~enhancement and label:~bug' do
+ it 'applies the filters' do
+ input_filtered_search('label:~bug label:~enhancement')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_content 'Bugfix2'
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_filters_by_milestones_spec.rb b/spec/features/merge_requests/user_filters_by_milestones_spec.rb
new file mode 100644
index 00000000000..727a236d980
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_milestones_spec.rb
@@ -0,0 +1,62 @@
+require 'rails_helper'
+
+describe 'Merge Requests > User filters by milestones', :js do
+ include FilteredSearchHelpers
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:milestone) { create(:milestone, project: project) }
+
+ before do
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ end
+
+ it 'filters by no milestone' do
+ input_filtered_search('milestone:none')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+
+ it 'filters by a specific milestone' do
+ input_filtered_search("milestone:%'#{milestone.title}'")
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+
+ describe 'filters by upcoming milestone' do
+ it 'does not show merge requests with no expiry' do
+ input_filtered_search('milestone:upcoming')
+
+ expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0)
+ expect(page).to have_css('.merge-request', count: 0)
+ end
+
+ context 'with an upcoming milestone' do
+ let(:milestone) { create(:milestone, project: project, due_date: Date.tomorrow) }
+
+ it 'shows merge requests' do
+ input_filtered_search('milestone:upcoming')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+ end
+
+ context 'with a due milestone' do
+ let(:milestone) { create(:milestone, project: project, due_date: Date.yesterday) }
+
+ it 'does not show any merge requests' do
+ input_filtered_search('milestone:upcoming')
+
+ expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0)
+ expect(page).to have_css('.merge-request', count: 0)
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb b/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb
new file mode 100644
index 00000000000..1615899a047
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb
@@ -0,0 +1,38 @@
+require 'rails_helper'
+
+describe 'Merge requests > User filters by multiple criteria', :js do
+ include FilteredSearchHelpers
+
+ let!(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let!(:milestone) { create(:milestone, title: 'v1.1', project: project) }
+ let!(:wontfix) { create(:label, project: project, title: "Won't fix") }
+
+ before do
+ sign_in(user)
+ mr = create(:merge_request, title: 'Bugfix2', author: user, assignee: user, source_project: project, target_project: project, milestone: milestone)
+ mr.labels << wontfix
+
+ visit project_merge_requests_path(project)
+ end
+
+ describe 'filtering by label:~"Won\'t fix" and assignee:~bug' do
+ it 'applies the filters' do
+ input_filtered_search("label:~\"Won't fix\" assignee:@#{user.username}")
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_content 'Bugfix2'
+ expect_filtered_search_input_empty
+ end
+ end
+
+ describe 'filtering by text, author, assignee, milestone, and label' do
+ it 'filters by text, author, assignee, milestone, and label' do
+ input_filtered_search_keys("author:@#{user.username} assignee:@#{user.username} milestone:%\"v1.1\" label:~\"Won't fix\" Bug")
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_content 'Bugfix2'
+ expect_filtered_search_input('Bug')
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index 416a0f78a45..ef7ae490b0f 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -1,6 +1,6 @@
-require 'spec_helper'
+require 'rails_helper'
-describe 'Projects > Merge requests > User lists merge requests' do
+describe 'Merge requests > User lists merge requests' do
include MergeRequestHelpers
include SortingHelper
diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index a96404b86ed..199ba7e87ad 100644
--- a/spec/features/merge_requests/update_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -1,8 +1,8 @@
require 'rails_helper'
-feature 'Multiple merge requests updating from merge_requests#index' do
- let!(:user) { create(:user)}
- let!(:project) { create(:project, :repository) }
+describe 'Merge requests > User mass updates', :js do
+ let(:project) { create(:project, :repository) }
+ let(:user) { project.creator }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
before do
@@ -10,7 +10,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do
sign_in(user)
end
- context 'status', :js do
+ context 'status' do
describe 'close merge request' do
before do
visit project_merge_requests_path(project)
@@ -37,13 +37,13 @@ feature 'Multiple merge requests updating from merge_requests#index' do
end
end
- context 'assignee', :js do
+ context 'assignee' do
describe 'set assignee' do
before do
visit project_merge_requests_path(project)
end
- it "updates merge request with assignee" do
+ it 'updates merge request with assignee' do
change_assignee(user.name)
page.within('.merge-request .controls') do
@@ -59,7 +59,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do
visit project_merge_requests_path(project)
end
- it "removes assignee from the merge request" do
+ it 'removes assignee from the merge request' do
change_assignee('Unassigned')
expect(find('.merge-request .controls')).not_to have_css('.author_link')
@@ -67,7 +67,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do
end
end
- context 'milestone', :js do
+ context 'milestone' do
let(:milestone) { create(:milestone, project: project) }
describe 'set milestone' do
@@ -75,7 +75,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do
visit project_merge_requests_path(project)
end
- it "updates merge request with milestone" do
+ it 'updates merge request with milestone' do
change_milestone(milestone.title)
expect(find('.merge-request')).to have_content milestone.title
@@ -89,7 +89,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do
visit project_merge_requests_path(project)
end
- it "removes milestone from the merge request" do
+ it 'removes milestone from the merge request' do
change_milestone("No Milestone")
expect(find('.merge-request')).not_to have_content milestone.title
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
deleted file mode 100644
index ec2da72ddff..00000000000
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'spec_helper'
-
-feature 'Widget Deployments Header', :js do
- describe 'when deployed to an environment' do
- given(:user) { create(:user) }
- given(:project) { merge_request.target_project }
- given(:merge_request) { create(:merge_request, :merged) }
- given(:environment) { create(:environment, project: project) }
- given(:role) { :developer }
- given(:sha) { project.commit('master').id }
- given!(:deployment) { create(:deployment, environment: environment, sha: sha) }
- given!(:manual) { }
-
- background do
- sign_in(user)
- project.add_role(user, role)
- visit project_merge_request_path(project, merge_request)
- end
-
- scenario 'displays that the environment is deployed' do
- wait_for_requests
-
- expect(page).to have_content("Deployed to #{environment.name}")
- expect(find('.js-deploy-time')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
- end
-
- context 'with stop action' do
- given(:pipeline) { create(:ci_pipeline, project: project) }
- given(:build) { create(:ci_build, pipeline: pipeline) }
- given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
- given(:deployment) do
- create(:deployment, environment: environment, ref: merge_request.target_branch,
- sha: sha, deployable: build, on_stop: 'close_app')
- end
-
- background do
- wait_for_requests
- end
-
- scenario 'does show stop button' do
- expect(page).to have_button('Stop environment')
- end
-
- scenario 'does start build when stop button clicked' do
- accept_confirm { click_button('Stop environment') }
-
- expect(page).to have_content('close_app')
- end
-
- context 'for reporter' do
- given(:role) { :reporter }
-
- scenario 'does not show stop button' do
- expect(page).not_to have_button('Stop environment')
- end
- end
- end
- end
-end
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index 49d8e52f861..a5e325ee2e3 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -10,8 +10,7 @@ feature 'OAuth Login', :js, :allow_forgery_protection do
def stub_omniauth_config(provider)
OmniAuth.config.add_mock(provider, OmniAuth::AuthHash.new(provider: provider.to_s, uid: "12345"))
- set_devise_mapping(context: Rails.application)
- Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
+ stub_omniauth_provider(provider)
end
providers = [:github, :twitter, :bitbucket, :gitlab, :google_oauth2,
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index 266af8f4e3d..90d6841af0e 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -32,18 +32,6 @@ describe 'User visits the profile preferences page' do
end
end
- describe 'User changes their multi file editor preferences', :js do
- it 'set the new_repo cookie when the option is ON' do
- choose 'user_multi_file_on'
- expect(get_cookie('new_repo')).not_to be_nil
- end
-
- it 'deletes the new_repo cookie when the option is OFF' do
- choose 'user_multi_file_off'
- expect(get_cookie('new_repo')).to be_nil
- end
- end
-
describe 'User changes their default dashboard', :js do
it 'creates a flash message' do
select 'Starred Projects', from: 'user_dashboard'
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 8953b30bebf..94bde723e2f 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -95,7 +95,7 @@ feature 'Gcp Cluster', :js do
context 'when user disables the cluster' do
before do
- page.find(:css, '.js-toggle-cluster').click
+ page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
page.within('#cluster-integration') { click_button 'Save changes' }
end
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index a519b9f9c7e..b9ab434c259 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -62,7 +62,7 @@ feature 'User Cluster', :js do
context 'when user disables the cluster' do
before do
- page.find(:css, '.js-toggle-cluster').click
+ page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
fill_in 'cluster_name', with: 'dev-cluster'
page.within('#cluster-integration') { click_button 'Save changes' }
end
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index eae2910a8f6..497a50bebe4 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -37,13 +37,13 @@ feature 'Clusters', :js do
context 'inline update of cluster' do
it 'user can update cluster' do
- expect(page).to have_selector('.js-toggle-cluster-list')
+ expect(page).to have_selector('.js-project-feature-toggle')
end
context 'with sucessfull request' do
it 'user sees updated cluster' do
expect do
- page.find('.js-toggle-cluster-list').click
+ page.find('.js-project-feature-toggle').click
wait_for_requests
end.to change { cluster.reload.enabled }
@@ -57,7 +57,7 @@ feature 'Clusters', :js do
expect_any_instance_of(Clusters::UpdateService).to receive(:execute).and_call_original
allow_any_instance_of(Clusters::Cluster).to receive(:valid?) { false }
- page.find('.js-toggle-cluster-list').click
+ page.find('.js-project-feature-toggle').click
expect(page).to have_content('Something went wrong on our end.')
expect(page).to have_selector('.is-checked')
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index af125e1b9d3..e8bb9c6a86c 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -32,7 +32,7 @@ feature 'Import/Export - project import integration test', :js do
expect(page).to have_content('Import an exported GitLab project')
expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=#{project_path}")
- expect(Gitlab::ImportExport).to receive(:import_upload_path).with(filename: /\A\h{32}_test-project-path\h*\z/).and_call_original
+ expect(Gitlab::ImportExport).to receive(:import_upload_path).with(filename: /\A\h{32}\z/).and_call_original
attach_file('file', file)
click_on 'Import project'
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index a5cd858b11a..e661db1809a 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -384,12 +384,12 @@ feature 'Jobs' do
expect(page).to have_link('Trigger this manual action')
end
- it 'plays manual action', :js do
+ it 'plays manual action and shows pending status', :js do
click_link 'Trigger this manual action'
wait_for_requests
- expect(page).to have_content('This job has not been triggered')
- expect(page).to have_content('This job is stuck, because the project doesn\'t have any runners online assigned to it.')
+ expect(page).to have_content('This job has not started yet')
+ expect(page).to have_content('This job is in pending state and is waiting to be picked by a runner')
expect(page).to have_content('pending')
end
end
@@ -403,6 +403,20 @@ feature 'Jobs' do
it 'shows empty state' do
expect(page).to have_content('This job has not been triggered yet')
+ expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered')
+ end
+ end
+
+ context 'Pending job' do
+ let(:job) { create(:ci_build, :pending, pipeline: pipeline) }
+
+ before do
+ visit project_job_path(project, job)
+ end
+
+ it 'shows pending empty state' do
+ expect(page).to have_content('This job has not started yet')
+ expect(page).to have_content('This job is in pending state and is waiting to be picked by a runner')
end
end
end
diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb
deleted file mode 100644
index b34b13db381..00000000000
--- a/spec/features/projects/merge_requests/list_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'spec_helper'
-
-feature 'Merge Requests List' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
-
- background do
- project.add_developer(user)
-
- sign_in(user)
- end
-
- scenario 'user does not see create new list button' do
- create(:merge_request, source_project: project)
-
- visit project_merge_requests_path(project)
-
- expect(page).not_to have_selector('.js-new-board-list')
- end
-
- it 'should show an empty state' do
- visit project_merge_requests_path(project)
-
- expect(page).to have_selector('.empty-state')
- end
-
- it 'empty state should have a create merge request button' do
- visit project_merge_requests_path(project)
-
- expect(page).to have_link 'New merge request', href: project_new_merge_request_path(project)
- end
-
- context 'if there are merge requests' do
- before do
- create(:merge_request, assignee: user, source_project: project)
-
- visit project_merge_requests_path(project)
- end
-
- it 'should not show an empty state' do
- expect(page).not_to have_selector('.empty-state')
- end
- end
-end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 81b282502fc..14670e91006 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -43,7 +43,7 @@ feature 'Repository settings' do
fill_in 'deploy_key_title', with: 'new_deploy_key'
fill_in 'deploy_key_key', with: new_ssh_key
- check 'deploy_key_can_push'
+ check 'deploy_key_deploy_keys_projects_attributes_0_can_push'
click_button 'Add key'
expect(page).to have_content('new_deploy_key')
@@ -57,7 +57,7 @@ feature 'Repository settings' do
find('li', text: private_deploy_key.title).click_link('Edit')
fill_in 'deploy_key_title', with: 'updated_deploy_key'
- check 'deploy_key_can_push'
+ check 'deploy_key_deploy_keys_projects_attributes_0_can_push'
click_button 'Save changes'
expect(page).to have_content('updated_deploy_key')
@@ -74,11 +74,9 @@ feature 'Repository settings' do
find('li', text: private_deploy_key.title).click_link('Edit')
fill_in 'deploy_key_title', with: 'updated_deploy_key'
- check 'deploy_key_can_push'
click_button 'Save changes'
expect(page).to have_content('updated_deploy_key')
- expect(page).to have_content('Write access allowed')
end
scenario 'remove an existing deploy key' do
diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb
index 670e8dda916..975c157bcf5 100644
--- a/spec/features/user_can_display_performance_bar_spec.rb
+++ b/spec/features/user_can_display_performance_bar_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
describe 'User can display performance bar', :js do
- shared_examples 'performance bar is disabled' do
+ shared_examples 'performance bar cannot be displayed' do
it 'does not show the performance bar by default' do
expect(page).not_to have_css('#peek')
end
@@ -17,7 +17,7 @@ describe 'User can display performance bar', :js do
end
end
- shared_examples 'performance bar is enabled' do
+ shared_examples 'performance bar can be displayed' do
it 'does not show the performance bar by default' do
expect(page).not_to have_css('#peek')
end
@@ -33,6 +33,18 @@ describe 'User can display performance bar', :js do
end
end
+ shared_examples 'performance bar is enabled by default in development' do
+ before do
+ allow(Rails.env).to receive(:development?).and_return(true)
+ end
+
+ it 'shows the performance bar by default' do
+ refresh # Because we're stubbing Rails.env after the 1st visit to root_path
+
+ expect(page).to have_css('#peek')
+ end
+ end
+
let(:group) { create(:group) }
context 'when user is logged-out' do
@@ -45,7 +57,7 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: nil)
end
- it_behaves_like 'performance bar is disabled'
+ it_behaves_like 'performance bar cannot be displayed'
end
context 'when the performance_bar feature is enabled' do
@@ -53,7 +65,7 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: group.id)
end
- it_behaves_like 'performance bar is disabled'
+ it_behaves_like 'performance bar cannot be displayed'
end
end
@@ -72,7 +84,8 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: nil)
end
- it_behaves_like 'performance bar is disabled'
+ it_behaves_like 'performance bar cannot be displayed'
+ it_behaves_like 'performance bar is enabled by default in development'
end
context 'when the performance_bar feature is enabled' do
@@ -80,7 +93,8 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: group.id)
end
- it_behaves_like 'performance bar is enabled'
+ it_behaves_like 'performance bar is enabled by default in development'
+ it_behaves_like 'performance bar can be displayed'
end
end
end
diff --git a/spec/features/user_page_spec.rb b/spec/features/user_page_spec.rb
new file mode 100644
index 00000000000..19c587e53c8
--- /dev/null
+++ b/spec/features/user_page_spec.rb
@@ -0,0 +1,107 @@
+require 'spec_helper'
+
+describe 'User page', :js do
+ let!(:user) { create :user }
+ let!(:private_project) do
+ create :project, :private, name: 'private', namespace: user.namespace do |project|
+ project.add_master(user)
+ end
+ end
+
+ let!(:internal_project) do
+ create :project, :internal, name: 'internal', namespace: user.namespace do |project|
+ project.add_master(user)
+ end
+ end
+
+ let!(:public_project) do
+ create :project, :public, name: 'public', namespace: user.namespace do |project|
+ project.add_master(user)
+ end
+ end
+
+ def click_nav_link(name)
+ page.within '.nav-links' do
+ click_link name
+ end
+ end
+
+ context 'when not signed in' do
+ it 'renders user public project' do
+ visit user_path(user)
+ click_nav_link('Personal projects')
+
+ expect(page).to have_css('.tab-content #projects.active')
+ expect(title).to start_with(user.name)
+
+ expect(page).to have_content(public_project.name)
+ expect(page).not_to have_content(private_project.name)
+ expect(page).not_to have_content(internal_project.name)
+ end
+ end
+
+ context 'when signed in as another user' do
+ let(:another_user) { create :user }
+
+ before do
+ sign_in(another_user)
+ end
+
+ it 'renders user public and internal projects' do
+ visit user_path(user)
+ click_nav_link('Personal projects')
+
+ expect(title).to start_with(user.name)
+
+ expect(page).not_to have_content(private_project.name)
+ expect(page).to have_content(public_project.name)
+ expect(page).to have_content(internal_project.name)
+ end
+ end
+
+ context 'when signed in as user' do
+ before do
+ sign_in(user)
+ end
+
+ describe 'personal projects' do
+ it 'renders all user projects' do
+ visit user_path(user)
+ click_nav_link('Personal projects')
+
+ expect(title).to start_with(user.name)
+
+ expect(page).to have_content(private_project.name)
+ expect(page).to have_content(public_project.name)
+ expect(page).to have_content(internal_project.name)
+ end
+ end
+
+ describe 'contributed projects' do
+ context 'when user has contributions' do
+ let(:contributed_project) do
+ create :project, :public, :empty_repo
+ end
+
+ before do
+ Issues::CreateService.new(contributed_project, user, { title: 'Bug in old browser' }).execute
+ event = create(:push_event, project: contributed_project, author: user)
+ create(:push_event_payload, event: event, commit_count: 3)
+ end
+
+ it 'renders contributed project' do
+ visit user_path(user)
+
+ expect(title).to start_with(user.name)
+ expect(page).to have_css('.js-contrib-calendar')
+
+ click_nav_link('Contributed projects')
+
+ page.within '#contributed' do
+ expect(page).to have_content(contributed_project.name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index ae050f36b4a..375bcc9087e 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -35,6 +35,15 @@ describe GroupDescendantsFinder do
expect(finder.execute).to contain_exactly(project)
end
+ it 'does not include projects shared with the group' do
+ project = create(:project, namespace: group)
+ other_project = create(:project)
+ other_project.project_group_links.create(group: group,
+ group_access: ProjectGroupLink::MASTER)
+
+ expect(finder.execute).to contain_exactly(project)
+ end
+
context 'when archived is `true`' do
let(:params) { { archived: 'true' } }
@@ -189,6 +198,17 @@ describe GroupDescendantsFinder do
expect(finder.execute).to contain_exactly(subgroup, matching_project)
end
+ context 'with a small page size' do
+ let(:params) { { filter: 'test', per_page: 1 } }
+
+ it 'contains all the ancestors of a matching subgroup regardless the page size' do
+ subgroup = create(:group, :private, parent: group)
+ matching = create(:group, :private, name: 'testgroup', parent: subgroup)
+
+ expect(finder.execute).to contain_exactly(subgroup, matching)
+ end
+ end
+
it 'does not include the parent itself' do
group.update!(name: 'test')
diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb
index 8ae08656e01..0b3cf7ece5f 100644
--- a/spec/finders/milestones_finder_spec.rb
+++ b/spec/finders/milestones_finder_spec.rb
@@ -21,10 +21,19 @@ describe MilestonesFinder do
expect(result).to contain_exactly(milestone_1, milestone_2)
end
- it 'returns milestones for groups and projects' do
- result = described_class.new(project_ids: [project_1.id, project_2.id], group_ids: group.id, state: 'all').execute
+ context 'milestones for groups and project' do
+ let(:result) do
+ described_class.new(project_ids: [project_1.id, project_2.id], group_ids: group.id, state: 'all').execute
+ end
+
+ it 'returns milestones for groups and projects' do
+ expect(result).to contain_exactly(milestone_1, milestone_2, milestone_3, milestone_4)
+ end
- expect(result).to contain_exactly(milestone_1, milestone_2, milestone_3, milestone_4)
+ it 'orders milestones by due date' do
+ expect(result.first).to eq(milestone_1)
+ expect(result.second).to eq(milestone_3)
+ end
end
context 'with filters' do
@@ -61,30 +70,4 @@ describe MilestonesFinder do
expect(result.to_a).to contain_exactly(milestone_1)
end
end
-
- context 'with order' do
- let(:params) do
- {
- project_ids: [project_1.id, project_2.id],
- group_ids: group.id,
- state: 'all'
- }
- end
-
- it "default orders by due date" do
- result = described_class.new(params).execute
-
- expect(result.first).to eq(milestone_1)
- expect(result.second).to eq(milestone_3)
- end
-
- it "orders by parameter" do
- result = described_class.new(params.merge(order: 'id DESC')).execute
-
- expect(result.first).to eq(milestone_4)
- expect(result.second).to eq(milestone_3)
- expect(result.third).to eq(milestone_2)
- expect(result.fourth).to eq(milestone_1)
- end
- end
end
diff --git a/spec/fixtures/emails/attachment.eml b/spec/fixtures/emails/attachment.eml
index f25c3d1a449..b3a30b3221b 100644
--- a/spec/fixtures/emails/attachment.eml
+++ b/spec/fixtures/emails/attachment.eml
@@ -91,7 +91,7 @@ x #ccc solid;padding-left:1ex"><div>
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
+ <img src=3D"https://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m=
ax-width:694px" width=3D"45" height=3D"45">
</td>
@@ -121,7 +121,7 @@ nk">@eviltrout</a> Any idea why it showed up in suggested topics? </p>
<div style=3D"color:#666">
<p>To respond, reply to this email or visit <a href=3D"http://meta.disc=
ourse.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"co=
-lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back=
+lor:#666" target=3D"_blank">https://meta.discourse.org/t/spam-post-pops-back=
-up-in-suggested-topics/11005/5</a> in your browser.</p>
</div>
@@ -132,12 +132,12 @@ lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back=
lpadding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
+ <img src=3D"https://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m=
ax-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/neil" style=3D"font-size=
+ <a href=3D"https://meta.discourse.org/users/neil" style=3D"font-size=
:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif;c=
olor:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">Neil<=
/a><br>
@@ -155,12 +155,12 @@ vember 19</span>
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
+ <img src=3D"https://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D=
"max-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/riking" style=3D"font-si=
+ <a href=3D"https://meta.discourse.org/users/riking" style=3D"font-si=
ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif=
;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik=
ing</a><br>
@@ -173,7 +173,7 @@ vember 19</span>
<td style=3D"padding-top:5px" colspan=3D"2">
<p style=3D"margin-top:0"><u></u></p><div>
<div></div>
-<img width=3D"20" height=3D"20" src=3D"http://www.gravatar.com/avatar/51d62=
+<img width=3D"20" height=3D"20" src=3D"https://www.gravatar.com/avatar/51d62=
3f33f8b83095db84ff35e15dbe8.png?s=3D40&amp;r=3Dpg&amp;d=3Didenticon" style=
=3D"max-width:694px">codinghorror:</div>
<blockquote><p style=3D"margin-top:0">I can&#39;t even find that topic by n=
@@ -193,12 +193,12 @@ uld be invisible to me, and not showing up in Suggested Topics.</p>
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/51d623f33f8b83095db84ff3=
+ <img src=3D"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff3=
5e15dbe8.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"codinghorror" st=
yle=3D"max-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/codinghorror" style=3D"f=
+ <a href=3D"https://meta.discourse.org/users/codinghorror" style=3D"f=
ont-size:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans=
-serif;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blan=
k">codinghorror</a><br>
@@ -219,12 +219,12 @@ rout" target=3D"_blank">@eviltrout</a>? I can&#39;t even find that topic by=
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
+ <img src=3D"https://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D=
"max-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/riking" style=3D"font-si=
+ <a href=3D"https://meta.discourse.org/users/riking" style=3D"font-si=
ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif=
;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik=
ing</a><br>
@@ -241,7 +241,7 @@ lar spam post, and it was promptly deleted/hidden, but it just popped up in=
<p style=3D"margin-top:0"></p>
<div><a href=3D"//cdn.discourse.org/uploads/meta_discourse/2158/50b8b49557c=
-b249e.png" target=3D"_blank"><img src=3D"http://cdn.discourse.org/uploads/m=
+b249e.png" target=3D"_blank"><img src=3D"https://cdn.discourse.org/uploads/m=
eta_discourse/_optimized/ab1/c92/acd2c33402_584x134.png" width=3D"584" heig=
ht=3D"134" style=3D"max-width:694px"><div>
@@ -257,12 +257,12 @@ ht=3D"134" style=3D"max-width:694px"><div>
<div style=3D"color:#666">
<p>To respond, reply to this email or visit <a href=3D"http://meta.discours=
e.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"color:=
-#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back-up-=
+#666" target=3D"_blank">https://meta.discourse.org/t/spam-post-pops-back-up-=
in-suggested-topics/11005/5</a> in your browser.</p>
</div>
<div style=3D"color:#666">
-<p>To unsubscribe from these emails, visit your <a href=3D"http://meta.disc=
+<p>To unsubscribe from these emails, visit your <a href=3D"https://meta.disc=
ourse.org/user_preferences" style=3D"color:#666" target=3D"_blank">user pre=
ferences</a>.</p>
</div>
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 5c5d53877a6..f7a4a7afced 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -100,7 +100,7 @@ describe ApplicationHelper do
end
it 'returns a generic avatar' do
- expect(helper.gravatar_icon(user_email)).to match('no_avatar.png')
+ expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
end
end
@@ -110,14 +110,14 @@ describe ApplicationHelper do
end
it 'returns a generic avatar when email is blank' do
- expect(helper.gravatar_icon('')).to match('no_avatar.png')
+ expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
end
it 'returns a valid Gravatar URL' do
stub_config_setting(https: false)
expect(helper.gravatar_icon(user_email))
- .to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
end
it 'uses HTTPs when configured' do
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index d601cbdb39b..7fa665aecdc 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -125,10 +125,10 @@ describe IssuablesHelper do
describe '#updated_at_by' do
let(:user) { create(:user) }
let(:unedited_issuable) { create(:issue) }
- let(:edited_issuable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) }
+ let(:edited_issuable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 1.day.ago, last_edited_at: 2.days.ago) }
let(:edited_updated_at_by) do
{
- updatedAt: edited_issuable.updated_at.to_time.iso8601,
+ updatedAt: edited_issuable.last_edited_at.to_time.iso8601,
updatedBy: {
name: user.name,
path: user_path(user)
@@ -142,7 +142,7 @@ describe IssuablesHelper do
context 'when updated by a deleted user' do
let(:edited_updated_at_by) do
{
- updatedAt: edited_issuable.updated_at.to_time.iso8601,
+ updatedAt: edited_issuable.last_edited_at.to_time.iso8601,
updatedBy: {
name: User.ghost.name,
path: user_path(User.ghost)
@@ -192,4 +192,33 @@ describe IssuablesHelper do
expect(JSON.parse(helper.issuable_initial_data(issue))).to eq(expected_data)
end
end
+
+ describe '#selected_labels' do
+ context 'if label_name param is a string' do
+ it 'returns a new label with title' do
+ allow(helper).to receive(:params)
+ .and_return(ActionController::Parameters.new(label_name: 'test label'))
+
+ labels = helper.selected_labels
+
+ expect(labels).to be_an(Array)
+ expect(labels.size).to eq(1)
+ expect(labels.first.title).to eq('test label')
+ end
+ end
+
+ context 'if label_name param is an array' do
+ it 'returns a new label with title for each element' do
+ allow(helper).to receive(:params)
+ .and_return(ActionController::Parameters.new(label_name: ['test label 1', 'test label 2']))
+
+ labels = helper.selected_labels
+
+ expect(labels).to be_an(Array)
+ expect(labels.size).to eq(2)
+ expect(labels.first.title).to eq('test label 1')
+ expect(labels.second.title).to eq('test label 2')
+ end
+ end
+ end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index ede9d232efd..c0251bf7dc0 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe ProjectsHelper do
+ include ProjectForksHelper
+
describe "#project_status_css_class" do
it "returns appropriate class" do
expect(project_status_css_class("started")).to eq("active")
@@ -10,9 +12,9 @@ describe ProjectsHelper do
end
describe "can_change_visibility_level?" do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project) }
let(:user) { create(:project_member, :reporter, user: create(:user), project: project).user }
- let(:fork_project) { Projects::ForkService.new(project, user).execute }
+ let(:forked_project) { fork_project(project, user) }
it "returns false if there are no appropriate permissions" do
allow(helper).to receive(:can?) { false }
@@ -26,21 +28,29 @@ describe ProjectsHelper do
expect(helper.can_change_visibility_level?(project, user)).to be_truthy
end
+ it 'allows visibility level to be changed if the project is forked' do
+ allow(helper).to receive(:can?).with(user, :change_visibility_level, project) { true }
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ fork_project(project)
+
+ expect(helper.can_change_visibility_level?(project, user)).to be_truthy
+ end
+
context "forks" do
it "returns false if there are permissions and origin project is PRIVATE" do
allow(helper).to receive(:can?) { true }
- project.update visibility_level: Gitlab::VisibilityLevel::PRIVATE
+ project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- expect(helper.can_change_visibility_level?(fork_project, user)).to be_falsey
+ expect(helper.can_change_visibility_level?(forked_project, user)).to be_falsey
end
it "returns true if there are permissions and origin project is INTERNAL" do
allow(helper).to receive(:can?) { true }
- project.update visibility_level: Gitlab::VisibilityLevel::INTERNAL
+ project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
- expect(helper.can_change_visibility_level?(fork_project, user)).to be_truthy
+ expect(helper.can_change_visibility_level?(forked_project, user)).to be_truthy
end
end
end
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
index a11824d0ac5..838ca9fabef 100644
--- a/spec/initializers/settings_spec.rb
+++ b/spec/initializers/settings_spec.rb
@@ -24,7 +24,7 @@ describe Settings do
expect(described_class.host_without_www('http://foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('http://www.foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com'
- expect(described_class.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+ expect(described_class.host_without_www('https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
expect(described_class.host_without_www('https://foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('https://www.foo.com')).to eq 'foo.com'
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index 2aa4fb1f6c6..cc5fa42aafe 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -262,9 +262,9 @@ describe('Api', () => {
it('fetches an issue template', (done) => {
const namespace = 'some namespace';
const project = 'some project';
- const templateKey = 'template key';
+ const templateKey = ' template #%?.key ';
const templateType = 'template type';
- const expectedUrl = `${dummyUrlRoot}/${namespace}/${project}/templates/${templateType}/${templateKey}`;
+ const expectedUrl = `${dummyUrlRoot}/${namespace}/${project}/templates/${templateType}/${encodeURIComponent(templateKey)}`;
spyOn(jQuery, 'ajax').and.callFake((request) => {
expect(request.url).toEqual(expectedUrl);
return sendDummyResponse();
diff --git a/spec/javascripts/behaviors/secret_values_spec.js b/spec/javascripts/behaviors/secret_values_spec.js
index 9eeae474e7d..38d9bba6868 100644
--- a/spec/javascripts/behaviors/secret_values_spec.js
+++ b/spec/javascripts/behaviors/secret_values_spec.js
@@ -1,16 +1,24 @@
import SecretValues from '~/behaviors/secret_values';
-function generateFixtureMarkup(secrets, isRevealed) {
+function generateValueMarkup(
+ secret,
+ valueClass = 'js-secret-value',
+ placeholderClass = 'js-secret-value-placeholder',
+) {
+ return `
+ <div class="${placeholderClass}">
+ ***
+ </div>
+ <div class="hide ${valueClass}">
+ ${secret}
+ </div>
+ `;
+}
+
+function generateFixtureMarkup(secrets, isRevealed, valueClass, placeholderClass) {
return `
<div class="js-secret-container">
- ${secrets.map(secret => `
- <div class="js-secret-value-placeholder">
- ***
- </div>
- <div class="hide js-secret-value">
- ${secret}
- </div>
- `).join('')}
+ ${secrets.map(secret => generateValueMarkup(secret, valueClass, placeholderClass)).join('')}
<button
class="js-secret-value-reveal-button"
data-secret-reveal-status="${isRevealed}"
@@ -21,11 +29,25 @@ function generateFixtureMarkup(secrets, isRevealed) {
`;
}
-function setupSecretFixture(secrets, isRevealed) {
+function setupSecretFixture(
+ secrets,
+ isRevealed,
+ valueClass = 'js-secret-value',
+ placeholderClass = 'js-secret-value-placeholder',
+) {
const wrapper = document.createElement('div');
- wrapper.innerHTML = generateFixtureMarkup(secrets, isRevealed);
-
- const secretValues = new SecretValues(wrapper.querySelector('.js-secret-container'));
+ wrapper.innerHTML = generateFixtureMarkup(
+ secrets,
+ isRevealed,
+ valueClass,
+ placeholderClass,
+ );
+
+ const secretValues = new SecretValues({
+ container: wrapper.querySelector('.js-secret-container'),
+ valueSelector: `.${valueClass}`,
+ placeholderSelector: `.${placeholderClass}`,
+ });
secretValues.init();
return wrapper;
@@ -49,7 +71,7 @@ describe('setupSecretValues', () => {
expect(revealButton.textContent).toEqual('Hide value');
});
- it('should value hidden initially', () => {
+ it('should have value hidden initially', () => {
const wrapper = setupSecretFixture(secrets, false);
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
@@ -143,4 +165,64 @@ describe('setupSecretValues', () => {
});
});
});
+
+ describe('with dynamic secrets', () => {
+ const secrets = ['mysecret123', 'happygoat456', 'tanuki789'];
+
+ it('should toggle values and placeholders', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ // Insert the new dynamic row
+ wrapper.querySelector('.js-secret-container').insertAdjacentHTML('afterbegin', generateValueMarkup('foobarbazdynamic'));
+
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+ const values = wrapper.querySelectorAll('.js-secret-value');
+ const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
+
+ revealButton.click();
+
+ expect(values.length).toEqual(4);
+ values.forEach((value) => {
+ expect(value.classList.contains('hide')).toEqual(false);
+ });
+ expect(placeholders.length).toEqual(4);
+ placeholders.forEach((placeholder) => {
+ expect(placeholder.classList.contains('hide')).toEqual(true);
+ });
+
+ revealButton.click();
+
+ expect(values.length).toEqual(4);
+ values.forEach((value) => {
+ expect(value.classList.contains('hide')).toEqual(true);
+ });
+ expect(placeholders.length).toEqual(4);
+ placeholders.forEach((placeholder) => {
+ expect(placeholder.classList.contains('hide')).toEqual(false);
+ });
+ });
+ });
+
+ describe('selector options', () => {
+ const secrets = ['mysecret123'];
+
+ it('should respect `valueSelector` and `placeholderSelector` options', () => {
+ const valueClass = 'js-some-custom-placeholder-selector';
+ const placeholderClass = 'js-some-custom-value-selector';
+
+ const wrapper = setupSecretFixture(secrets, false, valueClass, placeholderClass);
+ const values = wrapper.querySelectorAll(`.${valueClass}`);
+ const placeholders = wrapper.querySelectorAll(`.${placeholderClass}`);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+
+ expect(values.length).toEqual(1);
+ expect(placeholders.length).toEqual(1);
+
+ revealButton.click();
+
+ expect(values.length).toEqual(1);
+ expect(values[0].classList.contains('hide')).toEqual(false);
+ expect(placeholders.length).toEqual(1);
+ expect(placeholders[0].classList.contains('hide')).toEqual(true);
+ });
+ });
});
diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js
index df1b2c9960b..a143fc827d5 100644
--- a/spec/javascripts/blob/notebook/index_spec.js
+++ b/spec/javascripts/blob/notebook/index_spec.js
@@ -45,7 +45,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('does not show loading icon', () => {
@@ -96,7 +96,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('does not show loading icon', () => {
@@ -127,7 +127,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('does not show loading icon', () => {
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 4e73fa1fe87..80a598e63bd 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -55,7 +55,7 @@ describe('Board card', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('returns false when detailIssue is empty', () => {
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index b7cc3a8813e..a5fcb10b9dd 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -60,7 +60,7 @@ describe('Board list component', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('renders component', () => {
@@ -184,9 +184,9 @@ describe('Board list component', () => {
component.$refs.list.style.height = '100px';
component.$refs.list.style.overflow = 'scroll';
- for (let i = 0; i < 19; i += 1) {
- const issue = component.list.issues[0];
- issue.id += 1;
+ for (let i = 1; i < 20; i += 1) {
+ const issue = Object.assign({}, component.list.issues[0]);
+ issue.id += i;
component.list.issues.push(issue);
}
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index c62c537841c..e204985f039 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -58,7 +58,7 @@ describe('Issue boards new issue form', () => {
afterEach(() => {
vm.$destroy();
- mock.reset();
+ mock.restore();
});
it('calls submit if submit button is clicked', (done) => {
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 49fb20f4c84..8411f4dd8a6 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -35,7 +35,7 @@ describe('Store', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('starts with a blank state', () => {
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index e5e7b48228b..34964b20b05 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -30,7 +30,7 @@ describe('List model', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('gets issues when created', (done) => {
diff --git a/spec/javascripts/boards/utils/query_data_spec.js b/spec/javascripts/boards/utils/query_data_spec.js
new file mode 100644
index 00000000000..922215ffc1d
--- /dev/null
+++ b/spec/javascripts/boards/utils/query_data_spec.js
@@ -0,0 +1,27 @@
+import queryData from '~/boards/utils/query_data';
+
+describe('queryData', () => {
+ it('parses path for label with trailing +', () => {
+ expect(
+ queryData('label_name[]=label%2B', {}),
+ ).toEqual({
+ label_name: ['label+'],
+ });
+ });
+
+ it('parses path for milestone with trailing +', () => {
+ expect(
+ queryData('milestone_title=A%2B', {}),
+ ).toEqual({
+ milestone_title: 'A+',
+ });
+ });
+
+ it('parses path for search terms with spaces', () => {
+ expect(
+ queryData('search=two+words', {}),
+ ).toEqual({
+ search: 'two words',
+ });
+ });
+});
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index f5be9ea0fb2..7b38f6b7855 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -23,16 +23,24 @@ describe('Clusters', () => {
});
describe('toggle', () => {
- it('should update the button and the input field on click', () => {
- cluster.toggleButton.click();
+ it('should update the button and the input field on click', (done) => {
+ const toggleButton = document.querySelector('.js-cluster-enable-toggle-area .js-project-feature-toggle');
+ const toggleInput = document.querySelector('.js-cluster-enable-toggle-area .js-project-feature-toggle-input');
- expect(
- cluster.toggleButton.classList,
- ).not.toContain('is-checked');
+ toggleButton.click();
- expect(
- cluster.toggleInput.getAttribute('value'),
- ).toEqual('false');
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(
+ toggleButton.classList,
+ ).not.toContain('is-checked');
+
+ expect(
+ toggleInput.getAttribute('value'),
+ ).toEqual('false');
+ })
+ .then(done)
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/clusters/clusters_index_spec.js b/spec/javascripts/clusters/clusters_index_spec.js
deleted file mode 100644
index 0a8b63ed5b4..00000000000
--- a/spec/javascripts/clusters/clusters_index_spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import setClusterTableToggles from '~/clusters/clusters_index';
-import { setTimeout } from 'core-js/library/web/timers';
-
-describe('Clusters table', () => {
- preloadFixtures('clusters/index_cluster.html.raw');
- let mock;
-
- beforeEach(() => {
- loadFixtures('clusters/index_cluster.html.raw');
- mock = new MockAdapter(axios);
- setClusterTableToggles();
- });
-
- describe('update cluster', () => {
- it('renders loading state while request is made', () => {
- const button = document.querySelector('.js-toggle-cluster-list');
-
- button.click();
-
- expect(button.classList).toContain('is-loading');
- expect(button.getAttribute('disabled')).toEqual('true');
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('shows updated state after sucessfull request', (done) => {
- mock.onPut().reply(200, {}, {});
- const button = document.querySelector('.js-toggle-cluster-list');
- button.click();
-
- expect(button.classList).toContain('is-loading');
-
- setTimeout(() => {
- expect(button.classList).not.toContain('is-loading');
- expect(button.classList).not.toContain('is-checked');
- done();
- }, 0);
- });
-
- it('shows inital state after failed request', (done) => {
- mock.onPut().reply(500, {}, {});
- const button = document.querySelector('.js-toggle-cluster-list');
-
- button.click();
- expect(button.classList).toContain('is-loading');
-
- setTimeout(() => {
- expect(button.classList).not.toContain('is-loading');
- expect(button.classList).toContain('is-checked');
- done();
- }, 0);
- });
- });
-});
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index d62c2966a8b..0afe09d87bc 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -10,9 +10,10 @@ describe('Pipelines table in Commits and Merge requests', () => {
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- PipelinesTable = Vue.extend(pipelinesTable);
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
- pipeline = pipelines.find(p => p.id === 1);
+
+ PipelinesTable = Vue.extend(pipelinesTable);
+ pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
});
describe('successful request', () => {
diff --git a/spec/javascripts/create_item_dropdown_spec.js b/spec/javascripts/create_item_dropdown_spec.js
new file mode 100644
index 00000000000..c8b00a4f553
--- /dev/null
+++ b/spec/javascripts/create_item_dropdown_spec.js
@@ -0,0 +1,106 @@
+import CreateItemDropdown from '~/create_item_dropdown';
+
+const DROPDOWN_ITEM_DATA = [{
+ title: 'one',
+ id: 'one',
+ text: 'one',
+}, {
+ title: 'two',
+ id: 'two',
+ text: 'two',
+}, {
+ title: 'three',
+ id: 'three',
+ text: 'three',
+}];
+
+describe('CreateItemDropdown', () => {
+ preloadFixtures('static/create_item_dropdown.html.raw');
+
+ let $wrapperEl;
+
+ beforeEach(() => {
+ loadFixtures('static/create_item_dropdown.html.raw');
+ $wrapperEl = $('.js-create-item-dropdown-fixture-root');
+
+ // eslint-disable-next-line no-new
+ new CreateItemDropdown({
+ $dropdown: $wrapperEl.find('.js-dropdown-menu-toggle'),
+ defaultToggleLabel: 'All variables',
+ fieldName: 'variable[environment]',
+ getData: (term, callback) => {
+ callback(DROPDOWN_ITEM_DATA);
+ },
+ });
+ });
+
+ afterEach(() => {
+ $wrapperEl.remove();
+ });
+
+ it('should have a dropdown item for each piece of data', () => {
+ // Get the data in the dropdown
+ $('.js-dropdown-menu-toggle').click();
+
+ const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+ expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length);
+ });
+
+ describe('created items', () => {
+ const NEW_ITEM_TEXT = 'foobarbaz';
+
+ function createItemAndClearInput(text) {
+ // Filter for the new item
+ $wrapperEl.find('.dropdown-input-field')
+ .val(text)
+ .trigger('input');
+
+ // Create the new item
+ const $createButton = $wrapperEl.find('.js-dropdown-create-new-item');
+ $createButton.click();
+
+ // Clear out the filter
+ $wrapperEl.find('.dropdown-input-field')
+ .val('')
+ .trigger('input');
+ }
+
+ beforeEach(() => {
+ // Open the dropdown
+ $('.js-dropdown-menu-toggle').click();
+
+ // Filter for the new item
+ $wrapperEl.find('.dropdown-input-field')
+ .val(NEW_ITEM_TEXT)
+ .trigger('input');
+ });
+
+ it('create new item button should include the filter text', () => {
+ expect($wrapperEl.find('.js-dropdown-create-new-item code').text()).toEqual(NEW_ITEM_TEXT);
+ });
+
+ it('should update the dropdown with the newly created item', () => {
+ // Create the new item
+ const $createButton = $wrapperEl.find('.js-dropdown-create-new-item');
+ $createButton.click();
+
+ expect($wrapperEl.find('.dropdown-toggle-text').text()).toEqual(NEW_ITEM_TEXT);
+ expect($wrapperEl.find('input[name="variable[environment]"]').val()).toEqual(NEW_ITEM_TEXT);
+ });
+
+ it('should include newly created item in dropdown list', () => {
+ createItemAndClearInput(NEW_ITEM_TEXT);
+
+ const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+ expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length);
+ expect($($itemEls.get(DROPDOWN_ITEM_DATA.length)).text()).toEqual(NEW_ITEM_TEXT);
+ });
+
+ it('should not duplicate an item when trying to create an existing item', () => {
+ createItemAndClearInput(DROPDOWN_ITEM_DATA[0].text);
+
+ const $itemEls = $wrapperEl.find('.js-dropdown-content a');
+ expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length);
+ });
+ });
+});
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index 2f28c5bbf01..b7aadf604a4 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -53,18 +53,24 @@ describe('Deploy keys key', () => {
).toBe('Remove');
});
- it('shows write access text when key has write access', (done) => {
- vm.deployKey.can_push = true;
+ it('shows write access title when key has write access', (done) => {
+ vm.deployKey.deploy_keys_projects[0].can_push = true;
Vue.nextTick(() => {
expect(
- vm.$el.querySelector('.write-access-allowed'),
- ).not.toBeNull();
-
- expect(
- vm.$el.querySelector('.write-access-allowed').textContent.trim(),
+ vm.$el.querySelector('.deploy-project-label').getAttribute('data-original-title'),
).toBe('Write access allowed');
+ done();
+ });
+ });
+
+ it('does not show write access title when key has write access', (done) => {
+ vm.deployKey.deploy_keys_projects[0].can_push = false;
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector('.deploy-project-label').getAttribute('data-original-title'),
+ ).toBe('Read access only');
done();
});
});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 0e141adb628..7a34126eef7 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -68,7 +68,7 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit: {
@@ -84,7 +84,7 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
diff --git a/spec/javascripts/fixtures/clusters.rb b/spec/javascripts/fixtures/clusters.rb
index d26ea3febe8..8e74c4f859c 100644
--- a/spec/javascripts/fixtures/clusters.rb
+++ b/spec/javascripts/fixtures/clusters.rb
@@ -31,19 +31,4 @@ describe Projects::ClustersController, '(JavaScript fixtures)', type: :controlle
expect(response).to be_success
store_frontend_fixture(response, example.description)
end
-
- context 'rendering non-empty state' do
- before do
- cluster
- end
-
- it 'clusters/index_cluster.html.raw' do |example|
- get :index,
- namespace_id: namespace,
- project_id: project
-
- expect(response).to be_success
- store_frontend_fixture(response, example.description)
- end
- end
end
diff --git a/spec/javascripts/fixtures/create_item_dropdown.html.haml b/spec/javascripts/fixtures/create_item_dropdown.html.haml
new file mode 100644
index 00000000000..d4d91b93caf
--- /dev/null
+++ b/spec/javascripts/fixtures/create_item_dropdown.html.haml
@@ -0,0 +1,13 @@
+.js-create-item-dropdown-fixture-root
+ %input{ name: 'variable[environment]', type: 'hidden' }
+ = dropdown_tag('some label',
+ options: { toggle_class: 'js-dropdown-menu-toggle',
+ content_class: 'js-dropdown-content',
+ filter: true,
+ dropdown_class: "dropdown-menu-selectable",
+ footer_content: true }) do
+ %ul.dropdown-footer-list
+ %li
+ %button{ class: "dropdown-create-new-item-button js-dropdown-create-new-item" }
+ Create wildcard
+ %code
diff --git a/spec/javascripts/fixtures/projects.json b/spec/javascripts/fixtures/projects.json
index 1339ee00870..68a150f602a 100644
--- a/spec/javascripts/fixtures/projects.json
+++ b/spec/javascripts/fixtures/projects.json
@@ -14,7 +14,7 @@
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url": "http://localhost:3000/u/root"
},
"name": "test",
diff --git a/spec/javascripts/fixtures/search.rb b/spec/javascripts/fixtures/search.rb
new file mode 100644
index 00000000000..703cd3d49fa
--- /dev/null
+++ b/spec/javascripts/fixtures/search.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe SearchController, '(JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('search/')
+ end
+
+ it 'search/show.html.raw' do |example|
+ get :show
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js
index 97e3ab682c5..7198dbd4cf2 100644
--- a/spec/javascripts/flash_spec.js
+++ b/spec/javascripts/flash_spec.js
@@ -183,11 +183,15 @@ describe('Flash', () => {
});
it('adds flash element into container', () => {
- flash('test');
+ flash('test', 'alert', document, null, false, true);
expect(
document.querySelector('.flash-alert'),
).not.toBeNull();
+
+ expect(
+ document.body.className,
+ ).toContain('flash-shown');
});
it('adds flash into specified parent', () => {
@@ -220,13 +224,17 @@ describe('Flash', () => {
});
it('removes element after clicking', () => {
- flash('test', 'alert', document, null, false);
+ flash('test', 'alert', document, null, false, true);
document.querySelector('.flash-alert').click();
expect(
document.querySelector('.flash-alert'),
).toBeNull();
+
+ expect(
+ document.body.className,
+ ).not.toContain('flash-shown');
});
describe('with actionConfig', () => {
diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js
index a3fa07d5bc2..eb9330f5e5b 100644
--- a/spec/javascripts/fly_out_nav_spec.js
+++ b/spec/javascripts/fly_out_nav_spec.js
@@ -167,30 +167,26 @@ describe('Fly out sidebar navigation', () => {
describe('mouseEnterTopItems', () => {
beforeEach(() => {
- jasmine.clock().install();
-
el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute; top: 0; left: 100px; height: 200px;"></div>';
});
- afterEach(() => {
- jasmine.clock().uninstall();
- });
-
- it('shows sub-items after 0ms if no menu is open', () => {
+ it('shows sub-items after 0ms if no menu is open', (done) => {
mouseEnterTopItems(el);
expect(
getHideSubItemsInterval(),
).toBe(0);
- jasmine.clock().tick(0);
+ setTimeout(() => {
+ expect(
+ el.querySelector('.sidebar-sub-level-items').style.display,
+ ).toBe('block');
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).toBe('block');
+ done();
+ });
});
- it('shows sub-items after 300ms if a menu is currently open', () => {
+ it('shows sub-items after 300ms if a menu is currently open', (done) => {
documentMouseMove({
clientX: el.getBoundingClientRect().left,
clientY: el.getBoundingClientRect().top,
@@ -203,17 +199,19 @@ describe('Fly out sidebar navigation', () => {
clientY: el.getBoundingClientRect().top + 10,
});
- mouseEnterTopItems(el);
+ mouseEnterTopItems(el, 0);
expect(
getHideSubItemsInterval(),
).toBe(300);
- jasmine.clock().tick(300);
+ setTimeout(() => {
+ expect(
+ el.querySelector('.sidebar-sub-level-items').style.display,
+ ).toBe('block');
- expect(
- el.querySelector('.sidebar-sub-level-items').style.display,
- ).toBe('block');
+ done();
+ });
});
});
diff --git a/spec/javascripts/helpers/class_spec_helper_spec.js b/spec/javascripts/helpers/class_spec_helper_spec.js
index 686b8eaed31..1415ffb7eb3 100644
--- a/spec/javascripts/helpers/class_spec_helper_spec.js
+++ b/spec/javascripts/helpers/class_spec_helper_spec.js
@@ -3,7 +3,7 @@
import './class_spec_helper';
describe('ClassSpecHelper', () => {
- describe('itShouldBeAStaticMethod', function () {
+ describe('itShouldBeAStaticMethod', () => {
beforeEach(() => {
class TestClass {
instanceMethod() { this.prop = 'val'; }
@@ -14,23 +14,5 @@ describe('ClassSpecHelper', () => {
});
ClassSpecHelper.itShouldBeAStaticMethod(ClassSpecHelper, 'itShouldBeAStaticMethod');
-
- it('should have a defined spec', () => {
- expect(ClassSpecHelper.itShouldBeAStaticMethod(this.TestClass, 'staticMethod').description).toBe('should be a static method');
- });
-
- it('should pass for a static method', () => {
- const spec = ClassSpecHelper.itShouldBeAStaticMethod(this.TestClass, 'staticMethod');
- expect(spec.status()).toBe('passed');
- });
-
- it('should fail for an instance method', (done) => {
- const spec = ClassSpecHelper.itShouldBeAStaticMethod(this.TestClass, 'instanceMethod');
- spec.resultCallback = (result) => {
- expect(result.status).toBe('failed');
- done();
- };
- spec.execute();
- });
});
});
diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js
index a9783ea065c..323fee3767e 100644
--- a/spec/javascripts/helpers/user_mock_data_helper.js
+++ b/spec/javascripts/helpers/user_mock_data_helper.js
@@ -4,7 +4,7 @@ export default {
for (let i = 0; i < numberUsers; i = i += 1) {
users.push(
{
- avatar: 'http://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: (i + 1),
name: `GitLab User ${i}`,
username: `gitlab${i}`,
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 1454ca52018..1c9f48028f2 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -59,7 +59,7 @@ describe('Issuable output', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
realtimeRequestCount = 0;
vm.poll.stop();
@@ -218,6 +218,39 @@ describe('Issuable output', () => {
});
});
+ describe('shows dialog when issue has unsaved changed', () => {
+ it('confirms on title change', (done) => {
+ vm.showForm = true;
+ vm.state.titleText = 'title has changed';
+ const e = { returnValue: null };
+ vm.handleBeforeUnloadEvent(e);
+ Vue.nextTick(() => {
+ expect(e.returnValue).not.toBeNull();
+ done();
+ });
+ });
+
+ it('confirms on description change', (done) => {
+ vm.showForm = true;
+ vm.state.descriptionText = 'description has changed';
+ const e = { returnValue: null };
+ vm.handleBeforeUnloadEvent(e);
+ Vue.nextTick(() => {
+ expect(e.returnValue).not.toBeNull();
+ done();
+ });
+ });
+
+ it('does nothing when nothing has changed', (done) => {
+ const e = { returnValue: null };
+ vm.handleBeforeUnloadEvent(e);
+ Vue.nextTick(() => {
+ expect(e.returnValue).toBeNull();
+ done();
+ });
+ });
+ });
+
describe('error when updating', () => {
beforeEach(() => {
spyOn(window, 'Flash').and.callThrough();
diff --git a/spec/javascripts/jobs/job_details_mediator_spec.js b/spec/javascripts/jobs/job_details_mediator_spec.js
index 3069a0cd60e..ca5c9cf87e4 100644
--- a/spec/javascripts/jobs/job_details_mediator_spec.js
+++ b/spec/javascripts/jobs/job_details_mediator_spec.js
@@ -12,6 +12,10 @@ describe('JobMediator', () => {
mock = new MockAdapter(axios);
});
+ afterEach(() => {
+ mock.restore();
+ });
+
it('should set defaults', () => {
expect(mediator.store).toBeDefined();
expect(mediator.service).toBeDefined();
@@ -24,10 +28,6 @@ describe('JobMediator', () => {
mock.onGet().reply(200, job, {});
});
- afterEach(() => {
- mock.restore();
- });
-
it('should store received data', (done) => {
mediator.fetchJob();
setTimeout(() => {
diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js
index 43532275121..43589d54be4 100644
--- a/spec/javascripts/jobs/mock_data.js
+++ b/spec/javascripts/jobs/mock_data.js
@@ -37,7 +37,7 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
erase_path: '/root/ci-mock/-/jobs/4757/erase',
@@ -54,7 +54,7 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
active: false,
@@ -107,10 +107,10 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://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',
+ author_gravatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url: 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
},
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index 6f8dad6b835..69a23d7b2f3 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -63,13 +63,13 @@ describe('text_utility', () => {
});
});
- describe('stripeHtml', () => {
+ describe('stripHtml', () => {
it('replaces html tag with the default replacement', () => {
- expect(textUtils.stripeHtml('This is a text with <p>html</p>.')).toEqual('This is a text with html.');
+ expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual('This is a text with html.');
});
it('replaces html tags with the provided replacement', () => {
- expect(textUtils.stripeHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
+ expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
});
});
});
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 9885b8a790f..eb8f6bbe50d 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -38,7 +38,7 @@ describe('Dashboard', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('shows up a loading state', (done) => {
diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js
index a88e9ed3d99..02304bf5d7d 100644
--- a/spec/javascripts/notebook/cells/markdown_spec.js
+++ b/spec/javascripts/notebook/cells/markdown_spec.js
@@ -42,6 +42,18 @@ describe('Markdown component', () => {
expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
});
+ it('sanitizes output', (done) => {
+ Object.assign(cell, {
+ source: ['[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n'],
+ });
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('a')).toBeNull();
+
+ done();
+ });
+ });
+
describe('katex', () => {
beforeEach(() => {
json = getJSONFixture('blob/notebook/math.json');
diff --git a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js
new file mode 100644
index 00000000000..d587573fc9e
--- /dev/null
+++ b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js
@@ -0,0 +1,66 @@
+export default {
+ 'protocol-based JS injection: simple, no spaces': {
+ input: '<a href="javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: simple, spaces before': {
+ input: '<a href="javascript :alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: simple, spaces after': {
+ input: '<a href="javascript: alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: simple, spaces before and after': {
+ input: '<a href="javascript : alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: preceding colon': {
+ input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: UTF-8 encoding': {
+ input: '<a href="javascript&#58;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: long UTF-8 encoding': {
+ input: '<a href="javascript&#0058;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: long UTF-8 encoding without semicolons': {
+ input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: hex encoding': {
+ input: '<a href="javascript&#x3A;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: long hex encoding': {
+ input: '<a href="javascript&#x003A;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: hex encoding without semicolons': {
+ input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: null char': {
+ input: '<a href=java\0script:alert("XSS")>foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: invalid URL char': {
+ input: '<img src=java\script:alert("XSS")>', // eslint-disable-line no-useless-escape
+ output: '<img>',
+ },
+ 'protocol-based JS injection: Unicode': {
+ input: '<a href="\u0001java\u0003script:alert(\'XSS\')">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: spaces and entities': {
+ input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'img on error': {
+ input: '<img src="x" onerror="alert(document.domain)" />',
+ output: '<img src="x">',
+ },
+};
diff --git a/spec/javascripts/notebook/cells/output/html_spec.js b/spec/javascripts/notebook/cells/output/html_spec.js
new file mode 100644
index 00000000000..9c5385f2922
--- /dev/null
+++ b/spec/javascripts/notebook/cells/output/html_spec.js
@@ -0,0 +1,29 @@
+import Vue from 'vue';
+import htmlOutput from '~/notebook/cells/output/html.vue';
+import sanitizeTests from './html_sanitize_tests';
+
+describe('html output cell', () => {
+ function createComponent(rawCode) {
+ const Component = Vue.extend(htmlOutput);
+
+ return new Component({
+ propsData: {
+ rawCode,
+ },
+ }).$mount();
+ }
+
+ describe('sanitizes output', () => {
+ Object.keys(sanitizeTests).forEach((key) => {
+ it(key, () => {
+ const test = sanitizeTests[key];
+ const vm = createComponent(test.input);
+ const outputEl = [...vm.$el.querySelectorAll('div')].pop();
+
+ expect(outputEl.innerHTML).toEqual(test.output);
+
+ vm.$destroy();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index b020a1020df..f0c800c759d 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -107,7 +107,7 @@ export const note = {
"name": "Administrator",
"username": "root",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"path": "/root"
},
"created_at": "2017-08-10T15:24:03.087Z",
diff --git a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
new file mode 100644
index 00000000000..440a6585d57
--- /dev/null
+++ b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
@@ -0,0 +1,63 @@
+import Vue from 'vue';
+
+import axios from '~/lib/utils/axios_utils';
+import stopJobsModal from '~/pages/admin/jobs/index/components/stop_jobs_modal.vue';
+import * as urlUtility from '~/lib/utils/url_utility';
+
+import mountComponent from '../../../../../helpers/vue_mount_component_helper';
+
+describe('stop_jobs_modal.vue', () => {
+ const props = {
+ url: `${gl.TEST_HOST}/stop_jobs_modal.vue/stopAll`,
+ };
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ beforeEach(() => {
+ const Component = Vue.extend(stopJobsModal);
+ vm = mountComponent(Component, props);
+ });
+
+ describe('onSubmit', () => {
+ it('stops jobs and redirects to overview page', (done) => {
+ const responseURL = `${gl.TEST_HOST}/stop_jobs_modal.vue/jobs`;
+ const redirectSpy = spyOn(urlUtility, 'redirectTo');
+ spyOn(axios, 'post').and.callFake((url) => {
+ expect(url).toBe(props.url);
+ return Promise.resolve({
+ request: {
+ responseURL,
+ },
+ });
+ });
+
+ vm.onSubmit()
+ .then(() => {
+ expect(redirectSpy).toHaveBeenCalledWith(responseURL);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays error if stopping jobs failed', (done) => {
+ const dummyError = new Error('stopping jobs failed');
+ const redirectSpy = spyOn(urlUtility, 'redirectTo');
+ spyOn(axios, 'post').and.callFake((url) => {
+ expect(url).toBe(props.url);
+ return Promise.reject(dummyError);
+ });
+
+ vm.onSubmit()
+ .then(done.fail)
+ .catch((error) => {
+ expect(error).toBe(dummyError);
+ expect(redirectSpy).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
new file mode 100644
index 00000000000..3cd33a3e900
--- /dev/null
+++ b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
@@ -0,0 +1,95 @@
+import Vue from 'vue';
+
+import axios from '~/lib/utils/axios_utils';
+import deleteMilestoneModal from '~/pages/milestones/shared/components/delete_milestone_modal.vue';
+import eventHub from '~/pages/milestones/shared/event_hub';
+import * as urlUtility from '~/lib/utils/url_utility';
+
+import mountComponent from '../../../../helpers/vue_mount_component_helper';
+
+describe('delete_milestone_modal.vue', () => {
+ const Component = Vue.extend(deleteMilestoneModal);
+ const props = {
+ issueCount: 1,
+ mergeRequestCount: 2,
+ milestoneId: 3,
+ milestoneTitle: 'my milestone title',
+ milestoneUrl: `${gl.TEST_HOST}/delete_milestone_modal.vue/milestone`,
+ };
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('onSubmit', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ spyOn(eventHub, '$emit');
+ });
+
+ it('deletes milestone and redirects to overview page', (done) => {
+ const responseURL = `${gl.TEST_HOST}/delete_milestone_modal.vue/milestoneOverview`;
+ spyOn(axios, 'delete').and.callFake((url) => {
+ expect(url).toBe(props.milestoneUrl);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestStarted', props.milestoneUrl);
+ eventHub.$emit.calls.reset();
+ return Promise.resolve({
+ request: {
+ responseURL,
+ },
+ });
+ });
+ const redirectSpy = spyOn(urlUtility, 'redirectTo');
+
+ vm.onSubmit()
+ .then(() => {
+ expect(redirectSpy).toHaveBeenCalledWith(responseURL);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { milestoneUrl: props.milestoneUrl, successful: true });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays error if deleting milestone failed', (done) => {
+ const dummyError = new Error('deleting milestone failed');
+ dummyError.response = { status: 418 };
+ spyOn(axios, 'delete').and.callFake((url) => {
+ expect(url).toBe(props.milestoneUrl);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestStarted', props.milestoneUrl);
+ eventHub.$emit.calls.reset();
+ return Promise.reject(dummyError);
+ });
+ const redirectSpy = spyOn(urlUtility, 'redirectTo');
+
+ vm.onSubmit()
+ .catch((error) => {
+ expect(error).toBe(dummyError);
+ expect(redirectSpy).not.toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { milestoneUrl: props.milestoneUrl, successful: false });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('text', () => {
+ it('contains the issue and milestone count', () => {
+ vm = mountComponent(Component, props);
+ const value = vm.text;
+
+ expect(value).toContain('remove it from 1 issue and 2 merge requests');
+ });
+
+ it('contains neither issue nor milestone count', () => {
+ vm = mountComponent(Component, { ...props,
+ issueCount: 0,
+ mergeRequestCount: 0,
+ });
+
+ const value = vm.text;
+
+ expect(value).toContain('is not currently used');
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index 35e36e9c353..c3dc7b53d0f 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -61,14 +61,14 @@ describe('pipeline graph job component', () => {
it('it should render status and name', () => {
component = mountComponent(JobComponent, {
job: {
- id: 4256,
+ id: 4257,
name: 'test',
status: {
icon: 'icon_status_success',
text: 'passed',
label: 'passed',
group: 'success',
- details_path: '/root/ci-mock/builds/4256',
+ details_path: '/root/ci-mock/builds/4257',
has_details: false,
},
},
@@ -118,7 +118,7 @@ describe('pipeline graph job component', () => {
it('should not render status label when it is not provided', () => {
component = mountComponent(JobComponent, {
job: {
- id: 4256,
+ id: 4258,
name: 'test',
status: {
icon: 'icon_status_success',
@@ -132,7 +132,7 @@ describe('pipeline graph job component', () => {
it('should not render status label when it is provided', () => {
component = mountComponent(JobComponent, {
job: {
- id: 4256,
+ id: 4259,
name: 'test',
status: {
icon: 'icon_status_success',
diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
index 063ab53681b..f744f1af5e6 100644
--- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js
+++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
@@ -4,18 +4,18 @@ import stageColumnComponent from '~/pipelines/components/graph/stage_column_comp
describe('stage column component', () => {
let component;
const mockJob = {
- id: 4256,
+ id: 4250,
name: 'test',
status: {
icon: 'icon_status_success',
text: 'passed',
label: 'passed',
group: 'success',
- details_path: '/root/ci-mock/builds/4256',
+ details_path: '/root/ci-mock/builds/4250',
action: {
icon: 'retry',
title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
+ path: '/root/ci-mock/builds/4250/retry',
method: 'post',
},
},
@@ -24,10 +24,17 @@ describe('stage column component', () => {
beforeEach(() => {
const StageColumnComponent = Vue.extend(stageColumnComponent);
+ const mockJobs = [];
+ for (let i = 0; i < 3; i += 1) {
+ const mockedJob = Object.assign({}, mockJob);
+ mockedJob.id += i;
+ mockJobs.push(mockedJob);
+ }
+
component = new StageColumnComponent({
propsData: {
title: 'foo',
- jobs: [mockJob, mockJob, mockJob],
+ jobs: mockJobs,
},
}).$mount();
});
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index a9126d2f4e9..de744739e42 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -24,9 +24,10 @@ describe('Pipelines Table Row', () => {
beforeEach(() => {
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
- pipeline = pipelines.find(p => p.id === 1);
- pipelineWithoutAuthor = pipelines.find(p => p.id === 2);
- pipelineWithoutCommit = pipelines.find(p => p.id === 3);
+
+ pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
+ pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null);
+ pipelineWithoutCommit = pipelines.find(p => p.user === null && p.commit === null);
});
afterEach(() => {
diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js
index ca2f9163313..4fc3c08145e 100644
--- a/spec/javascripts/pipelines/pipelines_table_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_spec.js
@@ -11,9 +11,10 @@ describe('Pipelines Table', () => {
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- PipelinesTableComponent = Vue.extend(pipelinesTableComp);
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
- pipeline = pipelines.find(p => p.id === 1);
+
+ PipelinesTableComponent = Vue.extend(pipelinesTableComp);
+ pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
});
describe('table', () => {
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
index c4d3866c922..debde1bb357 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
@@ -12,7 +12,7 @@ describe('Multi-file editor commit sidebar list collapsed', () => {
vm = createComponentWithStore(Component, store);
- vm.$store.state.openFiles.push(file(), file());
+ vm.$store.state.openFiles.push(file('file1'), file('file2'));
vm.$store.state.openFiles[0].tempFile = true;
vm.$store.state.openFiles.forEach((f) => {
Object.assign(f, {
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
index fc7c9ae9dd7..4b20fdf70d6 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
@@ -10,7 +10,7 @@ describe('Multi-file editor commit sidebar list item', () => {
beforeEach(() => {
const Component = Vue.extend(listItem);
- f = file();
+ f = file('test-file');
vm = mountComponent(Component, {
file: f,
diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js
index cd93fb3ccbf..676ac09f2c9 100644
--- a/spec/javascripts/repo/components/repo_commit_section_spec.js
+++ b/spec/javascripts/repo/components/repo_commit_section_spec.js
@@ -29,7 +29,7 @@ describe('RepoCommitSection', () => {
comp.$store.state.rightPanelCollapsed = false;
comp.$store.state.currentBranch = 'master';
- comp.$store.state.openFiles = [file(), file()];
+ comp.$store.state.openFiles = [file('file1'), file('file2')];
comp.$store.state.openFiles.forEach(f => Object.assign(f, {
changed: true,
content: 'testing',
diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js
index 0810da87e80..27b55ed1f87 100644
--- a/spec/javascripts/repo/components/repo_file_spec.js
+++ b/spec/javascripts/repo/components/repo_file_spec.js
@@ -25,7 +25,7 @@ describe('RepoFile', () => {
vm = new RepoFile({
store,
propsData: {
- file: file(),
+ file: file('t4'),
},
});
spyOn(vm, 'timeFormated').and.returnValue(updated);
@@ -39,7 +39,7 @@ describe('RepoFile', () => {
it('does render if hasFiles is true and is loading tree', () => {
vm = createComponent({
- file: file(),
+ file: file('t1'),
});
expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy();
@@ -47,7 +47,7 @@ describe('RepoFile', () => {
it('does not render commit message and datetime if mini', (done) => {
vm = createComponent({
- file: file(),
+ file: file('t2'),
});
vm.$store.state.openFiles.push(vm.file);
@@ -61,7 +61,7 @@ describe('RepoFile', () => {
it('fires clickFile when the link is clicked', () => {
vm = createComponent({
- file: file(),
+ file: file('t3'),
});
spyOn(vm, 'clickFile');
diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js
index 507bca983df..933e8d3a06a 100644
--- a/spec/javascripts/repo/components/repo_tab_spec.js
+++ b/spec/javascripts/repo/components/repo_tab_spec.js
@@ -56,7 +56,7 @@ describe('RepoTab', () => {
});
it('renders an fa-circle icon if tab is changed', () => {
- const tab = file();
+ const tab = file('changedFile');
tab.changed = true;
vm = createComponent({
tab,
@@ -68,7 +68,7 @@ describe('RepoTab', () => {
describe('methods', () => {
describe('closeTab', () => {
it('does not close tab if is changed', (done) => {
- const tab = file();
+ const tab = file('closeFile');
tab.changed = true;
tab.opened = true;
vm = createComponent({
diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js
index 0beaf643793..2c363364d70 100644
--- a/spec/javascripts/repo/components/repo_tabs_spec.js
+++ b/spec/javascripts/repo/components/repo_tabs_spec.js
@@ -4,7 +4,7 @@ import repoTabs from '~/ide/components/repo_tabs.vue';
import { file, resetStore } from '../helpers';
describe('RepoTabs', () => {
- const openedFiles = [file(), file()];
+ const openedFiles = [file('open1'), file('open2')];
let vm;
function createComponent() {
diff --git a/spec/javascripts/repo/stores/actions/file_spec.js b/spec/javascripts/repo/stores/actions/file_spec.js
index 8ce01d3bf12..e2d8f002e27 100644
--- a/spec/javascripts/repo/stores/actions/file_spec.js
+++ b/spec/javascripts/repo/stores/actions/file_spec.js
@@ -18,7 +18,7 @@ describe('Multi-file store file actions', () => {
oldGetLastCommitData = store._actions.getLastCommitData; // eslint-disable-line
store._actions.getLastCommitData = [getLastCommitDataSpy]; // eslint-disable-line
- localFile = file();
+ localFile = file('testFile');
localFile.active = true;
localFile.opened = true;
localFile.parentTreeUrl = 'parentTreeUrl';
@@ -81,7 +81,7 @@ describe('Multi-file store file actions', () => {
});
it('sets next file as active', (done) => {
- const f = file();
+ const f = file('otherfile');
store.state.openFiles.push(f);
expect(f.active).toBeFalsy();
@@ -119,7 +119,7 @@ describe('Multi-file store file actions', () => {
});
it('calls scrollToTab', (done) => {
- store.dispatch('setFileActive', file())
+ store.dispatch('setFileActive', file('setThisActive'))
.then(() => {
expect(scrollToTabSpy).toHaveBeenCalled();
@@ -128,7 +128,7 @@ describe('Multi-file store file actions', () => {
});
it('sets the file active', (done) => {
- const localFile = file();
+ const localFile = file('activeFile');
store.dispatch('setFileActive', localFile)
.then(() => {
@@ -139,7 +139,7 @@ describe('Multi-file store file actions', () => {
});
it('returns early if file is already active', (done) => {
- const localFile = file();
+ const localFile = file('earlyActive');
localFile.active = true;
store.dispatch('setFileActive', localFile)
@@ -151,11 +151,11 @@ describe('Multi-file store file actions', () => {
});
it('sets current active file to not active', (done) => {
- const localFile = file();
+ const localFile = file('currentActive');
localFile.active = true;
store.state.openFiles.push(localFile);
- store.dispatch('setFileActive', file())
+ store.dispatch('setFileActive', file('newActive'))
.then(() => {
expect(localFile.active).toBeFalsy();
@@ -166,7 +166,7 @@ describe('Multi-file store file actions', () => {
it('resets location.hash for line highlighting', (done) => {
location.hash = 'test';
- store.dispatch('setFileActive', file())
+ store.dispatch('setFileActive', file('otherActive'))
.then(() => {
expect(location.hash).not.toBe('test');
@@ -176,7 +176,7 @@ describe('Multi-file store file actions', () => {
});
describe('getFileData', () => {
- let localFile = file();
+ let localFile;
beforeEach(() => {
spyOn(service, 'getFileData').and.returnValue(Promise.resolve({
@@ -194,10 +194,17 @@ describe('Multi-file store file actions', () => {
}),
}));
- localFile = file();
+ localFile = file('newCreate');
localFile.url = 'getFileDataURL';
});
+ afterEach(() => {
+ store.dispatch('closeFile', {
+ file: localFile,
+ force: true,
+ });
+ });
+
it('calls the service', (done) => {
store.dispatch('getFileData', localFile)
.then(() => {
@@ -268,7 +275,7 @@ describe('Multi-file store file actions', () => {
beforeEach(() => {
spyOn(service, 'getRawFileData').and.returnValue(Promise.resolve('raw'));
- tmpFile = file();
+ tmpFile = file('tmpFile');
});
it('calls getRawFileData service method', (done) => {
@@ -294,7 +301,7 @@ describe('Multi-file store file actions', () => {
let tmpFile;
beforeEach(() => {
- tmpFile = file();
+ tmpFile = file('tmpFile');
});
it('updates file content', (done) => {
diff --git a/spec/javascripts/repo/stores/actions_spec.js b/spec/javascripts/repo/stores/actions_spec.js
index 0b0d34f072a..8d830c67290 100644
--- a/spec/javascripts/repo/stores/actions_spec.js
+++ b/spec/javascripts/repo/stores/actions_spec.js
@@ -48,14 +48,14 @@ describe('Multi-file store actions', () => {
describe('discardAllChanges', () => {
beforeEach(() => {
- store.state.openFiles.push(file());
+ store.state.openFiles.push(file('discardAll'));
store.state.openFiles[0].changed = true;
});
});
describe('closeAllFiles', () => {
beforeEach(() => {
- store.state.openFiles.push(file());
+ store.state.openFiles.push(file('closeAll'));
store.state.openFiles[0].opened = true;
});
@@ -97,7 +97,7 @@ describe('Multi-file store actions', () => {
it('opens discard popup if there are changed files', (done) => {
store.state.editMode = true;
- store.state.openFiles.push(file());
+ store.state.openFiles.push(file('discardChanges'));
store.state.openFiles[0].changed = true;
store.dispatch('toggleEditMode')
@@ -111,7 +111,7 @@ describe('Multi-file store actions', () => {
it('can force closed if there are changed files', (done) => {
store.state.editMode = true;
- store.state.openFiles.push(file());
+ store.state.openFiles.push(file('forceClose'));
store.state.openFiles[0].changed = true;
store.dispatch('toggleEditMode', true)
@@ -124,7 +124,7 @@ describe('Multi-file store actions', () => {
});
it('discards file changes', (done) => {
- const f = file();
+ const f = file('discard');
store.state.editMode = true;
store.state.openFiles.push(f);
f.changed = true;
@@ -285,8 +285,8 @@ describe('Multi-file store actions', () => {
});
it('adds commit data to changed files', (done) => {
- const changedFile = file();
- const f = file();
+ const changedFile = file('changed');
+ const f = file('newfile');
changedFile.changed = true;
store.state.openFiles.push(changedFile, f);
@@ -300,19 +300,6 @@ describe('Multi-file store actions', () => {
}).catch(done.fail);
});
- it('closes all files', (done) => {
- store.state.openFiles.push(file());
- store.state.openFiles[0].opened = true;
-
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(Vue.nextTick)
- .then(() => {
- expect(store.state.openFiles.length).toBe(0);
-
- done();
- }).catch(done.fail);
- });
-
it('scrolls to top of page', (done) => {
store.dispatch('commitChanges', { payload, newMr: false })
.then(() => {
diff --git a/spec/javascripts/repo/stores/mutations/file_spec.js b/spec/javascripts/repo/stores/mutations/file_spec.js
index 947a60587df..6e204ef0404 100644
--- a/spec/javascripts/repo/stores/mutations/file_spec.js
+++ b/spec/javascripts/repo/stores/mutations/file_spec.js
@@ -117,7 +117,7 @@ describe('Multi-file store file mutations', () => {
describe('CREATE_TMP_FILE', () => {
it('adds file into parent tree', () => {
- const f = file();
+ const f = file('tmpFile');
mutations.CREATE_TMP_FILE(localState, {
file: f,
diff --git a/spec/javascripts/repo/stores/mutations/tree_spec.js b/spec/javascripts/repo/stores/mutations/tree_spec.js
index cf1248ba28b..e6ca8ea139e 100644
--- a/spec/javascripts/repo/stores/mutations/tree_spec.js
+++ b/spec/javascripts/repo/stores/mutations/tree_spec.js
@@ -57,7 +57,7 @@ describe('Multi-file store tree mutations', () => {
describe('CREATE_TMP_TREE', () => {
it('adds tree into parent tree', () => {
- const tmpEntry = file();
+ const tmpEntry = file('tmpTree');
mutations.CREATE_TMP_TREE(localState, {
tmpEntry,
diff --git a/spec/javascripts/search_spec.js b/spec/javascripts/search_spec.js
new file mode 100644
index 00000000000..38e94d45e55
--- /dev/null
+++ b/spec/javascripts/search_spec.js
@@ -0,0 +1,40 @@
+import Api from '~/api';
+import Search from '~/pages/search/show/search';
+
+describe('Search', () => {
+ const fixturePath = 'search/show.html.raw';
+ const searchTerm = 'some search';
+ const fillDropdownInput = (dropdownSelector) => {
+ const dropdownElement = document.querySelector(dropdownSelector).parentNode;
+ const inputElement = dropdownElement.querySelector('.dropdown-input-field');
+ inputElement.value = searchTerm;
+ return inputElement;
+ };
+
+ preloadFixtures(fixturePath);
+
+ beforeEach(() => {
+ loadFixtures(fixturePath);
+ new Search(); // eslint-disable-line no-new
+ });
+
+ it('requests groups from backend when filtering', (done) => {
+ spyOn(Api, 'groups').and.callFake((term) => {
+ expect(term).toBe(searchTerm);
+ done();
+ });
+ const inputElement = fillDropdownInput('.js-search-group-dropdown');
+
+ $(inputElement).trigger('input');
+ });
+
+ it('requests projects from backend when filtering', (done) => {
+ spyOn(Api, 'projects').and.callFake((term) => {
+ expect(term).toBe(searchTerm);
+ done();
+ });
+ const inputElement = fillDropdownInput('.js-search-project-dropdown');
+
+ $(inputElement).trigger('input');
+ });
+});
diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js
index 7bc591d2d47..d9e84e35f69 100644
--- a/spec/javascripts/sidebar/mock_data.js
+++ b/spec/javascripts/sidebar/mock_data.js
@@ -27,7 +27,7 @@ const RESPONSE_MAP = {
username: 'user0',
id: 22,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0',
},
{
@@ -35,7 +35,7 @@ const RESPONSE_MAP = {
username: 'tajuana',
id: 18,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana',
},
{
@@ -43,7 +43,7 @@ const RESPONSE_MAP = {
username: 'michaele.will',
id: 16,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will',
},
],
@@ -72,24 +72,24 @@ const RESPONSE_MAP = {
username: 'user0',
id: 22,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/user0',
+ avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/user0',
},
{
name: 'Marguerite Bartell',
username: 'tajuana',
id: 18,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/tajuana',
+ avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/tajuana',
},
{
name: 'Laureen Ritchie',
username: 'michaele.will',
id: 16,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/michaele.will',
+ avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/michaele.will',
},
],
human_time_estimate: null,
@@ -100,24 +100,24 @@ const RESPONSE_MAP = {
username: 'user0',
id: 22,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/user0',
+ avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/user0',
},
{
name: 'Marguerite Bartell',
username: 'tajuana',
id: 18,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/tajuana',
+ avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/tajuana',
},
{
name: 'Laureen Ritchie',
username: 'michaele.will',
id: 16,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/michaele.will',
+ avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/michaele.will',
},
],
subscribed: true,
@@ -182,7 +182,7 @@ const mockData = {
id: 1,
name: 'Administrator',
username: 'root',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
rootPath: '/',
fullPath: '/gitlab-org/gitlab-shell',
@@ -194,7 +194,7 @@ const mockData = {
human_total_time_spent: null,
},
user: {
- avatar: 'http://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: 1,
name: 'Administrator',
username: 'root',
diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js
index ea4eae1e23f..3591f96ff87 100644
--- a/spec/javascripts/sidebar/sidebar_store_spec.js
+++ b/spec/javascripts/sidebar/sidebar_store_spec.js
@@ -6,14 +6,14 @@ const ASSIGNEE = {
id: 2,
name: 'gitlab user 2',
username: 'gitlab2',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
};
const ANOTHER_ASSINEE = {
id: 3,
name: 'gitlab user 3',
username: 'gitlab3',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
};
const PARTICIPANT = {
@@ -38,7 +38,7 @@ describe('Sidebar store', () => {
id: 1,
name: 'Administrator',
username: 'root',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
editable: true,
rootPath: '/',
diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js
index c4f500788b2..b1b03ef1e09 100644
--- a/spec/javascripts/signin_tabs_memoizer_spec.js
+++ b/spec/javascripts/signin_tabs_memoizer_spec.js
@@ -53,6 +53,13 @@ import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer';
expect(memo.readData()).toEqual('#standard');
});
+ it('overrides last selected tab with hash tag when given', () => {
+ window.location.hash = '#ldap';
+ createMemoizer();
+
+ expect(memo.readData()).toEqual('#ldap');
+ });
+
describe('class constructor', () => {
beforeEach(() => {
memo = createMemoizer();
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 2f6691df9cd..9b2a5379855 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -6,6 +6,8 @@ import '~/commons';
import Vue from 'vue';
import VueResource from 'vue-resource';
+import { getDefaultAdapter } from '~/lib/utils/axios_utils';
+
const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent);
Vue.config.devtools = !isHeadlessChrome;
Vue.config.productionTip = false;
@@ -59,6 +61,8 @@ beforeEach(() => {
Vue.http.interceptors = builtinVueHttpInterceptors.slice();
});
+const axiosDefaultAdapter = getDefaultAdapter();
+
// render all of our tests
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function (path) {
@@ -94,6 +98,12 @@ describe('test errors', () => {
it('has no Vue error', () => {
expect(hasVueErrors).toBe(false);
});
+
+ it('restores axios adapter after mocking', () => {
+ if (getDefaultAdapter() !== axiosDefaultAdapter) {
+ fail('axios adapter is not restored! Did you forget a restore() on MockAdapter?');
+ }
+ });
});
// if we're generating coverage reports, make sure to include all files so
diff --git a/spec/javascripts/toggle_buttons_spec.js b/spec/javascripts/toggle_buttons_spec.js
new file mode 100644
index 00000000000..205e396d682
--- /dev/null
+++ b/spec/javascripts/toggle_buttons_spec.js
@@ -0,0 +1,120 @@
+import setupToggleButtons from '~/toggle_buttons';
+import getSetTimeoutPromise from './helpers/set_timeout_promise_helper';
+
+function generateMarkup(isChecked = true) {
+ return `
+ <button type="button" class="${isChecked ? 'is-checked' : ''} js-project-feature-toggle">
+ <input type="hidden" class="js-project-feature-toggle-input" value="${isChecked}" />
+ </button>
+ `;
+}
+
+function setupFixture(isChecked, clickCallback) {
+ const wrapper = document.createElement('div');
+ wrapper.innerHTML = generateMarkup(isChecked);
+
+ setupToggleButtons(wrapper, clickCallback);
+
+ return wrapper;
+}
+
+describe('ToggleButtons', () => {
+ describe('when input value is true', () => {
+ it('should initialize as checked', () => {
+ const wrapper = setupFixture(true);
+
+ expect(wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked')).toEqual(true);
+ expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('true');
+ });
+
+ it('should toggle to unchecked when clicked', (done) => {
+ const wrapper = setupFixture(true);
+ const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
+
+ toggleButton.click();
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(toggleButton.classList.contains('is-checked')).toEqual(false);
+ expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('false');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('when input value is false', () => {
+ it('should initialize as unchecked', () => {
+ const wrapper = setupFixture(false);
+
+ expect(wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked')).toEqual(false);
+ expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('false');
+ });
+
+ it('should toggle to checked when clicked', (done) => {
+ const wrapper = setupFixture(false);
+ const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
+
+ toggleButton.click();
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(toggleButton.classList.contains('is-checked')).toEqual(true);
+ expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('true');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ it('should emit `trigger-change` event', (done) => {
+ const changeSpy = jasmine.createSpy('changeEventHandler');
+ const wrapper = setupFixture(false);
+ const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
+ const input = wrapper.querySelector('.js-project-feature-toggle-input');
+
+ $(input).on('trigger-change', changeSpy);
+
+ toggleButton.click();
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(changeSpy).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ describe('clickCallback', () => {
+ it('should show loading indicator while waiting', (done) => {
+ const isChecked = true;
+ const clickCallback = (newValue, toggleButton) => {
+ const input = toggleButton.querySelector('.js-project-feature-toggle-input');
+
+ expect(newValue).toEqual(false);
+
+ // Check for the loading state
+ expect(toggleButton.classList.contains('is-checked')).toEqual(false);
+ expect(toggleButton.classList.contains('is-loading')).toEqual(true);
+ expect(toggleButton.disabled).toEqual(true);
+ expect(input.value).toEqual('true');
+
+ // After the callback finishes, check that the loading state is gone
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(toggleButton.classList.contains('is-checked')).toEqual(false);
+ expect(toggleButton.classList.contains('is-loading')).toEqual(false);
+ expect(toggleButton.disabled).toEqual(false);
+ expect(input.value).toEqual('false');
+ })
+ .then(done)
+ .catch(done.fail);
+ };
+
+ const wrapper = setupFixture(isChecked, clickCallback);
+ const toggleButton = wrapper.querySelector('.js-project-feature-toggle');
+
+ toggleButton.click();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index 06f89fabf42..93bb83ca8bd 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -48,20 +48,23 @@ describe('MRWidgetHeader', () => {
describe('template', () => {
let vm;
let el;
+ let mr;
const sourceBranchPath = '/foo/bar/mr-widget-refactor';
- const mr = {
- divergedCommitsCount: 12,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: `<a href="${sourceBranchPath}">mr-widget-refactor</a>`,
- targetBranchPath: 'foo/bar/commits-path',
- targetBranchTreePath: 'foo/bar/tree/path',
- targetBranch: 'master',
- isOpen: true,
- emailPatchesPath: '/mr/email-patches',
- plainDiffPath: '/mr/plainDiffPath',
- };
beforeEach(() => {
+ mr = {
+ divergedCommitsCount: 12,
+ sourceBranch: 'mr-widget-refactor',
+ sourceBranchLink: `<a href="${sourceBranchPath}">mr-widget-refactor</a>`,
+ sourceBranchRemoved: false,
+ targetBranchPath: 'foo/bar/commits-path',
+ targetBranchTreePath: 'foo/bar/tree/path',
+ targetBranch: 'master',
+ isOpen: true,
+ emailPatchesPath: '/mr/email-patches',
+ plainDiffPath: '/mr/plainDiffPath',
+ };
+
vm = createComponent(mr);
el = vm.$el;
});
@@ -82,6 +85,8 @@ describe('MRWidgetHeader', () => {
expect(el.textContent).toContain('Check out branch');
expect(el.querySelectorAll('.dropdown li a')[0].getAttribute('href')).toEqual(mr.emailPatchesPath);
expect(el.querySelectorAll('.dropdown li a')[1].getAttribute('href')).toEqual(mr.plainDiffPath);
+
+ expect(el.querySelector('a[href="#modal_merge_info"]').getAttribute('disabled')).toBeNull();
});
it('should not have right action links if the MR state is not open', (done) => {
@@ -101,5 +106,16 @@ describe('MRWidgetHeader', () => {
done();
});
});
+
+ it('should disable check out branch button if source branch has been removed', (done) => {
+ vm.mr.sourceBranchRemoved = true;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(el.querySelector('a[href="#modal_merge_info"]').getAttribute('disabled')).toBe('disabled');
+ done();
+ })
+ .catch(done.fail);
+ });
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
new file mode 100644
index 00000000000..c39fcda0071
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
@@ -0,0 +1,44 @@
+import Vue from 'vue';
+import mrStatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('MR widget status icon component', () => {
+ let vm;
+ let Component;
+
+ beforeEach(() => {
+ Component = Vue.extend(mrStatusIcon);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('while loading', () => {
+ it('renders loading icon', () => {
+ vm = mountComponent(Component, { status: 'loading' });
+ expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
+ });
+ });
+
+ describe('with status icon', () => {
+ it('renders ci status icon', () => {
+ vm = mountComponent(Component, { status: 'failed' });
+ expect(vm.$el.querySelector('.js-ci-status-icon-failed')).not.toBeNull();
+ });
+ });
+
+ describe('with disabled button', () => {
+ it('renders a disabled button', () => {
+ vm = mountComponent(Component, { status: 'failed', showDisabledButton: true });
+ expect(vm.$el.querySelector('.js-disabled-merge-button').textContent.trim()).toEqual('Merge');
+ });
+ });
+
+ describe('without disabled button', () => {
+ it('does not render a disabled button', () => {
+ vm = mountComponent(Component, { status: 'failed' });
+ expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
index 4869fb17d96..f98ebdb38e6 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
@@ -1,18 +1,31 @@
import Vue from 'vue';
-import archivedComponent from '~/vue_merge_request_widget/components/states/mr_widget_archived';
+import archivedComponent from '~/vue_merge_request_widget/components/states/mr_widget_archived.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetArchived', () => {
- describe('template', () => {
- it('should have correct elements', () => {
- const Component = Vue.extend(archivedComponent);
- const el = new Component({
- el: document.createElement('div'),
- }).$el;
+ let vm;
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.querySelector('button').classList.contains('btn-success')).toBeTruthy();
- expect(el.querySelector('button').disabled).toBeTruthy();
- expect(el.innerText).toContain('This project is archived, write access has been disabled');
- });
+ beforeEach(() => {
+ const Component = Vue.extend(archivedComponent);
+ vm = mountComponent(Component);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders a ci status failed icon', () => {
+ expect(vm.$el.querySelector('.ci-status-icon')).not.toBeNull();
+ });
+
+ it('renders a disabled button', () => {
+ expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
+ expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Merge');
+ });
+
+ it('renders information', () => {
+ expect(
+ vm.$el.querySelector('.bold').textContent.trim(),
+ ).toEqual('This project is archived, write access has been disabled');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
index 6042d7384d5..95c94e95e3a 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
@@ -1,32 +1,47 @@
import Vue from 'vue';
-import autoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed';
-
-const mergeError = 'This is the merge error';
+import autoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue';
+import eventHub from '~/vue_merge_request_widget/event_hub';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetAutoMergeFailed', () => {
- describe('props', () => {
- it('should have props', () => {
- const mrProp = autoMergeFailedComponent.props.mr;
+ let vm;
+ const mergeError = 'This is the merge error';
- expect(mrProp.type instanceof Object).toBeTruthy();
- expect(mrProp.required).toBeTruthy();
+ beforeEach(() => {
+ const Component = Vue.extend(autoMergeFailedComponent);
+ vm = mountComponent(Component, {
+ mr: { mergeError },
});
});
- describe('template', () => {
- const Component = Vue.extend(autoMergeFailedComponent);
- const vm = new Component({
- el: document.createElement('div'),
- propsData: {
- mr: { mergeError },
- },
- });
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders failed message', () => {
+ expect(vm.$el.textContent).toContain('This merge request failed to be merged automatically');
+ });
+
+ it('renders merge error provided', () => {
+ expect(vm.$el.innerText).toContain(mergeError);
+ });
+
+ it('render refresh button', () => {
+ expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Refresh');
+ });
+
+ it('emits event and shows loading icon when button is clicked', (done) => {
+ spyOn(eventHub, '$emit');
+ vm.$el.querySelector('button').click();
+
+ expect(eventHub.$emit.calls.argsFor(0)[0]).toEqual('MRWidgetUpdateRequested');
- it('should have correct elements', () => {
- expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeFalsy();
- expect(vm.$el.innerText).toContain('This merge request failed to be merged automatically');
- expect(vm.$el.innerText).toContain(mergeError);
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
+ expect(
+ vm.$el.querySelector('button i').classList,
+ ).toContain('fa-spinner');
+ done();
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
index 6b7aa935ad3..658cadddb81 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
@@ -1,19 +1,29 @@
import Vue from 'vue';
-import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking';
+import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetChecking', () => {
- describe('template', () => {
- it('should have correct elements', () => {
- const Component = Vue.extend(checkingComponent);
- const el = new Component({
- el: document.createElement('div'),
- }).$el;
+ let Component;
+ let vm;
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.querySelector('button').classList.contains('btn-success')).toBeTruthy();
- expect(el.querySelector('button').disabled).toBeTruthy();
- expect(el.innerText).toContain('Checking ability to merge automatically');
- expect(el.querySelector('i')).toBeDefined();
- });
+ beforeEach(() => {
+ Component = Vue.extend(checkingComponent);
+ vm = mountComponent(Component);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders disabled button', () => {
+ expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
+ });
+
+ it('renders loading icon', () => {
+ expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
+ });
+
+ it('renders information about merging', () => {
+ expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual('Checking ability to merge automatically');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
index 1bf97bbf093..51a34739ee9 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
@@ -1,74 +1,58 @@
import Vue from 'vue';
-import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed';
-
-const mr = {
- targetBranch: 'good-branch',
- targetBranchPath: '/good-branch',
- metrics: {
- mergedBy: {},
- mergedAt: 'mergedUpdatedAt',
- closedBy: {
- name: 'Fatih Acet',
- username: 'fatihacet',
- },
- closedAt: 'closedEventUpdatedAt',
- readableMergedAt: '',
- readableClosedAt: '',
- },
- updatedAt: 'mrUpdatedAt',
- closedAt: '1 day ago',
-};
-
-const createComponent = () => {
- const Component = Vue.extend(closedComponent);
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr },
- });
-};
+import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetClosed', () => {
- describe('props', () => {
- it('should have props', () => {
- const mrProp = closedComponent.props.mr;
-
- expect(mrProp.type instanceof Object).toBeTruthy();
- expect(mrProp.required).toBeTruthy();
- });
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(closedComponent);
+ vm = mountComponent(Component, { mr: {
+ metrics: {
+ mergedBy: {},
+ closedBy: {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://localhost:3000/root',
+ avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ closedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ readableMergedAt: '',
+ readableClosedAt: 'less than a minute ago',
+ },
+ targetBranchPath: '/twitter/flight/commits/so_long_jquery',
+ targetBranch: 'so_long_jquery',
+ } });
});
- describe('components', () => {
- it('should have components added', () => {
- expect(closedComponent.components['mr-widget-author-and-time']).toBeDefined();
- });
+ afterEach(() => {
+ vm.$destroy();
});
- describe('template', () => {
- let vm;
- let el;
+ it('renders warning icon', () => {
+ expect(vm.$el.querySelector('.js-ci-status-icon-warning')).not.toBeNull();
+ });
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
+ it('renders closed by information with author and time', () => {
+ expect(
+ vm.$el.querySelector('.js-mr-widget-author').textContent.trim().replace(/\s\s+/g, ' '),
+ ).toContain(
+ 'Closed by Administrator less than a minute ago',
+ );
+ });
- afterEach(() => {
- vm.$destroy();
- });
+ it('links to the user that closed the MR', () => {
+ expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual('http://localhost:3000/root');
+ });
- it('should have correct elements', () => {
- expect(el.querySelector('h4').textContent).toContain('Closed by');
- expect(el.querySelector('h4').textContent).toContain(mr.metrics.closedBy.name);
- expect(el.textContent).toContain('The changes were not merged into');
- expect(el.querySelector('.label-branch').getAttribute('href')).toEqual(mr.targetBranchPath);
- expect(el.querySelector('.label-branch').textContent).toContain(mr.targetBranch);
- });
+ it('renders information about the changes not being merged', () => {
+ expect(
+ vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' '),
+ ).toContain('The changes were not merged into so_long_jquery');
+ });
- it('should use closedEvent updatedAt as tooltip title', () => {
- expect(
- el.querySelector('time').getAttribute('title'),
- ).toBe('closedEventUpdatedAt');
- });
+ it('renders link for target branch', () => {
+ expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual('/twitter/flight/commits/so_long_jquery');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
index 5d4c7ec09dc..a7d69fdcdb9 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
@@ -1,105 +1,85 @@
import Vue from 'vue';
-import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts';
+import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
-const ConflictsComponent = Vue.extend(conflictsComponent);
-const path = '/conflicts';
-
describe('MRWidgetConflicts', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr } = conflictsComponent.props;
+ let Component;
+ let vm;
+ const path = '/conflicts';
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
- });
+ beforeEach(() => {
+ Component = Vue.extend(conflictsComponent);
});
- describe('template', () => {
- describe('when allowed to merge', () => {
- let vm;
-
- beforeEach(() => {
- vm = mountComponent(ConflictsComponent, {
- mr: {
- canMerge: true,
- conflictResolutionPath: path,
- },
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should tell you about conflicts without bothering other people', () => {
- expect(vm.$el.textContent).toContain('There are merge conflicts');
- expect(vm.$el.textContent).not.toContain('ask someone with write access');
- });
-
- it('should allow you to resolve the conflicts', () => {
- const resolveButton = vm.$el.querySelector('.js-resolve-conflicts-button');
+ afterEach(() => {
+ vm.$destroy();
+ });
- expect(resolveButton.textContent).toContain('Resolve conflicts');
- expect(resolveButton.getAttribute('href')).toEqual(path);
+ describe('when allowed to merge', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ mr: {
+ canMerge: true,
+ conflictResolutionPath: path,
+ },
});
+ });
- it('should have merge buttons', () => {
- const mergeButton = vm.$el.querySelector('.js-disabled-merge-button');
- const mergeLocallyButton = vm.$el.querySelector('.js-merge-locally-button');
-
- expect(mergeButton.textContent).toContain('Merge');
- expect(mergeButton.disabled).toBeTruthy();
- expect(mergeButton.classList.contains('btn-success')).toEqual(true);
- expect(mergeLocallyButton.textContent).toContain('Merge locally');
- });
+ it('should tell you about conflicts without bothering other people', () => {
+ expect(vm.$el.textContent).toContain('There are merge conflicts');
+ expect(vm.$el.textContent).not.toContain('ask someone with write access');
});
- describe('when user does not have permission to merge', () => {
- let vm;
+ it('should allow you to resolve the conflicts', () => {
+ const resolveButton = vm.$el.querySelector('.js-resolve-conflicts-button');
- beforeEach(() => {
- vm = mountComponent(ConflictsComponent, {
- mr: {
- canMerge: false,
- },
- });
- });
+ expect(resolveButton.textContent).toContain('Resolve conflicts');
+ expect(resolveButton.getAttribute('href')).toEqual(path);
+ });
- afterEach(() => {
- vm.$destroy();
- });
+ it('should have merge buttons', () => {
+ const mergeButton = vm.$el.querySelector('.js-disabled-merge-button');
+ const mergeLocallyButton = vm.$el.querySelector('.js-merge-locally-button');
- it('should show proper message', () => {
- expect(vm.$el.textContent).toContain('ask someone with write access');
- });
+ expect(mergeButton.textContent).toContain('Merge');
+ expect(mergeButton.disabled).toBeTruthy();
+ expect(mergeButton.classList.contains('btn-success')).toEqual(true);
+ expect(mergeLocallyButton.textContent).toContain('Merge locally');
+ });
+ });
- it('should not have action buttons', () => {
- expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeDefined();
- expect(vm.$el.querySelector('.js-resolve-conflicts-button')).toBeNull();
- expect(vm.$el.querySelector('.js-merge-locally-button')).toBeNull();
+ describe('when user does not have permission to merge', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ mr: {
+ canMerge: false,
+ },
});
});
- describe('when fast-forward or semi-linear merge enabled', () => {
- let vm;
+ it('should show proper message', () => {
+ expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('ask someone with write access');
+ });
- beforeEach(() => {
- vm = mountComponent(ConflictsComponent, {
- mr: {
- shouldBeRebased: true,
- },
- });
- });
+ it('should not have action buttons', () => {
+ expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeDefined();
+ expect(vm.$el.querySelector('.js-resolve-conflicts-button')).toBeNull();
+ expect(vm.$el.querySelector('.js-merge-locally-button')).toBeNull();
+ });
+ });
- afterEach(() => {
- vm.$destroy();
+ describe('when fast-forward or semi-linear merge enabled', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ mr: {
+ shouldBeRebased: true,
+ },
});
+ });
- it('should tell you to rebase locally', () => {
- expect(vm.$el.textContent).toContain('Fast-forward merge is not possible.');
- expect(vm.$el.textContent).toContain('To merge this request, first rebase locally');
- });
+ it('should tell you to rebase locally', () => {
+ expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('Fast-forward merge is not possible.');
+ expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('To merge this request, first rebase locally');
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
index cef365eec8a..a57b9811e08 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
@@ -1,45 +1,37 @@
import Vue from 'vue';
-import failedToMergeComponent from '~/vue_merge_request_widget/components/states/mr_widget_failed_to_merge';
+import failedToMergeComponent from '~/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-
-const mr = {
- mergeError: 'Merge error happened.',
-};
-const createComponent = () => {
- const Component = Vue.extend(failedToMergeComponent);
- return new Component({
- el: document.createElement('div'),
- propsData: { mr },
- });
-};
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetFailedToMerge', () => {
- describe('data', () => {
- it('should have default data', () => {
- const data = failedToMergeComponent.data();
+ let Component;
+ let vm;
+
+ beforeEach(() => {
+ Component = Vue.extend(failedToMergeComponent);
+ spyOn(eventHub, '$emit');
+ vm = mountComponent(Component, { mr: {
+ mergeError: 'Merge error happened.',
+ } });
+ });
- expect(data.timer).toEqual(10);
- expect(data.isRefreshing).toBeFalsy();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
describe('timerText', () => {
it('should return correct timer text', () => {
- const vm = createComponent();
- expect(vm.timerText).toEqual('10 seconds');
+ expect(vm.timerText).toEqual('Refreshing in 10 seconds to show the updated status...');
vm.timer = 1;
- expect(vm.timerText).toEqual('a second');
+ expect(vm.timerText).toEqual('Refreshing in a second to show the updated status...');
});
});
});
describe('created', () => {
it('should disable polling', () => {
- spyOn(eventHub, '$emit');
- createComponent();
-
expect(eventHub.$emit).toHaveBeenCalledWith('DisablePolling');
});
});
@@ -47,13 +39,10 @@ describe('MRWidgetFailedToMerge', () => {
describe('methods', () => {
describe('refresh', () => {
it('should emit event to request component refresh', () => {
- spyOn(eventHub, '$emit');
- const vm = createComponent();
-
- expect(vm.isRefreshing).toBeFalsy();
+ expect(vm.isRefreshing).toEqual(false);
vm.refresh();
- expect(vm.isRefreshing).toBeTruthy();
+ expect(vm.isRefreshing).toEqual(true);
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
expect(eventHub.$emit).toHaveBeenCalledWith('EnablePolling');
});
@@ -61,12 +50,11 @@ describe('MRWidgetFailedToMerge', () => {
describe('updateTimer', () => {
it('should update timer and emit event when timer end', () => {
- const vm = createComponent();
spyOn(vm, 'refresh');
expect(vm.timer).toEqual(10);
- for (let i = 0; i < 10; i++) { // eslint-disable-line
+ for (let i = 0; i < 10; i += 1) {
expect(vm.timer).toEqual(10 - i);
vm.updateTimer();
}
@@ -76,47 +64,54 @@ describe('MRWidgetFailedToMerge', () => {
});
});
- describe('template', () => {
- let vm;
- let el;
+ describe('while it is refreshing', () => {
+ it('renders Refresing now', (done) => {
+ vm.isRefreshing = true;
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.js-refresh-label').textContent.trim()).toEqual('Refreshing now');
+ done();
+ });
});
+ });
- it('should have correct elements', (done) => {
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.innerText).toContain('Merge error happened.');
- expect(el.innerText).toContain('Refreshing in 10 seconds');
- expect(el.innerText).not.toContain('Merge failed.');
- expect(el.querySelector('button').getAttribute('disabled')).toBeTruthy();
- expect(el.querySelector('button').innerText).toContain('Merge');
- expect(el.querySelector('.js-refresh-button').innerText).toContain('Refresh now');
- expect(el.querySelector('.js-refresh-label')).toEqual(null);
- expect(el.innerText).not.toContain('Refreshing now');
- setTimeout(() => {
- expect(el.innerText).toContain('Refreshing in 9 seconds');
- done();
- }, 1010);
+ describe('while it is not regresing', () => {
+ it('renders warning icon and disabled merge button', () => {
+ expect(vm.$el.querySelector('.js-ci-status-icon-warning')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-disabled-merge-button').getAttribute('disabled')).toEqual('disabled');
+ });
+
+ it('renders given error', () => {
+ expect(vm.$el.querySelector('.has-error-message').textContent.trim()).toEqual('Merge error happened..');
});
- it('should just generic merge failed message if merge_error is not available', (done) => {
- vm.mr.mergeError = null;
+ it('renders refresh button', () => {
+ expect(vm.$el.querySelector('.js-refresh-button').textContent.trim()).toEqual('Refresh now');
+ });
- Vue.nextTick(() => {
- expect(el.innerText).toContain('Merge failed.');
- expect(el.innerText).not.toContain('Merge error happened.');
- done();
- });
+ it('renders remaining time', () => {
+ expect(
+ vm.$el.querySelector('.has-custom-error').textContent.trim(),
+ ).toEqual('Refreshing in 10 seconds to show the updated status...');
+ });
+ });
+
+ it('should just generic merge failed message if merge_error is not available', (done) => {
+ vm.mr.mergeError = null;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.innerText).toContain('Merge failed.');
+ expect(vm.$el.innerText).not.toContain('Merge error happened.');
+ done();
});
+ });
- it('should show refresh label when refresh requested', () => {
- vm.refresh();
- Vue.nextTick(() => {
- expect(el.innerText).not.toContain('Merge failed. Refreshing');
- expect(el.innerText).toContain('Refreshing now');
- });
+ it('should show refresh label when refresh requested', (done) => {
+ vm.refresh();
+ Vue.nextTick(() => {
+ expect(vm.$el.innerText).not.toContain('Merge failed. Refreshing');
+ expect(vm.$el.innerText).toContain('Refreshing now');
+ done();
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
deleted file mode 100644
index 237035648cf..00000000000
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Vue from 'vue';
-import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging';
-
-describe('MRWidgetMerging', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr } = mergingComponent.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
- });
- });
-
- describe('template', () => {
- it('should have correct elements', () => {
- const Component = Vue.extend(mergingComponent);
- const mr = {
- targetBranchPath: '/branch-path',
- targetBranch: 'branch',
- };
- const el = new Component({
- el: document.createElement('div'),
- propsData: { mr },
- }).$el;
-
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.innerText).toContain('This merge request is in the process of being merged');
- expect(el.innerText).toContain('changes will be merged into');
- expect(el.querySelector('.label-branch a').getAttribute('href')).toEqual(mr.targetBranchPath);
- expect(el.querySelector('.label-branch a').textContent).toContain(mr.targetBranch);
- });
- });
-});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
index 5f4df15bcd6..df56c4e2c5c 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
@@ -1,77 +1,50 @@
import Vue from 'vue';
-import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds';
+import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-
-const targetBranchPath = '/foo/bar';
-const targetBranch = 'foo';
-const sha = '1EA2EZ34';
-
-const createComponent = () => {
- const Component = Vue.extend(mwpsComponent);
- const mr = {
- shouldRemoveSourceBranch: false,
- canRemoveSourceBranch: true,
- canCancelAutomaticMerge: true,
- mergeUserId: 1,
- currentUserId: 1,
- setToMWPSBy: {},
- sha,
- targetBranchPath,
- targetBranch,
- };
-
- const service = {
- cancelAutomaticMerge() {},
- mergeResource: {
- save() {},
- },
- };
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr, service },
- });
-};
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetMergeWhenPipelineSucceeds', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = mwpsComponent.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
-
- expect(service.type instanceof Object).toBeTruthy();
- expect(service.required).toBeTruthy();
+ let vm;
+ const targetBranchPath = '/foo/bar';
+ const targetBranch = 'foo';
+ const sha = '1EA2EZ34';
+
+ beforeEach(() => {
+ const Component = Vue.extend(mwpsComponent);
+ spyOn(eventHub, '$emit');
+
+ vm = mountComponent(Component, {
+ mr: {
+ shouldRemoveSourceBranch: false,
+ canRemoveSourceBranch: true,
+ canCancelAutomaticMerge: true,
+ mergeUserId: 1,
+ currentUserId: 1,
+ setToMWPSBy: {},
+ sha,
+ targetBranchPath,
+ targetBranch,
+ },
+ service: {
+ cancelAutomaticMerge() {},
+ mergeResource: {
+ save() {},
+ },
+ },
});
});
- describe('components', () => {
- it('should have components added', () => {
- expect(mwpsComponent.components['mr-widget-author']).toBeDefined();
- });
- });
-
- describe('data', () => {
- it('should have default data', () => {
- const data = mwpsComponent.data();
-
- expect(data.isCancellingAutoMerge).toBeFalsy();
- expect(data.isRemovingSourceBranch).toBeFalsy();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
describe('canRemoveSourceBranch', () => {
it('should return true when user is able to remove source branch', () => {
- const vm = createComponent();
-
expect(vm.canRemoveSourceBranch).toBeTruthy();
});
it('should return false when user id is not the same with who set the MWPS', () => {
- const vm = createComponent();
-
vm.mr.mergeUserId = 2;
expect(vm.canRemoveSourceBranch).toBeFalsy();
@@ -83,15 +56,11 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
});
it('should return false when shouldRemoveSourceBranch set to false', () => {
- const vm = createComponent();
-
vm.mr.shouldRemoveSourceBranch = true;
expect(vm.canRemoveSourceBranch).toBeFalsy();
});
it('should return false if user is not able to remove the source branch', () => {
- const vm = createComponent();
-
vm.mr.canRemoveSourceBranch = false;
expect(vm.canRemoveSourceBranch).toBeFalsy();
});
@@ -101,11 +70,9 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
describe('methods', () => {
describe('cancelAutomaticMerge', () => {
it('should set flag and call service then tell main component to update the widget with data', (done) => {
- const vm = createComponent();
const mrObj = {
is_new_mr_data: true,
};
- spyOn(eventHub, '$emit');
spyOn(vm.service, 'cancelAutomaticMerge').and.returnValue(new Promise((resolve) => {
resolve({
data: mrObj,
@@ -123,8 +90,6 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
describe('removeSourceBranch', () => {
it('should set flag and call service then request main component to update the widget', (done) => {
- const vm = createComponent();
- spyOn(eventHub, '$emit');
spyOn(vm.service.mergeResource, 'save').and.returnValue(new Promise((resolve) => {
resolve({
data: {
@@ -148,31 +113,23 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
});
describe('template', () => {
- let vm;
- let el;
-
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
-
it('should have correct elements', () => {
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.innerText).toContain('to be merged automatically when the pipeline succeeds');
- expect(el.innerText).toContain('The changes will be merged into');
- expect(el.innerText).toContain(targetBranch);
- expect(el.innerText).toContain('The source branch will not be removed');
- expect(el.querySelector('.js-cancel-auto-merge').innerText).toContain('Cancel automatic merge');
- expect(el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeFalsy();
- expect(el.querySelector('.js-remove-source-branch').innerText).toContain('Remove source branch');
- expect(el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeFalsy();
+ expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
+ expect(vm.$el.innerText).toContain('to be merged automatically when the pipeline succeeds');
+ expect(vm.$el.innerText).toContain('The changes will be merged into');
+ expect(vm.$el.innerText).toContain(targetBranch);
+ expect(vm.$el.innerText).toContain('The source branch will not be removed');
+ expect(vm.$el.querySelector('.js-cancel-auto-merge').innerText).toContain('Cancel automatic merge');
+ expect(vm.$el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeFalsy();
+ expect(vm.$el.querySelector('.js-remove-source-branch').innerText).toContain('Remove source branch');
+ expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeFalsy();
});
it('should disable cancel auto merge button when the action is in progress', (done) => {
vm.isCancellingAutoMerge = true;
Vue.nextTick(() => {
- expect(el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeTruthy();
+ expect(vm.$el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeTruthy();
done();
});
});
@@ -181,7 +138,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
vm.mr.shouldRemoveSourceBranch = true;
Vue.nextTick(() => {
- const normalizedText = el.innerText.replace(/\s+/g, ' ');
+ const normalizedText = vm.$el.innerText.replace(/\s+/g, ' ');
expect(normalizedText).toContain('The source branch will be removed');
expect(normalizedText).not.toContain('The source branch will not be removed');
done();
@@ -192,7 +149,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
vm.mr.currentUserId = 4;
Vue.nextTick(() => {
- expect(el.querySelector('.js-remove-source-branch')).toEqual(null);
+ expect(vm.$el.querySelector('.js-remove-source-branch')).toEqual(null);
done();
});
});
@@ -201,7 +158,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
vm.isRemovingSourceBranch = true;
Vue.nextTick(() => {
- expect(el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeTruthy();
+ expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeTruthy();
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index 2dc3b72ea40..43a989393ba 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -1,108 +1,99 @@
import Vue from 'vue';
-import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged';
+import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-
-const targetBranch = 'foo';
-
-const createComponent = () => {
- const Component = Vue.extend(mergedComponent);
- const mr = {
- isRemovingSourceBranch: false,
- cherryPickInForkPath: false,
- canCherryPickInCurrentMR: true,
- revertInForkPath: false,
- canRevertInCurrentMR: true,
- canRemoveSourceBranch: true,
- sourceBranchRemoved: true,
- metrics: {
- mergedBy: {},
- mergedAt: 'mergedUpdatedAt',
- readableMergedAt: '',
- closedBy: {},
- closedAt: 'mergedUpdatedAt',
- readableClosedAt: '',
- },
- updatedAt: 'mrUpdatedAt',
- targetBranch,
- };
-
- const service = {
- removeSourceBranch() {},
- };
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr, service },
- });
-};
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetMerged', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = mergedComponent.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
-
- expect(service.type instanceof Object).toBeTruthy();
- expect(service.required).toBeTruthy();
- });
- });
-
- describe('components', () => {
- it('should have components added', () => {
- expect(mergedComponent.components['mr-widget-author-and-time']).toBeDefined();
- });
+ let vm;
+ const targetBranch = 'foo';
+
+ beforeEach(() => {
+ const Component = Vue.extend(mergedComponent);
+ const mr = {
+ isRemovingSourceBranch: false,
+ cherryPickInForkPath: false,
+ canCherryPickInCurrentMR: true,
+ revertInForkPath: false,
+ canRevertInCurrentMR: true,
+ canRemoveSourceBranch: true,
+ sourceBranchRemoved: true,
+ metrics: {
+ mergedBy: {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://localhost:3000/root',
+ avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ readableMergedAt: '',
+ closedBy: {},
+ closedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ readableClosedAt: '',
+ },
+ updatedAt: 'mergedUpdatedAt',
+ targetBranch,
+ };
+
+ const service = {
+ removeSourceBranch() {},
+ };
+
+ spyOn(eventHub, '$emit');
+
+ vm = mountComponent(Component, { mr, service });
});
- describe('data', () => {
- it('should have default data', () => {
- const data = mergedComponent.data();
-
- expect(data.isMakingRequest).toBeFalsy();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
describe('shouldShowRemoveSourceBranch', () => {
- it('should correct value when fields changed', () => {
- const vm = createComponent();
+ it('returns true when sourceBranchRemoved is false', () => {
vm.mr.sourceBranchRemoved = false;
- expect(vm.shouldShowRemoveSourceBranch).toBeTruthy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(true);
+ });
+ it('returns false wehn sourceBranchRemoved is true', () => {
vm.mr.sourceBranchRemoved = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ });
+ it('returns false when canRemoveSourceBranch is false', () => {
vm.mr.sourceBranchRemoved = false;
vm.mr.canRemoveSourceBranch = false;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ });
+ it('returns false when is making request', () => {
vm.mr.canRemoveSourceBranch = true;
vm.isMakingRequest = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ });
+ it('returns true when all are true', () => {
vm.mr.isRemovingSourceBranch = true;
vm.mr.canRemoveSourceBranch = true;
vm.isMakingRequest = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
});
});
+
describe('shouldShowSourceBranchRemoving', () => {
it('should correct value when fields changed', () => {
- const vm = createComponent();
vm.mr.sourceBranchRemoved = false;
- expect(vm.shouldShowSourceBranchRemoving).toBeFalsy();
+ expect(vm.shouldShowSourceBranchRemoving).toEqual(false);
vm.mr.sourceBranchRemoved = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
vm.mr.sourceBranchRemoved = false;
vm.isMakingRequest = true;
- expect(vm.shouldShowSourceBranchRemoving).toBeTruthy();
+ expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
vm.isMakingRequest = false;
vm.mr.isRemovingSourceBranch = true;
- expect(vm.shouldShowSourceBranchRemoving).toBeTruthy();
+ expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
});
});
});
@@ -110,8 +101,6 @@ describe('MRWidgetMerged', () => {
describe('methods', () => {
describe('removeSourceBranch', () => {
it('should set flag and call service then request main component to update the widget', (done) => {
- const vm = createComponent();
- spyOn(eventHub, '$emit');
spyOn(vm.service, 'removeSourceBranch').and.returnValue(new Promise((resolve) => {
resolve({
data: {
@@ -123,7 +112,7 @@ describe('MRWidgetMerged', () => {
vm.removeSourceBranch();
setTimeout(() => {
const args = eventHub.$emit.calls.argsFor(0);
- expect(vm.isMakingRequest).toBeTruthy();
+ expect(vm.isMakingRequest).toEqual(true);
expect(args[0]).toEqual('MRWidgetUpdateRequested');
expect(args[1]).not.toThrow();
done();
@@ -132,53 +121,50 @@ describe('MRWidgetMerged', () => {
});
});
- describe('template', () => {
- let vm;
- let el;
+ it('has merged by information', () => {
+ expect(vm.$el.textContent).toContain('Merged by');
+ expect(vm.$el.textContent).toContain('Administrator');
+ });
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
+ it('renders branch information', () => {
+ expect(vm.$el.textContent).toContain('The changes were merged into');
+ expect(vm.$el.textContent).toContain(targetBranch);
+ });
- it('should have correct elements', () => {
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.querySelector('.js-mr-widget-author')).toBeDefined();
- expect(el.innerText).toContain('The changes were merged into');
- expect(el.innerText).toContain(targetBranch);
- expect(el.innerText).toContain('The source branch has been removed');
- expect(el.innerText).toContain('Revert');
- expect(el.innerText).toContain('Cherry-pick');
- expect(el.innerText).not.toContain('You can remove source branch now');
- expect(el.innerText).not.toContain('The source branch is being removed');
- });
+ it('renders information about branch being removed', () => {
+ expect(vm.$el.textContent).toContain('The source branch has been removed');
+ });
- it('should not show source branch removed text', (done) => {
- vm.mr.sourceBranchRemoved = false;
+ it('shows revert and cherry-pick buttons', () => {
+ expect(vm.$el.textContent).toContain('Revert');
+ expect(vm.$el.textContent).toContain('Cherry-pick');
+ });
- Vue.nextTick(() => {
- expect(el.innerText).toContain('You can remove source branch now');
- expect(el.innerText).not.toContain('The source branch has been removed');
- done();
- });
+ it('should not show source branch removed text', (done) => {
+ vm.mr.sourceBranchRemoved = false;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.innerText).toContain('You can remove source branch now');
+ expect(vm.$el.innerText).not.toContain('The source branch has been removed');
+ done();
});
+ });
- it('should show source branch removing text', (done) => {
- vm.mr.isRemovingSourceBranch = true;
- vm.mr.sourceBranchRemoved = false;
+ it('should show source branch removing text', (done) => {
+ vm.mr.isRemovingSourceBranch = true;
+ vm.mr.sourceBranchRemoved = false;
- Vue.nextTick(() => {
- expect(el.innerText).toContain('The source branch is being removed');
- expect(el.innerText).not.toContain('You can remove source branch now');
- expect(el.innerText).not.toContain('The source branch has been removed');
- done();
- });
+ Vue.nextTick(() => {
+ expect(vm.$el.innerText).toContain('The source branch is being removed');
+ expect(vm.$el.innerText).not.toContain('You can remove source branch now');
+ expect(vm.$el.innerText).not.toContain('The source branch has been removed');
+ done();
});
+ });
- it('should use mergedEvent updatedAt as tooltip title', () => {
- expect(
- el.querySelector('time').getAttribute('title'),
- ).toBe('mergedUpdatedAt');
- });
+ it('should use mergedEvent mergedAt as tooltip title', () => {
+ expect(
+ vm.$el.querySelector('time').getAttribute('title'),
+ ).toBe('Jan 24, 2018 1:02pm GMT+0000');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
new file mode 100644
index 00000000000..0b2ed2d4086
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
@@ -0,0 +1,34 @@
+import Vue from 'vue';
+import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('MRWidgetMerging', () => {
+ let vm;
+ beforeEach(() => {
+ const Component = Vue.extend(mergingComponent);
+
+ vm = mountComponent(Component, { mr: {
+ targetBranchPath: '/branch-path',
+ targetBranch: 'branch',
+ } });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders information about merge request being merged', () => {
+ expect(
+ vm.$el.querySelector('.media-body').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
+ ).toContain('This merge request is in the process of being merged');
+ });
+
+ it('renders branch information', () => {
+ expect(
+ vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
+ ).toEqual('The changes will be merged into branch');
+ expect(
+ vm.$el.querySelector('a').getAttribute('href'),
+ ).toEqual('/branch-path');
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 11858e45386..073f26cc78f 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -170,14 +170,14 @@ describe('MRWidgetReadyToMerge', () => {
expect(vm.iconClass).toEqual('success');
});
- it('shows x for failed status', () => {
+ it('shows warning icon for failed status', () => {
vm.mr.hasCI = true;
- expect(vm.iconClass).toEqual('failed');
+ expect(vm.iconClass).toEqual('warning');
});
- it('shows x for merge not allowed', () => {
+ it('shows warning icon for merge not allowed', () => {
vm.mr.hasCI = true;
- expect(vm.iconClass).toEqual('failed');
+ expect(vm.iconClass).toEqual('warning');
});
});
@@ -404,7 +404,7 @@ describe('MRWidgetReadyToMerge', () => {
setTimeout(() => {
const statusBox = document.querySelector('.status-box');
- expect(statusBox.classList.contains('status-box-merged')).toBeTruthy();
+ expect(statusBox.classList.contains('status-box-mr-merged')).toBeTruthy();
expect(statusBox.textContent).toContain('Merged');
done();
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index ae494267659..3dd75307484 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -38,7 +38,7 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
"merged_at": "2017-04-07T15:39:25.696Z",
@@ -50,7 +50,7 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
"merge_user": null,
@@ -64,7 +64,7 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
"active": false,
@@ -159,10 +159,10 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
- "author_gravatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "author_gravatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"commit_url": "http://localhost:3000/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d",
"commit_path": "/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d"
},
diff --git a/spec/javascripts/vue_shared/components/loading_icon_spec.js b/spec/javascripts/vue_shared/components/loading_icon_spec.js
index 1baf3537741..5cd3466f501 100644
--- a/spec/javascripts/vue_shared/components/loading_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/loading_icon_spec.js
@@ -16,7 +16,8 @@ describe('Loading Icon Component', () => {
).toEqual('fa fa-spin fa-spinner fa-1x');
expect(component.$el.tagName).toEqual('DIV');
- expect(component.$el.classList.contains('text-center')).toEqual(true);
+ expect(component.$el.classList).toContain('text-center');
+ expect(component.$el.classList).toContain('loading-container');
});
it('should render accessibility attributes', () => {
diff --git a/spec/javascripts/vue_shared/components/modal_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
index fe75a86cac8..a5f9c75be4e 100644
--- a/spec/javascripts/vue_shared/components/modal_spec.js
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -25,7 +25,7 @@ describe('Modal', () => {
});
describe('with id', () => {
- it('does not render a primary button', () => {
+ describe('does not render a primary button', () => {
beforeEach(() => {
vm = mountComponent(modalComponent, {
id: 'my-modal',
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
index 20363e78094..2de108da2ac 100644
--- a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
@@ -21,22 +21,21 @@ describe('collapsedGroupedDatePicker', () => {
});
});
- it('toggleCollapse events', () => {
- const toggleCollapse = jasmine.createSpy();
-
+ describe('toggleCollapse events', () => {
beforeEach((done) => {
+ spyOn(vm, 'toggleSidebar');
vm.minDate = new Date('07/17/2016');
Vue.nextTick(done);
});
it('should emit when sidebar is toggled', () => {
vm.$el.querySelector('.gutter-toggle').click();
- expect(toggleCollapse).toHaveBeenCalled();
+ expect(vm.toggleSidebar).toHaveBeenCalled();
});
it('should emit when collapsed-calendar-icon is clicked', () => {
vm.$el.querySelector('.sidebar-collapsed-icon').click();
- expect(toggleCollapse).toHaveBeenCalled();
+ expect(vm.toggleSidebar).toHaveBeenCalled();
});
});
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 7e17457ce70..3ca4652f7cc 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -278,18 +278,19 @@ describe Banzai::Filter::RelativeLinkFilter do
expect(doc.at_css('a')['href']).to eq 'http://example.com'
end
- it 'supports Unicode filenames' do
+ it 'supports unescaped Unicode filenames' do
path = '/uploads/한글.png'
- escaped = Addressable::URI.escape(path)
+ doc = filter(link(path))
- # Stub these methods so the file doesn't actually need to be in the repo
- allow_any_instance_of(described_class)
- .to receive(:file_exists?).and_return(true)
- allow_any_instance_of(described_class)
- .to receive(:image?).with(path).and_return(true)
+ expect(doc.at_css('a')['href']).to eq("/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png")
+ end
+ it 'supports escaped Unicode filenames' do
+ path = '/uploads/한글.png'
+ escaped = Addressable::URI.escape(path)
doc = filter(image(escaped))
- expect(doc.at_css('img')['src']).to match "/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png"
+
+ expect(doc.at_css('img')['src']).to eq("/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png")
end
end
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index a6fbec295b5..cc202ce8bca 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -136,8 +136,8 @@ describe Gitlab::Auth do
it 'grants deploy key write permissions' do
project = create(:project)
- key = create(:deploy_key, can_push: true)
- create(:deploy_keys_project, deploy_key: key, project: project)
+ key = create(:deploy_key)
+ create(:deploy_keys_project, :write_access, deploy_key: key, project: project)
token = Gitlab::LfsToken.new(key).token
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
@@ -146,7 +146,7 @@ describe Gitlab::Auth do
it 'does not grant deploy key write permissions' do
project = create(:project)
- key = create(:deploy_key, can_push: true)
+ key = create(:deploy_key)
token = Gitlab::LfsToken.new(key).token
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index d21183b668b..c8df6dd2118 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :truncate, :migration, schema: 20171114162227 do
+describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :migration, schema: 20171114162227 do
let(:merge_request_diffs) { table(:merge_request_diffs) }
let(:merge_requests) { table(:merge_requests) }
diff --git a/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb b/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
index 7b5a00c6111..021e1d14b18 100644
--- a/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder do
+describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder, :delete do
let(:migration) { described_class.new }
before do
@@ -8,7 +8,7 @@ describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder do
end
describe '#perform' do
- it 'renames the path of system-uploads', :truncate do
+ it 'renames the path of system-uploads' do
upload = create(:upload, model: create(:project), path: 'uploads/system/project/avatar.jpg')
migration.perform('uploads/system/', 'uploads/-/system/')
diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb
index e99257e3481..4cdb679c97f 100644
--- a/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb
@@ -7,6 +7,10 @@ describe Gitlab::BackgroundMigration::PopulateMergeRequestMetricsWithEventsData,
.to receive(:commits_count=).and_return(nil)
end
+ after do
+ [MergeRequest, MergeRequestDiff].each(&:reset_column_information)
+ end
+
describe '#perform' do
let(:mr_with_event) { create(:merge_request) }
let!(:merged_event) { create(:event, :merged, target: mr_with_event) }
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index 05e2d94cbd6..7549e9941b6 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -217,11 +217,58 @@ describe Gitlab::Ci::Ansi2html do
"#{section_end[0...-5]}</div>"
end
- it "prints light red" do
- text = "#{section_start}\e[91mHello\e[0m\n#{section_end}"
- html = %{#{section_start_html}<span class="term-fg-l-red">Hello</span><br>#{section_end_html}}
+ shared_examples 'forbidden char in section_name' do
+ it 'ignores sections' do
+ text = "#{section_start}Some text#{section_end}"
+ html = text.gsub("\033[0K", '').gsub('<', '&lt;')
- expect(convert_html(text)).to eq(html)
+ expect(convert_html(text)).to eq(html)
+ end
+ end
+
+ shared_examples 'a legit section' do
+ let(:text) { "#{section_start}Some text#{section_end}" }
+
+ it 'prints light red' do
+ text = "#{section_start}\e[91mHello\e[0m\n#{section_end}"
+ html = %{#{section_start_html}<span class="term-fg-l-red">Hello</span><br>#{section_end_html}}
+
+ expect(convert_html(text)).to eq(html)
+ end
+
+ it 'begins with a section_start html marker' do
+ expect(convert_html(text)).to start_with(section_start_html)
+ end
+
+ it 'ends with a section_end html marker' do
+ expect(convert_html(text)).to end_with(section_end_html)
+ end
+ end
+
+ it_behaves_like 'a legit section'
+
+ context 'section name includes $' do
+ let(:section_name) { 'my_$ection'}
+
+ it_behaves_like 'forbidden char in section_name'
+ end
+
+ context 'section name includes <' do
+ let(:section_name) { '<a_tag>'}
+
+ it_behaves_like 'forbidden char in section_name'
+ end
+
+ context 'section name contains .-_' do
+ let(:section_name) { 'a.Legit-SeCtIoN_namE' }
+
+ it_behaves_like 'a legit section'
+ end
+
+ it 'do not allow XSS injections' do
+ text = "#{section_start}section_end:1:2<script>alert('XSS Hack!');</script>#{section_end}"
+
+ expect(convert_html(text)).not_to include('<script>')
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb
index 5d4de60bc8a..3cbf19bea8b 100644
--- a/spec/lib/gitlab/ci/config/entry/key_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb
@@ -4,6 +4,26 @@ describe Gitlab::Ci::Config::Entry::Key do
let(:entry) { described_class.new(config) }
describe 'validations' do
+ shared_examples 'key with slash' do
+ it 'is invalid' do
+ expect(entry).not_to be_valid
+ end
+
+ it 'reports errors with config value' do
+ expect(entry.errors).to include 'key config cannot contain the "/" character'
+ end
+ end
+
+ shared_examples 'key with only dots' do
+ it 'is invalid' do
+ expect(entry).not_to be_valid
+ end
+
+ it 'reports errors with config value' do
+ expect(entry.errors).to include 'key config cannot be "." or ".."'
+ end
+ end
+
context 'when entry config value is correct' do
let(:config) { 'test' }
@@ -30,6 +50,48 @@ describe Gitlab::Ci::Config::Entry::Key do
end
end
end
+
+ context 'when entry value contains slash' do
+ let(:config) { 'key/with/some/slashes' }
+
+ it_behaves_like 'key with slash'
+ end
+
+ context 'when entry value contains URI encoded slash (%2F)' do
+ let(:config) { 'key%2Fwith%2Fsome%2Fslashes' }
+
+ it_behaves_like 'key with slash'
+ end
+
+ context 'when entry value is a dot' do
+ let(:config) { '.' }
+
+ it_behaves_like 'key with only dots'
+ end
+
+ context 'when entry value is two dots' do
+ let(:config) { '..' }
+
+ it_behaves_like 'key with only dots'
+ end
+
+ context 'when entry value is a URI encoded dot (%2E)' do
+ let(:config) { '%2e' }
+
+ it_behaves_like 'key with only dots'
+ end
+
+ context 'when entry value is two URI encoded dots (%2E)' do
+ let(:config) { '%2E%2e' }
+
+ it_behaves_like 'key with only dots'
+ end
+
+ context 'when entry value is one dot and one URI encoded dot' do
+ let(:config) { '.%2e' }
+
+ it_behaves_like 'key with only dots'
+ end
end
describe '.default' do
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 43761c2fe0c..1de3a14b809 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -1038,7 +1038,7 @@ describe Gitlab::Database::MigrationHelpers do
end
describe '#change_column_type_using_background_migration' do
- let!(:issue) { create(:issue) }
+ let!(:issue) { create(:issue, :closed, closed_at: Time.zone.now) }
let(:issue_model) do
Class.new(ActiveRecord::Base) do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index 596cc435bd9..cc7cb3f23fd 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index 1143182531f..f31475dbd71 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :delete do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
let(:namespace) { create(:group, name: 'the-path') }
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index e850b5cd6a4..0958144643b 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :delete do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
let(:project) do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
index 7695b95dc57..1d31f96159c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
@@ -13,7 +13,7 @@ shared_examples 'renames child namespaces' do |type|
end
end
-describe Gitlab::Database::RenameReservedPathsMigration::V1, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1, :delete do
let(:subject) { FakeRenameReservedPathMigrationV1.new }
before do
diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
new file mode 100644
index 00000000000..5d22dcfb508
--- /dev/null
+++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::Git::AttributesAtRefParser, seed_helper: true do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+
+ subject { described_class.new(repository, 'lfs') }
+
+ it 'loads .gitattributes blob' do
+ repository.raw # Initialize repository in advance since this also checks attributes
+
+ expected_filter = 'filter=lfs diff=lfs merge=lfs'
+ receive_blob = receive(:new).with(a_string_including(expected_filter))
+ expect(Gitlab::Git::AttributesParser).to receive_blob.and_call_original
+
+ subject
+ end
+
+ it 'handles missing blobs' do
+ expect { described_class.new(repository, 'non-existant-branch') }.not_to raise_error
+ end
+
+ describe '#attributes' do
+ it 'returns the attributes as a Hash' do
+ expect(subject.attributes('test.lfs')['filter']).to eq('lfs')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/attributes_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb
index b715fc3410a..323334e99a5 100644
--- a/spec/lib/gitlab/git/attributes_spec.rb
+++ b/spec/lib/gitlab/git/attributes_parser_spec.rb
@@ -1,11 +1,10 @@
require 'spec_helper'
-describe Gitlab::Git::Attributes, seed_helper: true do
- let(:path) do
- File.join(SEED_STORAGE_PATH, 'with-git-attributes.git')
- end
+describe Gitlab::Git::AttributesParser, seed_helper: true do
+ let(:attributes_path) { File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info', 'attributes') }
+ let(:data) { File.read(attributes_path) }
- subject { described_class.new(path) }
+ subject { described_class.new(data) }
describe '#attributes' do
context 'using a path with attributes' do
@@ -66,6 +65,26 @@ describe Gitlab::Git::Attributes, seed_helper: true do
expect(subject.attributes('test.foo')).to eq({})
end
end
+
+ context 'when attributes data is a file handle' do
+ subject do
+ File.open(attributes_path, 'r') do |file_handle|
+ described_class.new(file_handle)
+ end
+ end
+
+ it 'returns the attributes as a Hash' do
+ expect(subject.attributes('test.txt')).to eq({ 'text' => true })
+ end
+ end
+
+ context 'when attributes data is nil' do
+ let(:data) { nil }
+
+ it 'returns an empty Hash' do
+ expect(subject.attributes('test.foo')).to eq({})
+ end
+ end
end
describe '#patterns' do
@@ -74,14 +93,14 @@ describe Gitlab::Git::Attributes, seed_helper: true do
end
it 'parses an entry that uses a tab to separate the pattern and attributes' do
- expect(subject.patterns[File.join(path, '*.md')])
+ expect(subject.patterns[File.join('/', '*.md')])
.to eq({ 'gitlab-language' => 'markdown' })
end
it 'stores patterns in reverse order' do
first = subject.patterns.to_a[0]
- expect(first[0]).to eq(File.join(path, 'bla/bla.txt'))
+ expect(first[0]).to eq(File.join('/', 'bla/bla.txt'))
end
# It's a bit hard to test for something _not_ being processed. As such we'll
@@ -89,14 +108,6 @@ describe Gitlab::Git::Attributes, seed_helper: true do
it 'ignores any comments and empty lines' do
expect(subject.patterns.length).to eq(10)
end
-
- it 'does not parse anything when the attributes file does not exist' do
- expect(File).to receive(:exist?)
- .with(File.join(path, 'info/attributes'))
- .and_return(false)
-
- expect(subject.patterns).to eq({})
- end
end
describe '#parse_attributes' do
@@ -132,17 +143,9 @@ describe Gitlab::Git::Attributes, seed_helper: true do
expect { |b| subject.each_line(&b) }.to yield_successive_args(*args)
end
- it 'does not yield when the attributes file does not exist' do
- expect(File).to receive(:exist?)
- .with(File.join(path, 'info/attributes'))
- .and_return(false)
-
- expect { |b| subject.each_line(&b) }.not_to yield_control
- end
-
it 'does not yield when the attributes file has an unsupported encoding' do
- path = File.join(SEED_STORAGE_PATH, 'with-invalid-git-attributes.git')
- attrs = described_class.new(path)
+ path = File.join(SEED_STORAGE_PATH, 'with-invalid-git-attributes.git', 'info', 'attributes')
+ attrs = described_class.new(File.read(path))
expect { |b| attrs.each_line(&b) }.not_to yield_control
end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 07eb5b82d5f..8ac960133c5 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -16,6 +16,18 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
shared_examples 'finding blobs' do
+ context 'nil path' do
+ let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
+
+ it { expect(blob).to eq(nil) }
+ end
+
+ context 'blank path' do
+ let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, '') }
+
+ it { expect(blob).to eq(nil) }
+ end
+
context 'file in subdir' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
@@ -248,29 +260,57 @@ describe Gitlab::Git::Blob, seed_helper: true do
)
end
- it 'returns a list of Gitlab::Git::Blob' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
+ shared_examples 'fetching batch of LFS pointers' do
+ it 'returns a list of Gitlab::Git::Blob' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- end
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+ it 'accepts blob IDs as a lazy enumerator' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
- expect(blobs).to eq([])
- end
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
+
+ it 'handles empty list of IDs gracefully' do
+ blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
+ blobs_2 = described_class.batch_lfs_pointers(repository, [])
- it 'silently ignores non lfs objects' do
- blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ expect(blobs_1).to eq([])
+ expect(blobs_2).to eq([])
+ end
+
+ it 'silently ignores tree objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+
+ expect(blobs).to eq([])
+ end
+
+ it 'silently ignores non lfs objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
- expect(blobs).to eq([])
+ expect(blobs).to eq([])
+ end
+
+ it 'avoids loading large blobs into memory' do
+ # This line could call `lookup` on `repository`, so do here before mocking.
+ non_lfs_blob_id = non_lfs_blob.id
+
+ expect(repository).not_to receive(:lookup)
+
+ described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
+ end
end
- it 'avoids loading large blobs into memory' do
- expect(repository).not_to receive(:lookup)
+ context 'when Gitaly batch_lfs_pointers is enabled' do
+ it_behaves_like 'fetching batch of LFS pointers'
+ end
- described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ context 'when Gitaly batch_lfs_pointers is disabled', :disable_gitaly do
+ it_behaves_like 'fetching batch of LFS pointers'
end
end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 6a07a3ca8b8..85e6efd7ca2 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -388,6 +388,84 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
end
+
+ describe '.extract_signature' do
+ subject { described_class.extract_signature(repository, commit_id) }
+
+ shared_examples '.extract_signature' do
+ context 'when the commit is signed' do
+ let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
+
+ it 'returns signature and signed text' do
+ signature, signed_text = subject
+
+ expected_signature = <<~SIGNATURE
+ -----BEGIN PGP SIGNATURE-----
+ Version: GnuPG/MacGPG2 v2.0.22 (Darwin)
+ Comment: GPGTools - https://gpgtools.org
+
+ iQEcBAABCgAGBQJTDvaZAAoJEGJ8X1ifRn8XfvYIAMuB0yrbTGo1BnOSoDfyrjb0
+ Kw2EyUzvXYL72B63HMdJ+/0tlSDC6zONF3fc+bBD8z+WjQMTbwFNMRbSSy2rKEh+
+ mdRybOP3xBIMGgEph0/kmWln39nmFQBsPRbZBWoU10VfI/ieJdEOgOphszgryRar
+ TyS73dLBGE9y9NIININVaNISet9D9QeXFqc761CGjh4YIghvPpi+YihMWapGka6v
+ hgKhX+hc5rj+7IEE0CXmlbYR8OYvAbAArc5vJD7UTxAY4Z7/l9d6Ydt9GQ25khfy
+ ANFgltYzlR6evLFmDjssiP/mx/ZMN91AL0ueJ9nNGv411Mu2CUW+tDCaQf35mdc=
+ =j51i
+ -----END PGP SIGNATURE-----
+ SIGNATURE
+
+ expect(signature).to eq(expected_signature.chomp)
+ expect(signature).to be_a_binary_string
+
+ expected_signed_text = <<~SIGNED_TEXT
+ tree 22bfa2fbd217df24731f43ff43a4a0f8db759dae
+ parent ae73cb07c9eeaf35924a10f713b364d32b2dd34f
+ author Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> 1393489561 +0200
+ committer Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> 1393489561 +0200
+
+ Feature added
+
+ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
+ SIGNED_TEXT
+
+ expect(signed_text).to eq(expected_signed_text)
+ expect(signed_text).to be_a_binary_string
+ end
+ end
+
+ context 'when the commit has no signature' do
+ let(:commit_id) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+
+ context 'when the commit cannot be found' do
+ let(:commit_id) { Gitlab::Git::BLANK_SHA }
+
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+
+ context 'when the commit ID is invalid' do
+ let(:commit_id) { '4b4918a572fa86f9771e5ba40fbd48e' }
+
+ it 'raises ArgumentError' do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ context 'with gitaly' do
+ it_behaves_like '.extract_signature'
+ end
+
+ context 'without gitaly', :skip_gitaly_mock do
+ it_behaves_like '.extract_signature'
+ end
+ end
end
describe '#init_from_rugged' do
diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb
index 78e4fbca28e..f4b964e1ee9 100644
--- a/spec/lib/gitlab/git/gitlab_projects_spec.rb
+++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb
@@ -219,6 +219,9 @@ describe Gitlab::Git::GitlabProjects do
before do
FileUtils.mkdir_p(dest_repos_path)
+
+ # Undo spec_helper stub that deletes hooks
+ allow_any_instance_of(described_class).to receive(:fork_repository).and_call_original
end
after do
diff --git a/spec/lib/gitlab/git/info_attributes_spec.rb b/spec/lib/gitlab/git/info_attributes_spec.rb
new file mode 100644
index 00000000000..ea84909c3e0
--- /dev/null
+++ b/spec/lib/gitlab/git/info_attributes_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Gitlab::Git::InfoAttributes, seed_helper: true do
+ let(:path) do
+ File.join(SEED_STORAGE_PATH, 'with-git-attributes.git')
+ end
+
+ subject { described_class.new(path) }
+
+ describe '#attributes' do
+ context 'using a path with attributes' do
+ it 'returns the attributes as a Hash' do
+ expect(subject.attributes('test.txt')).to eq({ 'text' => true })
+ end
+
+ it 'returns an empty Hash for a defined path without attributes' do
+ expect(subject.attributes('bla/bla.txt')).to eq({})
+ end
+ end
+ end
+
+ describe '#parser' do
+ it 'parses a file with entries' do
+ expect(subject.patterns).to be_an_instance_of(Hash)
+ expect(subject.patterns["/*.txt"]).to eq({ 'text' => true })
+ end
+
+ it 'does not parse anything when the attributes file does not exist' do
+ expect(File).to receive(:exist?)
+ .with(File.join(path, 'info/attributes'))
+ .and_return(false)
+
+ expect(subject.patterns).to eq({})
+ end
+
+ it 'does not parse attributes files with unsupported encoding' do
+ path = File.join(SEED_STORAGE_PATH, 'with-invalid-git-attributes.git')
+ subject = described_class.new(path)
+
+ expect(subject.patterns).to eq({})
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 3cf165716bd..935d1df6dad 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -981,6 +981,16 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
end
+
+ context 'limit validation' do
+ where(:limit) do
+ [0, nil, '', 'foo']
+ end
+
+ with_them do
+ it { expect { repository.log(limit: limit) }.to raise_error(ArgumentError) }
+ end
+ end
end
describe "#rugged_commits_between" do
@@ -1964,6 +1974,75 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { expect(subject.repository_relative_path).to eq(repository.relative_path) }
end
+ describe '#bundle_to_disk' do
+ shared_examples 'bundling to disk' do
+ let(:save_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") }
+
+ after do
+ FileUtils.rm_rf(save_path)
+ end
+
+ it 'saves a bundle to disk' do
+ repository.bundle_to_disk(save_path)
+
+ success = system(
+ *%W(#{Gitlab.config.git.bin_path} -C #{repository.path} bundle verify #{save_path}),
+ [:out, :err] => '/dev/null'
+ )
+ expect(success).to be true
+ end
+ end
+
+ context 'when Gitaly bundle_to_disk feature is enabled' do
+ it_behaves_like 'bundling to disk'
+ end
+
+ context 'when Gitaly bundle_to_disk feature is disabled', :disable_gitaly do
+ it_behaves_like 'bundling to disk'
+ end
+ end
+
+ describe '#create_from_bundle' do
+ shared_examples 'creating repo from bundle' do
+ let(:bundle_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") }
+ let(:project) { create(:project) }
+ let(:imported_repo) { project.repository.raw }
+
+ before do
+ expect(repository.bundle_to_disk(bundle_path)).to be true
+ end
+
+ after do
+ FileUtils.rm_rf(bundle_path)
+ end
+
+ it 'creates a repo from a bundle file' do
+ expect(imported_repo).not_to exist
+
+ result = imported_repo.create_from_bundle(bundle_path)
+
+ expect(result).to be true
+ expect(imported_repo).to exist
+ expect { imported_repo.fsck }.not_to raise_exception
+ end
+
+ it 'creates a symlink to the global hooks dir' do
+ imported_repo.create_from_bundle(bundle_path)
+ hooks_path = File.join(imported_repo.path, 'hooks')
+
+ expect(File.readlink(hooks_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
+ end
+ end
+
+ context 'when Gitaly create_repo_from_bundle feature is enabled' do
+ it_behaves_like 'creating repo from bundle'
+ end
+
+ context 'when Gitaly create_repo_from_bundle feature is disabled', :disable_gitaly do
+ it_behaves_like 'creating repo from bundle'
+ end
+ end
+
context 'gitlab_projects commands' do
let(:gitlab_projects) { repository.gitlab_projects }
let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 4290fbb0087..2009a8ac48c 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -51,12 +51,12 @@ describe Gitlab::GitAccess do
context 'when the project exists' do
context 'when actor exists' do
context 'when actor is a DeployKey' do
- let(:deploy_key) { create(:deploy_key, user: user, can_push: true) }
+ let(:deploy_key) { create(:deploy_key, user: user) }
let(:actor) { deploy_key }
context 'when the DeployKey has access to the project' do
before do
- deploy_key.projects << project
+ deploy_key.deploy_keys_projects.create(project: project, can_push: true)
end
it 'allows push and pull access' do
@@ -696,15 +696,13 @@ describe Gitlab::GitAccess do
end
describe 'deploy key permissions' do
- let(:key) { create(:deploy_key, user: user, can_push: can_push) }
+ let(:key) { create(:deploy_key, user: user) }
let(:actor) { key }
context 'when deploy_key can push' do
- let(:can_push) { true }
-
context 'when project is authorized' do
before do
- key.projects << project
+ key.deploy_keys_projects.create(project: project, can_push: true)
end
it { expect { push_access_check }.not_to raise_error }
@@ -732,11 +730,9 @@ describe Gitlab::GitAccess do
end
context 'when deploy_key cannot push' do
- let(:can_push) { false }
-
context 'when project is authorized' do
before do
- key.projects << project
+ key.deploy_keys_projects.create(project: project, can_push: false)
end
it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) }
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index b2275119a04..3722a91c050 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -131,6 +131,29 @@ describe Gitlab::GitalyClient::CommitService do
end
end
+ describe '#commit_count' do
+ before do
+ expect_any_instance_of(Gitaly::CommitService::Stub)
+ .to receive(:count_commits)
+ .with(gitaly_request_with_path(storage_name, relative_path),
+ kind_of(Hash))
+ .and_return([])
+ end
+
+ it 'sends a commit_count message' do
+ client.commit_count(revision)
+ end
+
+ context 'with UTF-8 params strings' do
+ let(:revision) { "branch\u011F" }
+ let(:path) { "foo/\u011F.txt" }
+
+ it 'handles string encodings correctly' do
+ client.commit_count(revision, path: path)
+ end
+ end
+ end
+
describe '#find_commit' do
let(:revision) { '4b825dc642cb6eb9a060e54bf8d69288fbee4904' }
it 'sends an RPC request' do
diff --git a/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb b/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
new file mode 100644
index 00000000000..2c7e5eb5787
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::HealthCheckService do
+ let(:project) { create(:project) }
+ let(:storage_name) { project.repository_storage }
+
+ subject { described_class.new(storage_name) }
+
+ describe '#check' do
+ it 'successfully sends a health check request' do
+ expect(Gitlab::GitalyClient).to receive(:call).with(
+ storage_name,
+ :health_check,
+ :check,
+ instance_of(Grpc::Health::V1::HealthCheckRequest),
+ timeout: Gitlab::GitalyClient.fast_timeout).and_call_original
+
+ expect(subject.check).to eq({ success: true })
+ end
+
+ it 'receives an unsuccessful health check request' do
+ expect_any_instance_of(Grpc::Health::V1::Health::Stub)
+ .to receive(:check)
+ .and_return(double(status: false))
+
+ expect(subject.check).to eq({ success: false })
+ end
+
+ it 'gracefully handles gRPC error' do
+ expect(Gitlab::GitalyClient).to receive(:call).with(
+ storage_name,
+ :health_check,
+ :check,
+ instance_of(Grpc::Health::V1::HealthCheckRequest),
+ timeout: Gitlab::GitalyClient.fast_timeout)
+ .and_raise(GRPC::Unavailable.new('Connection refused'))
+
+ expect(subject.check).to eq({ success: false, message: '14:Connection refused' })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
index 9d540446532..872377c93d8 100644
--- a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
@@ -44,4 +44,18 @@ describe Gitlab::GitalyClient::RemoteService do
expect(client.fetch_internal_remote(remote_repository)).to be(true)
end
end
+
+ describe '#update_remote_mirror' do
+ let(:ref_name) { 'remote_mirror_1' }
+ let(:only_branches_matching) { ['my-branch', 'master'] }
+
+ it 'sends an update_remote_mirror message' do
+ expect_any_instance_of(Gitaly::RemoteService::Stub)
+ .to receive(:update_remote_mirror)
+ .with(kind_of(Enumerator), kind_of(Hash))
+ .and_return(double(:update_remote_mirror_response))
+
+ client.update_remote_mirror(ref_name, only_branches_matching)
+ end
+ end
end
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index 309b7338ef0..81bcd8c28ed 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -3,6 +3,31 @@ require 'spec_helper'
# We stub Gitaly in `spec/support/gitaly.rb` for other tests. We don't want
# those stubs while testing the GitalyClient itself.
describe Gitlab::GitalyClient, skip_gitaly_mock: true do
+ describe '.stub_class' do
+ it 'returns the gRPC health check stub' do
+ expect(described_class.stub_class(:health_check)).to eq(::Grpc::Health::V1::Health::Stub)
+ end
+
+ it 'returns a Gitaly stub' do
+ expect(described_class.stub_class(:ref_service)).to eq(::Gitaly::RefService::Stub)
+ end
+ end
+
+ describe '.stub_address' do
+ it 'returns the same result after being called multiple times' do
+ address = 'localhost:9876'
+ prefixed_address = "tcp://#{address}"
+
+ allow(Gitlab.config.repositories).to receive(:storages).and_return({
+ 'default' => { 'gitaly_address' => prefixed_address }
+ })
+
+ 2.times do
+ expect(described_class.stub_address('default')).to eq('localhost:9876')
+ end
+ end
+ end
+
describe '.stub' do
# Notice that this is referring to gRPC "stubs", not rspec stubs
before do
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
index d72572cd510..44695acbe7d 100644
--- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
@@ -244,7 +244,7 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns true when a commit exists' do
expect(project.repository)
- .to receive(:lookup)
+ .to receive(:commit)
.with('123')
.and_return(double(:commit))
@@ -253,9 +253,9 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns false when a commit does not exist' do
expect(project.repository)
- .to receive(:lookup)
+ .to receive(:commit)
.with('123')
- .and_raise(Rugged::OdbError)
+ .and_return(nil)
expect(importer.commit_exists?('123')).to eq(false)
end
diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb
index a6c99bc07d4..e3bf2801406 100644
--- a/spec/lib/gitlab/gpg/commit_spec.rb
+++ b/spec/lib/gitlab/gpg/commit_spec.rb
@@ -38,8 +38,8 @@ describe Gitlab::Gpg::Commit do
end
before do
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User1.signed_commit_signature,
@@ -77,8 +77,8 @@ describe Gitlab::Gpg::Commit do
end
before do
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User3.signed_commit_signature,
@@ -116,8 +116,8 @@ describe Gitlab::Gpg::Commit do
end
before do
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User1.signed_commit_signature,
@@ -151,8 +151,8 @@ describe Gitlab::Gpg::Commit do
end
before do
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User1.signed_commit_signature,
@@ -187,8 +187,8 @@ describe Gitlab::Gpg::Commit do
end
before do
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User1.signed_commit_signature,
@@ -217,8 +217,8 @@ describe Gitlab::Gpg::Commit do
let!(:commit) { create :commit, project: project, sha: commit_sha }
before do
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User1.signed_commit_signature,
diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
index d6000af0ecd..c034eccf2a6 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -26,8 +26,8 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
before do
allow_any_instance_of(Project).to receive(:commit).and_return(commit)
- allow(Rugged::Commit).to receive(:extract_signature)
- .with(Rugged::Repository, commit_sha)
+ allow(Gitlab::Git::Commit).to receive(:extract_signature)
+ .with(Gitlab::Git::Repository, commit_sha)
.and_return(signature)
end
diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
new file mode 100644
index 00000000000..724beefff69
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+
+describe Gitlab::HealthChecks::GitalyCheck do
+ let(:result_class) { Gitlab::HealthChecks::Result }
+ let(:repository_storages) { ['default'] }
+
+ before do
+ allow(described_class).to receive(:repository_storages) { repository_storages }
+ end
+
+ describe '#readiness' do
+ subject { described_class.readiness }
+
+ before do
+ expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check)
+ end
+
+ context 'Gitaly server is up' do
+ let(:gitaly_check) { double(check: { success: true }) }
+
+ it { is_expected.to eq([result_class.new(true, nil, shard: 'default')]) }
+ end
+
+ context 'Gitaly server is down' do
+ let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
+
+ it { is_expected.to eq([result_class.new(false, 'Connection refused', shard: 'default')]) }
+ end
+ end
+
+ describe '#metrics' do
+ subject { described_class.metrics }
+
+ before do
+ expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check)
+ end
+
+ context 'Gitaly server is up' do
+ let(:gitaly_check) { double(check: { success: true }) }
+
+ it 'provides metrics' do
+ expect(subject).to all(have_attributes(labels: { shard: 'default' }))
+ expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_success', value: 1))
+ expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_latency_seconds', value: be >= 0))
+ end
+ end
+
+ context 'Gitaly server is down' do
+ let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
+
+ it 'provides metrics' do
+ expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_success', value: 0))
+ expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_latency_seconds', value: be >= 0))
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 162b776e107..5cdc5138fda 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -12,30 +12,61 @@ describe Gitlab::ImportExport::FileImporter do
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
-
+ allow(SecureRandom).to receive(:hex).and_return('abcd')
setup_files
-
- described_class.import(archive_file: '', shared: shared)
end
after do
FileUtils.rm_rf(export_path)
end
- it 'removes symlinks in root folder' do
- expect(File.exist?(symlink_file)).to be false
- end
+ context 'normal run' do
+ before do
+ described_class.import(archive_file: '', shared: shared)
+ end
- it 'removes hidden symlinks in root folder' do
- expect(File.exist?(hidden_symlink_file)).to be false
- end
+ it 'removes symlinks in root folder' do
+ expect(File.exist?(symlink_file)).to be false
+ end
+
+ it 'removes hidden symlinks in root folder' do
+ expect(File.exist?(hidden_symlink_file)).to be false
+ end
+
+ it 'removes symlinks in subfolders' do
+ expect(File.exist?(subfolder_symlink_file)).to be false
+ end
- it 'removes symlinks in subfolders' do
- expect(File.exist?(subfolder_symlink_file)).to be false
+ it 'does not remove a valid file' do
+ expect(File.exist?(valid_file)).to be true
+ end
+
+ it 'creates the file in the right subfolder' do
+ expect(shared.export_path).to include('test/abcd')
+ end
end
- it 'does not remove a valid file' do
- expect(File.exist?(valid_file)).to be true
+ context 'error' do
+ before do
+ allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
+ described_class.import(archive_file: '', shared: shared)
+ end
+
+ it 'removes symlinks in root folder' do
+ expect(File.exist?(symlink_file)).to be false
+ end
+
+ it 'removes hidden symlinks in root folder' do
+ expect(File.exist?(hidden_symlink_file)).to be false
+ end
+
+ it 'removes symlinks in subfolders' do
+ expect(File.exist?(subfolder_symlink_file)).to be false
+ end
+
+ it 'does not remove a valid file' do
+ expect(File.exist?(valid_file)).to be true
+ end
end
def setup_files
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 4cf33778d15..b6c1f0c81cb 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -7096,7 +7096,7 @@
"project_id": 5,
"created_at": "2016-06-14T15:01:51.232Z",
"updated_at": "2016-06-14T15:01:51.232Z",
- "active": false,
+ "active": true,
"properties": {
},
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 08e5bbbd400..5804c45871e 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -164,6 +164,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(saved_project_json['services'].first['type']).to eq('CustomIssueTrackerService')
end
+ it 'saves the properties for a service' do
+ expect(saved_project_json['services'].first['properties']).to eq('one' => 'value')
+ end
+
it 'has project feature' do
project_feature = saved_project_json['project_feature']
expect(project_feature).not_to be_empty
@@ -279,7 +283,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
commit_id: ci_build.pipeline.sha)
create(:event, :created, target: milestone, project: project, author: user)
- create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker')
+ create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker', properties: { one: 'value' })
create(:project_custom_attribute, project: project)
create(:project_custom_attribute, project: project)
diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb
new file mode 100644
index 00000000000..4a43dbb2371
--- /dev/null
+++ b/spec/lib/gitlab/profiler_spec.rb
@@ -0,0 +1,156 @@
+require 'spec_helper'
+
+describe Gitlab::Profiler do
+ RSpec::Matchers.define_negated_matcher :not_change, :change
+
+ let(:null_logger) { Logger.new('/dev/null') }
+ let(:private_token) { 'private' }
+
+ describe '.profile' do
+ let(:app) { double(:app) }
+
+ before do
+ allow(ActionDispatch::Integration::Session).to receive(:new).and_return(app)
+ allow(app).to receive(:get)
+ end
+
+ it 'returns a profile result' do
+ expect(described_class.profile('/')).to be_an_instance_of(RubyProf::Profile)
+ end
+
+ it 'uses the custom logger given' do
+ expect(described_class).to receive(:create_custom_logger)
+ .with(null_logger, private_token: anything)
+ .and_call_original
+
+ described_class.profile('/', logger: null_logger)
+ end
+
+ it 'sends a POST request when data is passed' do
+ post_data = '{"a":1}'
+
+ expect(app).to receive(:post).with(anything, post_data, anything)
+
+ described_class.profile('/', post_data: post_data)
+ end
+
+ it 'uses the private_token for auth if given' do
+ expect(app).to receive(:get).with('/', nil, 'Private-Token' => private_token)
+ expect(app).to receive(:get).with('/api/v4/users')
+
+ described_class.profile('/', private_token: private_token)
+ end
+
+ it 'uses the user for auth if given' do
+ user = double(:user)
+ user_token = 'user'
+
+ allow(user).to receive_message_chain(:personal_access_tokens, :active, :pluck, :first).and_return(user_token)
+
+ expect(app).to receive(:get).with('/', nil, 'Private-Token' => user_token)
+ expect(app).to receive(:get).with('/api/v4/users')
+
+ described_class.profile('/', user: user)
+ end
+
+ it 'uses the private_token for auth if both it and user are set' do
+ user = double(:user)
+ user_token = 'user'
+
+ allow(user).to receive_message_chain(:personal_access_tokens, :active, :pluck, :first).and_return(user_token)
+
+ expect(app).to receive(:get).with('/', nil, 'Private-Token' => private_token)
+ expect(app).to receive(:get).with('/api/v4/users')
+
+ described_class.profile('/', user: user, private_token: private_token)
+ end
+ end
+
+ describe '.create_custom_logger' do
+ it 'does nothing when nil is passed' do
+ expect(described_class.create_custom_logger(nil)).to be_nil
+ end
+
+ context 'the new logger' do
+ let(:custom_logger) do
+ described_class.create_custom_logger(null_logger, private_token: private_token)
+ end
+
+ it 'does not affect the existing logger' do
+ expect(null_logger).not_to receive(:debug)
+ expect(custom_logger).to receive(:debug).and_call_original
+
+ custom_logger.debug('Foo')
+ end
+
+ it 'strips out the private token' do
+ expect(custom_logger).to receive(:add) do |severity, _progname, message|
+ expect(severity).to eq(Logger::DEBUG)
+ expect(message).to include('public').and include(described_class::FILTERED_STRING)
+ expect(message).not_to include(private_token)
+ end
+
+ custom_logger.debug("public #{private_token}")
+ end
+
+ it 'tracks model load times by model' do
+ custom_logger.debug('This is not a model load')
+ custom_logger.debug('User Load (1.2ms)')
+ custom_logger.debug('User Load (1.3ms)')
+ custom_logger.debug('Project Load (10.4ms)')
+
+ expect(custom_logger.load_times_by_model).to eq('User' => 2.5,
+ 'Project' => 10.4)
+ end
+
+ it 'logs the backtrace, ignoring lines as appropriate' do
+ # Skip Rails's backtrace cleaning.
+ allow(Rails.backtrace_cleaner).to receive(:clean, &:itself)
+
+ expect(custom_logger).to receive(:add)
+ .with(Logger::DEBUG,
+ anything,
+ a_string_matching(File.basename(__FILE__)))
+ .twice
+
+ expect(custom_logger).not_to receive(:add).with(Logger::DEBUG,
+ anything,
+ a_string_matching('lib/gitlab/profiler.rb'))
+
+ # Force a part of the backtrace to be in the (ignored) profiler source
+ # file.
+ described_class.with_custom_logger(nil) { custom_logger.debug('Foo') }
+ end
+ end
+ end
+
+ describe '.with_custom_logger' do
+ context 'when the logger is set' do
+ it 'uses the replacement logger for the duration of the block' do
+ expect(null_logger).to receive(:debug).and_call_original
+
+ expect { described_class.with_custom_logger(null_logger) { ActiveRecord::Base.logger.debug('foo') } }
+ .to not_change { ActiveRecord::Base.logger }
+ .and not_change { ActionController::Base.logger }
+ .and not_change { ActiveSupport::LogSubscriber.colorize_logging }
+ end
+
+ it 'returns the result of the block' do
+ expect(described_class.with_custom_logger(null_logger) { 2 }).to eq(2)
+ end
+ end
+
+ context 'when the logger is nil' do
+ it 'returns the result of the block' do
+ expect(described_class.with_custom_logger(nil) { 2 }).to eq(2)
+ end
+
+ it 'does not modify the standard Rails loggers' do
+ expect { described_class.with_custom_logger(nil) { } }
+ .to not_change { ActiveRecord::Base.logger }
+ .and not_change { ActionController::Base.logger }
+ .and not_change { ActiveSupport::LogSubscriber.colorize_logging }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index b5a9ac570e6..17b48b3d062 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -19,6 +19,12 @@ describe Gitlab::SearchResults do
project.add_developer(user)
end
+ describe '#objects' do
+ it 'returns without_page collection by default' do
+ expect(results.objects('projects')).to be_kind_of(Kaminari::PaginatableWithoutCount)
+ end
+ end
+
describe '#projects_count' do
it 'returns the total amount of projects' do
expect(results.projects_count).to eq(1)
@@ -43,6 +49,58 @@ describe Gitlab::SearchResults do
end
end
+ context "when count_limit is lower than total amount" do
+ before do
+ allow(results).to receive(:count_limit).and_return(1)
+ end
+
+ describe '#limited_projects_count' do
+ it 'returns the limited amount of projects' do
+ create(:project, name: 'foo2')
+
+ expect(results.limited_projects_count).to eq(1)
+ end
+ end
+
+ describe '#limited_merge_requests_count' do
+ it 'returns the limited amount of merge requests' do
+ create(:merge_request, :simple, source_project: project, title: 'foo2')
+
+ expect(results.limited_merge_requests_count).to eq(1)
+ end
+ end
+
+ describe '#limited_milestones_count' do
+ it 'returns the limited amount of milestones' do
+ create(:milestone, project: project, title: 'foo2')
+
+ expect(results.limited_milestones_count).to eq(1)
+ end
+ end
+
+ describe '#limited_issues_count' do
+ it 'runs single SQL query to get the limited amount of issues' do
+ create(:milestone, project: project, title: 'foo2')
+
+ expect(results).to receive(:issues).with(public_only: true).and_call_original
+ expect(results).not_to receive(:issues).with(no_args).and_call_original
+
+ expect(results.limited_issues_count).to eq(1)
+ end
+ end
+ end
+
+ context "when count_limit is higher than total amount" do
+ describe '#limited_issues_count' do
+ it 'runs multiple queries to get the limited amount of issues' do
+ expect(results).to receive(:issues).with(public_only: true).and_call_original
+ expect(results).to receive(:issues).with(no_args).and_call_original
+
+ expect(results.limited_issues_count).to eq(1)
+ end
+ end
+ end
+
it 'includes merge requests from source and target projects' do
forked_project = fork_project(project, user)
merge_request_2 = create(:merge_request, target_project: project, source_project: forked_project, title: 'foo')
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index e872a5290c5..bda239b7871 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -17,6 +17,22 @@ describe Gitlab::Utils do
end
end
+ describe '.remove_line_breaks' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:original, :expected) do
+ "foo\nbar\nbaz" | "foobarbaz"
+ "foo\r\nbar\r\nbaz" | "foobarbaz"
+ "foobar" | "foobar"
+ end
+
+ with_them do
+ it "replace line breaks with an empty string" do
+ expect(described_class.remove_line_breaks(original)).to eq(expected)
+ end
+ end
+ end
+
describe '.to_boolean' do
it 'accepts booleans' do
expect(to_boolean(true)).to be(true)
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index cbc8c67da61..7a8e798e3c9 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -71,6 +71,18 @@ describe Notify do
is_expected.to have_html_escaped_body_text issue.description
end
+ it 'does not add a reason header' do
+ is_expected.not_to have_header('X-GitLab-NotificationReason', /.+/)
+ end
+
+ context 'when sent with a reason' do
+ subject { described_class.new_issue_email(issue.assignees.first.id, issue.id, NotificationReason::ASSIGNED) }
+
+ it 'includes the reason in a header' do
+ is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+ end
+
context 'when enabled email_author_in_body' do
before do
stub_application_setting(email_author_in_body: true)
@@ -108,6 +120,14 @@ describe Notify do
is_expected.to have_body_text(project_issue_path(project, issue))
end
end
+
+ context 'when sent with a reason' do
+ subject { described_class.reassigned_issue_email(recipient.id, issue.id, [previous_assignee.id], current_user.id, NotificationReason::ASSIGNED) }
+
+ it 'includes the reason in a header' do
+ is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+ end
end
describe 'that have been relabeled' do
@@ -226,6 +246,14 @@ describe Notify do
is_expected.to have_html_escaped_body_text merge_request.description
end
+ context 'when sent with a reason' do
+ subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id, NotificationReason::ASSIGNED) }
+
+ it 'includes the reason in a header' do
+ is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+ end
+
context 'when enabled email_author_in_body' do
before do
stub_application_setting(email_author_in_body: true)
@@ -263,6 +291,27 @@ describe Notify do
is_expected.to have_html_escaped_body_text(assignee.name)
end
end
+
+ context 'when sent with a reason' do
+ subject { described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id, NotificationReason::ASSIGNED) }
+
+ it 'includes the reason in a header' do
+ is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+
+ it 'includes the reason in the footer' do
+ text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(NotificationReason::ASSIGNED)
+ is_expected.to have_body_text(text)
+
+ new_subject = described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id, NotificationReason::MENTIONED)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(NotificationReason::MENTIONED)
+ expect(new_subject).to have_body_text(text)
+
+ new_subject = described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id, nil)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(nil)
+ expect(new_subject).to have_body_text(text)
+ end
+ end
end
describe 'that have been relabeled' do
diff --git a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
index 84c2e9f7e52..63defcb39bf 100644
--- a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
+++ b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170508170547_add_head_pipeline_for_each_merge_request.rb')
-describe AddHeadPipelineForEachMergeRequest, :truncate do
+describe AddHeadPipelineForEachMergeRequest, :delete do
include ProjectForksHelper
let(:migration) { described_class.new }
diff --git a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
index 597d8eab51c..f3a46025376 100644
--- a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
+++ b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170803090603_calculate_conv_dev_index_percentages.rb')
-describe CalculateConvDevIndexPercentages, truncate: true do
+describe CalculateConvDevIndexPercentages, :delete do
let(:migration) { described_class.new }
let!(:conv_dev_index) do
create(:conversational_development_index_metric,
diff --git a/spec/migrations/fix_wrongly_renamed_routes_spec.rb b/spec/migrations/fix_wrongly_renamed_routes_spec.rb
index 5ef10b92a3a..543cf55f076 100644
--- a/spec/migrations/fix_wrongly_renamed_routes_spec.rb
+++ b/spec/migrations/fix_wrongly_renamed_routes_spec.rb
@@ -1,29 +1,35 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170518231126_fix_wrongly_renamed_routes.rb')
-describe FixWronglyRenamedRoutes, truncate: true do
+describe FixWronglyRenamedRoutes, :migration do
let(:subject) { described_class.new }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:routes_table) { table(:routes) }
let(:broken_namespace) do
- namespace = create(:group, name: 'apiis')
- namespace.route.update_attribute(:path, 'api0is')
- namespace
+ namespaces_table.create!(name: 'apiis', path: 'apiis').tap do |namespace|
+ routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'api0is', path: 'api0is')
+ end
end
+ let(:broken_namespace_route) { routes_table.where(source_type: 'Namespace', source_id: broken_namespace.id).first }
describe '#wrongly_renamed' do
it "includes routes that have names that don't match their namespace" do
broken_namespace
- _other_namespace = create(:group, name: 'api0')
+ other_namespace = namespaces_table.create!(name: 'api0', path: 'api0')
+ routes_table.create!(source_type: 'Namespace', source_id: other_namespace.id, name: 'api0', path: 'api0')
expect(subject.wrongly_renamed.map(&:id))
- .to contain_exactly(broken_namespace.route.id)
+ .to contain_exactly(broken_namespace_route.id)
end
end
describe "#paths_and_corrections" do
it 'finds the wrong path and gets the correction from the namespace' do
broken_namespace
- namespace = create(:group, name: 'uploads-test')
- namespace.route.update_attribute(:path, 'uploads0-test')
+ namespaces_table.create!(name: 'uploads-test', path: 'uploads-test').tap do |namespace|
+ routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'uploads-test', path: 'uploads0-test')
+ end
expected_result = [
{ 'namespace_path' => 'apiis', 'path' => 'api0is' },
@@ -36,38 +42,45 @@ describe FixWronglyRenamedRoutes, truncate: true do
describe '#routes_in_namespace_query' do
it 'includes only the required routes' do
- namespace = create(:group, path: 'hello')
- project = create(:project, namespace: namespace)
- _other_namespace = create(:group, path: 'hello0')
-
- result = Route.where(subject.routes_in_namespace_query('hello'))
-
- expect(result).to contain_exactly(namespace.route, project.route)
+ namespace = namespaces_table.create!(name: 'hello', path: 'hello')
+ namespace_route = routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'hello', path: 'hello')
+ project = projects_table.new(name: 'my-project', path: 'my-project', namespace_id: namespace.id).tap do |project|
+ project.save!(validate: false)
+ end
+ routes_table.create!(source_type: 'Project', source_id: project.id, name: 'my-project', path: 'hello/my-project')
+ _other_namespace = namespaces_table.create!(name: 'hello0', path: 'hello0')
+
+ result = routes_table.where(subject.routes_in_namespace_query('hello'))
+ project_route = routes_table.where(source_type: 'Project', source_id: project.id).first
+
+ expect(result).to contain_exactly(namespace_route, project_route)
end
end
describe '#up' do
- let(:broken_project) do
- project = create(:project, namespace: broken_namespace, path: 'broken-project')
- project.route.update_attribute(:path, 'api0is/broken-project')
- project
- end
-
it 'renames incorrectly named routes' do
- broken_project
+ broken_project =
+ projects_table.new(name: 'broken-project', path: 'broken-project', namespace_id: broken_namespace.id).tap do |project|
+ project.save!(validate: false)
+ routes_table.create!(source_type: 'Project', source_id: project.id, name: 'broken-project', path: 'api0is/broken-project')
+ end
subject.up
- expect(broken_project.route.reload.path).to eq('apiis/broken-project')
- expect(broken_namespace.route.reload.path).to eq('apiis')
+ broken_project_route = routes_table.where(source_type: 'Project', source_id: broken_project.id).first
+
+ expect(broken_project_route.path).to eq('apiis/broken-project')
+ expect(broken_namespace_route.reload.path).to eq('apiis')
end
it "doesn't touch namespaces that look like something that should be renamed" do
- namespace = create(:group, path: 'api0')
+ namespaces_table.create!(name: 'apiis', path: 'apiis')
+ namespace = namespaces_table.create!(name: 'hello', path: 'api0')
+ namespace_route = routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'hello', path: 'api0')
subject.up
- expect(namespace.route.reload.path).to eq('api0')
+ expect(namespace_route.reload.path).to eq('api0')
end
end
end
diff --git a/spec/migrations/migrate_issues_to_ghost_user_spec.rb b/spec/migrations/migrate_issues_to_ghost_user_spec.rb
index cfd4021fbac..ff0d44e1ed2 100644
--- a/spec/migrations/migrate_issues_to_ghost_user_spec.rb
+++ b/spec/migrations/migrate_issues_to_ghost_user_spec.rb
@@ -8,10 +8,10 @@ describe MigrateIssuesToGhostUser, :migration do
let(:users) { table(:users) }
before do
- projects.create!(name: 'gitlab')
+ project = projects.create!(name: 'gitlab')
user = users.create(email: 'test@example.com')
- issues.create(title: 'Issue 1', author_id: nil, project_id: 1)
- issues.create(title: 'Issue 2', author_id: user.id, project_id: 1)
+ issues.create(title: 'Issue 1', author_id: nil, project_id: project.id)
+ issues.create(title: 'Issue 2', author_id: user.id, project_id: project.id)
end
context 'when ghost user exists' do
diff --git a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
index 063829be546..a17c9c72bde 100644
--- a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
+++ b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170324160416_migrate_user_activities_to_users_last_activity_on.rb')
-describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :truncate do
+describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :delete do
let(:migration) { described_class.new }
let!(:user_active_1) { create(:user) }
let!(:user_active_2) { create(:user) }
diff --git a/spec/migrations/migrate_user_project_view_spec.rb b/spec/migrations/migrate_user_project_view_spec.rb
index 5e16769d63a..31d16e17d7b 100644
--- a/spec/migrations/migrate_user_project_view_spec.rb
+++ b/spec/migrations/migrate_user_project_view_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170406142253_migrate_user_project_view.rb')
-describe MigrateUserProjectView, :truncate do
+describe MigrateUserProjectView, :delete do
let(:migration) { described_class.new }
let!(:user) { create(:user, project_view: 'readme') }
diff --git a/spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb b/spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb
new file mode 100644
index 00000000000..0ff98933d5c
--- /dev/null
+++ b/spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20171215113714_populate_can_push_from_deploy_keys_projects.rb')
+
+describe PopulateCanPushFromDeployKeysProjects, :migration do
+ let(:migration) { described_class.new }
+ let(:deploy_keys) { table(:keys) }
+ let(:deploy_keys_projects) { table(:deploy_keys_projects) }
+ let(:projects) { table(:projects) }
+
+ before do
+ deploy_keys.inheritance_column = nil
+
+ projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1')
+ (1..10).each do |index|
+ deploy_keys.create!(id: index, title: 'dummy', type: 'DeployKey', key: Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com')
+ deploy_keys_projects.create!(id: index, deploy_key_id: index, project_id: 1)
+ end
+ end
+
+ describe '#up' do
+ it 'migrates can_push from deploy_keys to deploy_keys_projects' do
+ deploy_keys.limit(5).update_all(can_push: true)
+
+ expected = deploy_keys.order(:id).pluck(:id, :can_push)
+
+ migration.up
+
+ expect(deploy_keys_projects.order(:id).pluck(:deploy_key_id, :can_push)).to eq expected
+ end
+ end
+
+ describe '#down' do
+ it 'migrates can_push from deploy_keys_projects to deploy_keys' do
+ deploy_keys_projects.limit(5).update_all(can_push: true)
+
+ expected = deploy_keys_projects.order(:id).pluck(:deploy_key_id, :can_push)
+
+ migration.down
+
+ expect(deploy_keys.order(:id).pluck(:id, :can_push)).to eq expected
+ end
+ end
+end
diff --git a/spec/migrations/remove_duplicate_mr_events_spec.rb b/spec/migrations/remove_duplicate_mr_events_spec.rb
index e393374028f..e51872239ad 100644
--- a/spec/migrations/remove_duplicate_mr_events_spec.rb
+++ b/spec/migrations/remove_duplicate_mr_events_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170815060945_remove_duplicate_mr_events.rb')
-describe RemoveDuplicateMrEvents, truncate: true do
+describe RemoveDuplicateMrEvents, :delete do
let(:migration) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/remove_empty_fork_networks_spec.rb b/spec/migrations/remove_empty_fork_networks_spec.rb
index cf6ae5cda74..7f7ce91378b 100644
--- a/spec/migrations/remove_empty_fork_networks_spec.rb
+++ b/spec/migrations/remove_empty_fork_networks_spec.rb
@@ -3,12 +3,19 @@ require Rails.root.join('db', 'post_migrate', '20171114104051_remove_empty_fork_
describe RemoveEmptyForkNetworks, :migration do
let!(:fork_networks) { table(:fork_networks) }
+ let!(:projects) { table(:projects) }
+ let!(:fork_network_members) { table(:fork_network_members) }
- let(:deleted_project) { create(:project) }
- let!(:empty_network) { create(:fork_network, id: 1, root_project_id: deleted_project.id) }
- let!(:other_network) { create(:fork_network, id: 2, root_project_id: create(:project).id) }
+ let(:deleted_project) { projects.create! }
+ let!(:empty_network) { fork_networks.create!(id: 1, root_project_id: deleted_project.id) }
+ let!(:other_network) { fork_networks.create!(id: 2, root_project_id: projects.create.id) }
before do
+ fork_network_members.create(fork_network_id: empty_network.id,
+ project_id: empty_network.root_project_id)
+ fork_network_members.create(fork_network_id: other_network.id,
+ project_id: other_network.root_project_id)
+
deleted_project.destroy!
end
diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb
index ae3a4cb9b29..75310075cc5 100644
--- a/spec/migrations/rename_more_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_more_reserved_project_names_spec.rb
@@ -5,8 +5,8 @@ require Rails.root.join('db', 'post_migrate', '20170313133418_rename_more_reserv
# This migration uses multiple threads, and thus different transactions. This
# means data created in this spec may not be visible to some threads. To work
-# around this we use the TRUNCATE cleaning strategy.
-describe RenameMoreReservedProjectNames, truncate: true do
+# around this we use the DELETE cleaning strategy.
+describe RenameMoreReservedProjectNames, :delete do
let(:migration) { described_class.new }
let!(:project) { create(:project) }
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
index 462f4c08d63..e6555b1fe6b 100644
--- a/spec/migrations/rename_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -5,8 +5,8 @@ require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_pr
# This migration uses multiple threads, and thus different transactions. This
# means data created in this spec may not be visible to some threads. To work
-# around this we use the TRUNCATE cleaning strategy.
-describe RenameReservedProjectNames, truncate: true do
+# around this we use the DELETE cleaning strategy.
+describe RenameReservedProjectNames, :delete do
let(:migration) { described_class.new }
let!(:project) { create(:project) }
diff --git a/spec/migrations/rename_users_with_renamed_namespace_spec.rb b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
index 1e9aab3d9a1..cbc0ebeb44d 100644
--- a/spec/migrations/rename_users_with_renamed_namespace_spec.rb
+++ b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170518200835_rename_users_with_renamed_namespace.rb')
-describe RenameUsersWithRenamedNamespace, truncate: true do
+describe RenameUsersWithRenamedNamespace, :delete do
it 'renames a user that had their namespace renamed to the namespace path' do
other_user = create(:user, username: 'kodingu')
other_user1 = create(:user, username: 'api0')
diff --git a/spec/migrations/update_retried_for_ci_build_spec.rb b/spec/migrations/update_retried_for_ci_build_spec.rb
index 3742b4dafe5..ccb77766b84 100644
--- a/spec/migrations/update_retried_for_ci_build_spec.rb
+++ b/spec/migrations/update_retried_for_ci_build_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170503004427_update_retried_for_ci_build.rb')
-describe UpdateRetriedForCiBuild, truncate: true do
+describe UpdateRetriedForCiBuild, :delete do
let(:pipeline) { create(:ci_pipeline) }
let!(:build_old) { create(:ci_build, pipeline: pipeline, name: 'test') }
let!(:build_new) { create(:ci_build, pipeline: pipeline, name: 'test') }
diff --git a/spec/migrations/update_upload_paths_to_system_spec.rb b/spec/migrations/update_upload_paths_to_system_spec.rb
index d4a1553fb0e..984b428a020 100644
--- a/spec/migrations/update_upload_paths_to_system_spec.rb
+++ b/spec/migrations/update_upload_paths_to_system_spec.rb
@@ -1,53 +1,59 @@
-require "spec_helper"
-require Rails.root.join("db", "post_migrate", "20170317162059_update_upload_paths_to_system.rb")
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170317162059_update_upload_paths_to_system.rb')
-describe UpdateUploadPathsToSystem do
+describe UpdateUploadPathsToSystem, :migration do
let(:migration) { described_class.new }
+ let(:uploads_table) { table(:uploads) }
+ let(:base_upload_attributes) { { size: 42, uploader: 'John Doe' } }
before do
allow(migration).to receive(:say)
end
- describe "#uploads_to_switch_to_new_path" do
- it "contains only uploads with the old path for the correct models" do
- _upload_for_other_type = create(:upload, model: create(:ci_pipeline), path: "uploads/ci_pipeline/avatar.jpg")
- _upload_with_system_path = create(:upload, model: create(:project), path: "uploads/-/system/project/avatar.jpg")
- _upload_with_other_path = create(:upload, model: create(:project), path: "thelongsecretforafileupload/avatar.jpg")
- old_upload = create(:upload, model: create(:project), path: "uploads/project/avatar.jpg")
- group_upload = create(:upload, model: create(:group), path: "uploads/group/avatar.jpg")
+ describe '#uploads_to_switch_to_new_path' do
+ it 'contains only uploads with the old path for the correct models' do
+ _upload_for_other_type = create_upload('Pipeline', 'uploads/ci_pipeline/avatar.jpg')
+ _upload_with_system_path = create_upload('Project', 'uploads/-/system/project/avatar.jpg')
+ _upload_with_other_path = create_upload('Project', 'thelongsecretforafileupload/avatar.jpg')
+ old_upload = create_upload('Project', 'uploads/project/avatar.jpg')
+ group_upload = create_upload('Namespace', 'uploads/group/avatar.jpg')
- expect(Upload.where(migration.uploads_to_switch_to_new_path)).to contain_exactly(old_upload, group_upload)
+ expect(uploads_table.where(migration.uploads_to_switch_to_new_path)).to contain_exactly(old_upload, group_upload)
end
end
- describe "#uploads_to_switch_to_old_path" do
- it "contains only uploads with the new path for the correct models" do
- _upload_for_other_type = create(:upload, model: create(:ci_pipeline), path: "uploads/ci_pipeline/avatar.jpg")
- upload_with_system_path = create(:upload, model: create(:project), path: "uploads/-/system/project/avatar.jpg")
- _upload_with_other_path = create(:upload, model: create(:project), path: "thelongsecretforafileupload/avatar.jpg")
- _old_upload = create(:upload, model: create(:project), path: "uploads/project/avatar.jpg")
+ describe '#uploads_to_switch_to_old_path' do
+ it 'contains only uploads with the new path for the correct models' do
+ _upload_for_other_type = create_upload('Pipeline', 'uploads/ci_pipeline/avatar.jpg')
+ upload_with_system_path = create_upload('Project', 'uploads/-/system/project/avatar.jpg')
+ _upload_with_other_path = create_upload('Project', 'thelongsecretforafileupload/avatar.jpg')
+ _old_upload = create_upload('Project', 'uploads/project/avatar.jpg')
- expect(Upload.where(migration.uploads_to_switch_to_old_path)).to contain_exactly(upload_with_system_path)
+ expect(uploads_table.where(migration.uploads_to_switch_to_old_path)).to contain_exactly(upload_with_system_path)
end
end
- describe "#up", :truncate do
- it "updates old upload records to the new path" do
- old_upload = create(:upload, model: create(:project), path: "uploads/project/avatar.jpg")
+ describe '#up' do
+ it 'updates old upload records to the new path' do
+ old_upload = create_upload('Project', 'uploads/project/avatar.jpg')
migration.up
- expect(old_upload.reload.path).to eq("uploads/-/system/project/avatar.jpg")
+ expect(old_upload.reload.path).to eq('uploads/-/system/project/avatar.jpg')
end
end
- describe "#down", :truncate do
- it "updates the new system patsh to the old paths" do
- new_upload = create(:upload, model: create(:project), path: "uploads/-/system/project/avatar.jpg")
+ describe '#down' do
+ it 'updates the new system patsh to the old paths' do
+ new_upload = create_upload('Project', 'uploads/-/system/project/avatar.jpg')
migration.down
- expect(new_upload.reload.path).to eq("uploads/project/avatar.jpg")
+ expect(new_upload.reload.path).to eq('uploads/project/avatar.jpg')
end
end
+
+ def create_upload(type, path)
+ uploads_table.create(base_upload_attributes.merge(model_type: type, path: path))
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 45a606c1ea8..f5b3b4a9fc5 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -277,7 +277,7 @@ describe Ci::Build do
allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
end
- it { is_expected.to be_an(Array).and all(include(key: "key:1")) }
+ it { is_expected.to be_an(Array).and all(include(key: "key_1")) }
end
context 'when project does not have jobs_cache_index' do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index f8a98b43e46..959383ff0b7 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -228,7 +228,7 @@ eos
it { expect(data).to be_a(Hash) }
it { expect(data[:message]).to include('adds bar folder and branch-test text file to check Repository merged_to_root_ref method') }
it { expect(data[:timestamp]).to eq('2016-09-27T14:37:46Z') }
- it { expect(data[:added]).to eq(["bar/branch-test.txt"]) }
+ it { expect(data[:added]).to contain_exactly("bar/branch-test.txt") }
it { expect(data[:modified]).to eq([]) }
it { expect(data[:removed]).to eq([]) }
end
@@ -532,8 +532,8 @@ eos
let(:commit2) { merge_request1.merge_request_diff.commits.first }
it 'returns merge_requests that introduced that commit' do
- expect(commit1.merge_requests).to eq([merge_request1, merge_request2])
- expect(commit2.merge_requests).to eq([merge_request1])
+ expect(commit1.merge_requests).to contain_exactly(merge_request1, merge_request2)
+ expect(commit2.merge_requests).to contain_exactly(merge_request1)
end
end
end
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
index cbdc438be0b..3696e6f62fd 100644
--- a/spec/models/concerns/avatarable_spec.rb
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
describe Avatarable do
- subject { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
+ set(:project) { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
let(:gitlab_host) { "https://gitlab.example.com" }
let(:relative_url_root) { "/gitlab" }
- let(:asset_host) { "https://gitlab-assets.example.com" }
+ let(:asset_host) { 'https://gitlab-assets.example.com' }
before do
stub_config_setting(base_url: gitlab_host)
@@ -15,29 +15,32 @@ describe Avatarable do
describe '#avatar_path' do
using RSpec::Parameterized::TableSyntax
- where(:has_asset_host, :visibility_level, :only_path, :avatar_path) do
- true | Project::PRIVATE | true | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::PRIVATE | false | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::INTERNAL | true | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::INTERNAL | false | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::PUBLIC | true | [subject.avatar.url]
- true | Project::PUBLIC | false | [asset_host, subject.avatar.url]
- false | Project::PRIVATE | true | [relative_url_root, subject.avatar.url]
- false | Project::PRIVATE | false | [gitlab_host, relative_url_root, subject.avatar.url]
- false | Project::INTERNAL | true | [relative_url_root, subject.avatar.url]
- false | Project::INTERNAL | false | [gitlab_host, relative_url_root, subject.avatar.url]
- false | Project::PUBLIC | true | [relative_url_root, subject.avatar.url]
- false | Project::PUBLIC | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ where(:has_asset_host, :visibility_level, :only_path, :avatar_path_prefix) do
+ true | Project::PRIVATE | true | [gitlab_host, relative_url_root]
+ true | Project::PRIVATE | false | [gitlab_host, relative_url_root]
+ true | Project::INTERNAL | true | [gitlab_host, relative_url_root]
+ true | Project::INTERNAL | false | [gitlab_host, relative_url_root]
+ true | Project::PUBLIC | true | []
+ true | Project::PUBLIC | false | [asset_host]
+ false | Project::PRIVATE | true | [relative_url_root]
+ false | Project::PRIVATE | false | [gitlab_host, relative_url_root]
+ false | Project::INTERNAL | true | [relative_url_root]
+ false | Project::INTERNAL | false | [gitlab_host, relative_url_root]
+ false | Project::PUBLIC | true | [relative_url_root]
+ false | Project::PUBLIC | false | [gitlab_host, relative_url_root]
end
with_them do
before do
- allow(ActionController::Base).to receive(:asset_host).and_return(has_asset_host ? asset_host : nil)
- subject.visibility_level = visibility_level
+ allow(ActionController::Base).to receive(:asset_host) { has_asset_host && asset_host }
+
+ project.visibility_level = visibility_level
end
+ let(:avatar_path) { (avatar_path_prefix + [project.avatar.url]).join }
+
it 'returns the expected avatar path' do
- expect(subject.avatar_path(only_path: only_path)).to eq(avatar_path.join)
+ expect(project.avatar_path(only_path: only_path)).to eq(avatar_path)
end
end
end
diff --git a/spec/models/concerns/triggerable_hooks_spec.rb b/spec/models/concerns/triggerable_hooks_spec.rb
new file mode 100644
index 00000000000..621d2d38eae
--- /dev/null
+++ b/spec/models/concerns/triggerable_hooks_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+
+RSpec.describe TriggerableHooks do
+ before do
+ class TestableHook < WebHook
+ include TriggerableHooks
+ triggerable_hooks [:push_hooks]
+ end
+ end
+
+ describe 'scopes' do
+ it 'defines a scope for each of the requested triggers' do
+ expect(TestableHook).to respond_to :push_hooks
+ expect(TestableHook).not_to respond_to :tag_push_hooks
+ end
+ end
+
+ describe '.hooks_for' do
+ context 'the model has the required trigger scope' do
+ it 'returns the record' do
+ hook = TestableHook.create!(url: 'http://example.com', push_events: true)
+
+ expect(TestableHook.hooks_for(:push_hooks)).to eq [hook]
+ end
+ end
+
+ context 'the model does not have the required trigger scope' do
+ it 'returns an empty relation' do
+ TestableHook.create!(url: 'http://example.com')
+
+ expect(TestableHook.hooks_for(:tag_push_hooks)).to eq []
+ end
+ end
+
+ context 'the stock scope ".all" is accepted' do
+ it 'returns the record' do
+ hook = TestableHook.create!(url: 'http://example.com')
+
+ expect(TestableHook.hooks_for(:all)).to eq [hook]
+ end
+ end
+ end
+end
diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb
index 0345fefb254..fca3090ff4a 100644
--- a/spec/models/deploy_keys_project_spec.rb
+++ b/spec/models/deploy_keys_project_spec.rb
@@ -8,7 +8,7 @@ describe DeployKeysProject do
describe "Validation" do
it { is_expected.to validate_presence_of(:project_id) }
- it { is_expected.to validate_presence_of(:deploy_key_id) }
+ it { is_expected.to validate_presence_of(:deploy_key) }
end
describe "Destroying" do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 0e965f541d8..8bc45715dcd 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -7,7 +7,8 @@ describe SystemHook do
it 'sets defined default parameters' do
attrs = {
push_events: false,
- repository_update_events: true
+ repository_update_events: true,
+ merge_requests_events: false
}
expect(system_hook).to have_attributes(attrs)
end
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 388120160ab..ea6d6e53ef5 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -29,6 +29,12 @@ describe WebHook do
expect(hook.url).to eq('https://example.com')
end
end
+
+ describe 'token' do
+ it { is_expected.to allow_value("foobar").for(:token) }
+
+ it { is_expected.not_to allow_values("foo\nbar", "foo\r\nbar").for(:token) }
+ end
end
describe 'execute' do
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 6aa0e7f49c3..c64cdf8f812 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -488,7 +488,7 @@ describe Member do
member.accept_invite!(user)
end
- it "refreshes user's authorized projects", :truncate do
+ it "refreshes user's authorized projects", :delete do
project = member.source
expect(user.authorized_projects).not_to include(project)
@@ -523,7 +523,7 @@ describe Member do
end
end
- describe "destroying a record", :truncate do
+ describe "destroying a record", :delete do
it "refreshes user's authorized projects" do
project = create(:project, :private)
user = create(:user)
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c76f32b3989..eb9690df313 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1064,16 +1064,6 @@ describe MergeRequest do
end
describe '#can_be_reverted?' do
- context 'when there is no merged_at for the MR' do
- before do
- subject.metrics.update!(merged_at: nil)
- end
-
- it 'returns false' do
- expect(subject.can_be_reverted?(nil)).to be_falsey
- end
- end
-
context 'when there is no merge_commit for the MR' do
before do
subject.metrics.update!(merged_at: Time.now.utc)
@@ -1097,6 +1087,16 @@ describe MergeRequest do
end
end
+ context 'when there is no merged_at for the MR' do
+ before do
+ subject.metrics.update!(merged_at: nil)
+ end
+
+ it 'returns true' do
+ expect(subject.can_be_reverted?(nil)).to be_truthy
+ end
+ end
+
context 'when there is a revert commit' do
let(:current_user) { subject.author }
let(:branch) { subject.target_branch }
@@ -1127,9 +1127,29 @@ describe MergeRequest do
end
end
- context 'when the revert commit is mentioned in a note before the MR was merged' do
+ context 'when there is no merged_at for the MR' do
+ before do
+ subject.metrics.update!(merged_at: nil)
+ end
+
+ it 'returns false' do
+ expect(subject.can_be_reverted?(current_user)).to be_falsey
+ end
+ end
+
+ context 'when the revert commit is mentioned in a note just before the MR was merged' do
+ before do
+ subject.notes.last.update!(created_at: subject.metrics.merged_at - 30.seconds)
+ end
+
+ it 'returns false' do
+ expect(subject.can_be_reverted?(current_user)).to be_falsey
+ end
+ end
+
+ context 'when the revert commit is mentioned in a note long before the MR was merged' do
before do
- subject.notes.last.update!(created_at: subject.metrics.merged_at - 1.second)
+ subject.notes.last.update!(created_at: subject.metrics.merged_at - 2.minutes)
end
it 'returns true' do
@@ -1519,7 +1539,7 @@ describe MergeRequest do
expect { subject.reload_diff }.to change { subject.merge_request_diffs.count }.by(1)
end
- it "executs diff cache service" do
+ it "executes diff cache service" do
expect_any_instance_of(MergeRequests::MergeRequestDiffCacheService).to receive(:execute).with(subject)
subject.reload_diff
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 41e2ab20d69..1fccf92627a 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -30,7 +30,7 @@ describe ProjectGroupLink do
end
end
- describe "destroying a record", :truncate do
+ describe "destroying a record", :delete do
it "refreshes group users' authorized projects" do
project = create(:project, :private)
group = create(:group)
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb
index 6a5d0decfec..733086e258f 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/project_services/microsoft_teams_service_spec.rb
@@ -92,6 +92,10 @@ describe MicrosoftTeamsService do
service.hook_data(merge_request, 'open')
end
+ before do
+ project.add_developer(user)
+ end
+
it "calls Microsoft Teams API" do
chat_service.execute(merge_sample_data)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 78223c44999..31dcb543cbd 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1951,6 +1951,10 @@ describe Project do
expect(second_fork.fork_source).to eq(project)
end
+
+ it 'returns nil if it is the root of the fork network' do
+ expect(project.fork_source).to be_nil
+ end
end
describe '#lfs_storage_project' do
@@ -3206,4 +3210,40 @@ describe Project do
expect { project.write_repository_config }.not_to raise_error
end
end
+
+ describe '#execute_hooks' do
+ it 'executes the projects hooks with the specified scope' do
+ hook1 = create(:project_hook, merge_requests_events: true, tag_push_events: false)
+ hook2 = create(:project_hook, merge_requests_events: false, tag_push_events: true)
+ project = create(:project, hooks: [hook1, hook2])
+
+ expect_any_instance_of(ProjectHook).to receive(:async_execute).once
+
+ project.execute_hooks({}, :tag_push_hooks)
+ end
+
+ it 'executes the system hooks with the specified scope' do
+ expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with({ data: 'data' }, :merge_request_hooks)
+
+ project = build(:project)
+ project.execute_hooks({ data: 'data' }, :merge_request_hooks)
+ end
+
+ it 'executes the system hooks when inside a transaction' do
+ allow_any_instance_of(WebHookService).to receive(:execute)
+
+ create(:system_hook, merge_requests_events: true)
+
+ project = build(:project)
+
+ # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
+ # but since the entire spec run takes place in a transaction, we never
+ # actually get to the `after_commit` hook that queues these jobs.
+ expect do
+ project.transaction do
+ project.execute_hooks({ data: 'data' }, :merge_request_hooks)
+ end
+ end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
+ end
+ end
end
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index e78ed1df821..5cff2af4aca 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -146,6 +146,12 @@ describe ProjectStatistics do
expect(statistics.build_artifacts_size).to be(106365)
end
+
+ it 'calculates related build artifacts by project' do
+ expect(Ci::JobArtifact).to receive(:artifacts_size_for).with(project) { 0 }
+
+ statistics.update_build_artifacts_size
+ end
end
context 'when legacy artifacts are used' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index baaa9e3ef44..d4070b320ed 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -222,20 +222,20 @@ describe Repository do
it 'sets follow when path is a single path' do
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: true)).and_call_original.twice
- repository.commits('master', path: 'README.md')
- repository.commits('master', path: ['README.md'])
+ repository.commits('master', limit: 1, path: 'README.md')
+ repository.commits('master', limit: 1, path: ['README.md'])
end
it 'does not set follow when path is multiple paths' do
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original
- repository.commits('master', path: ['README.md', 'CHANGELOG'])
+ repository.commits('master', limit: 1, path: ['README.md', 'CHANGELOG'])
end
it 'does not set follow when there are no paths' do
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original
- repository.commits('master')
+ repository.commits('master', limit: 1)
end
end
@@ -365,12 +365,18 @@ describe Repository do
it { is_expected.to be_truthy }
end
- context 'non-mergeable branches' do
+ context 'non-mergeable branches without conflict sides missing' do
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
it { is_expected.to be_falsey }
end
+ context 'non-mergeable branches with conflict sides missing' do
+ subject { repository.can_be_merged?('conflict-missing-side', 'conflict-start') }
+
+ it { is_expected.to be_falsey }
+ end
+
context 'non merged branch' do
subject { repository.merged_to_root_ref?('fix') }
@@ -449,7 +455,7 @@ describe Repository do
expect do
repository.create_dir(user, 'newdir',
message: 'Create newdir', branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
newdir = repository.tree('master', 'newdir')
expect(newdir.path).to eq('newdir')
@@ -463,7 +469,7 @@ describe Repository do
repository.create_dir(user, 'newdir',
message: 'Create newdir', branch_name: 'patch',
start_branch_name: 'master', start_project: forked_project)
- end.to change { repository.commits('master').count }.by(0)
+ end.to change { repository.count_commits(ref: 'master') }.by(0)
expect(repository.branch_exists?('patch')).to be_truthy
expect(forked_project.repository.branch_exists?('patch')).to be_falsy
@@ -480,7 +486,7 @@ describe Repository do
message: 'Add newdir',
branch_name: 'master',
author_email: author_email, author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -496,7 +502,7 @@ describe Repository do
repository.create_file(user, 'NEWCHANGELOG', 'Changelog!',
message: 'Create changelog',
branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
blob = repository.blob_at('master', 'NEWCHANGELOG')
@@ -508,7 +514,7 @@ describe Repository do
repository.create_file(user, 'new_dir/new_file.txt', 'File!',
message: 'Create new_file with new_dir',
branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
expect(repository.tree('master', 'new_dir').path).to eq('new_dir')
expect(repository.blob_at('master', 'new_dir/new_file.txt').data).to eq('File!')
@@ -532,7 +538,7 @@ describe Repository do
branch_name: 'master',
author_email: author_email,
author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -548,7 +554,7 @@ describe Repository do
repository.update_file(user, 'CHANGELOG', 'Changelog!',
message: 'Update changelog',
branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
blob = repository.blob_at('master', 'CHANGELOG')
@@ -561,7 +567,7 @@ describe Repository do
branch_name: 'master',
previous_path: 'LICENSE',
message: 'Changes filename')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
files = repository.ls_files('master')
@@ -578,7 +584,7 @@ describe Repository do
message: 'Update README',
author_email: author_email,
author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -593,7 +599,7 @@ describe Repository do
expect do
repository.delete_file(user, 'README',
message: 'Remove README', branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
expect(repository.blob_at('master', 'README')).to be_nil
end
@@ -604,7 +610,7 @@ describe Repository do
repository.delete_file(user, 'README',
message: 'Remove README', branch_name: 'master',
author_email: author_email, author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -2350,7 +2356,7 @@ describe Repository do
let(:commit) { repository.commit }
let(:ancestor) { commit.parents.first }
- context 'with Gitaly enabled' do
+ shared_examples '#ancestor?' do
it 'it is an ancestor' do
expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true)
end
@@ -2364,27 +2370,19 @@ describe Repository do
expect(repository.ancestor?(ancestor.id, nil)).to eq(false)
expect(repository.ancestor?(nil, nil)).to eq(false)
end
- end
-
- context 'with Gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:enabled?).and_return(false)
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(false)
- end
- it 'it is an ancestor' do
- expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true)
+ it 'returns false for invalid commit IDs' do
+ expect(repository.ancestor?(commit.id, Gitlab::Git::BLANK_SHA)).to eq(false)
+ expect(repository.ancestor?( Gitlab::Git::BLANK_SHA, commit.id)).to eq(false)
end
+ end
- it 'it is not an ancestor' do
- expect(repository.ancestor?(commit.id, ancestor.id)).to eq(false)
- end
+ context 'with Gitaly enabled' do
+ it_behaves_like('#ancestor?')
+ end
- it 'returns false on nil-values' do
- expect(repository.ancestor?(nil, commit.id)).to eq(false)
- expect(repository.ancestor?(ancestor.id, nil)).to eq(false)
- expect(repository.ancestor?(nil, nil)).to eq(false)
- end
+ context 'with Gitaly disabled', :skip_gitaly_mock do
+ it_behaves_like('#ancestor?')
end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 8a3b1034f3c..88f54fd10e5 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -79,6 +79,13 @@ describe Route do
end
describe 'callbacks' do
+ context 'before validation' do
+ it 'calls #delete_conflicting_orphaned_routes' do
+ expect(route).to receive(:delete_conflicting_orphaned_routes)
+ route.valid?
+ end
+ end
+
context 'after update' do
it 'calls #create_redirect_for_old_path' do
expect(route).to receive(:create_redirect_for_old_path)
@@ -378,4 +385,58 @@ describe Route do
end
end
end
+
+ describe '#delete_conflicting_orphaned_routes' do
+ context 'when there is a conflicting route' do
+ let!(:conflicting_group) { create(:group, path: 'foo') }
+
+ before do
+ route.path = conflicting_group.route.path
+ end
+
+ context 'when the route is orphaned' do
+ let!(:offending_route) { conflicting_group.route }
+
+ before do
+ Group.delete(conflicting_group) # Orphan the route
+ end
+
+ it 'deletes the orphaned route' do
+ expect do
+ route.valid?
+ end.to change { described_class.count }.from(2).to(1)
+ end
+
+ it 'passes validation, as usual' do
+ expect(route.valid?).to be_truthy
+ end
+ end
+
+ context 'when the route is not orphaned' do
+ it 'does not delete the conflicting route' do
+ expect do
+ route.valid?
+ end.not_to change { described_class.count }
+ end
+
+ it 'fails validation, as usual' do
+ expect(route.valid?).to be_falsey
+ end
+ end
+ end
+
+ context 'when there are no conflicting routes' do
+ it 'does not delete any routes' do
+ route
+
+ expect do
+ route.valid?
+ end.not_to change { described_class.count }
+ end
+
+ it 'passes validation, as usual' do
+ expect(route.valid?).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index ab6678cab38..79f25dc4360 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -280,4 +280,38 @@ describe Service do
expect(KubernetesService.find_by_template).to eq(kubernetes_service)
end
end
+
+ describe '#api_field_names' do
+ let(:fake_service) do
+ Class.new(Service) do
+ def fields
+ [
+ { name: 'token' },
+ { name: 'api_token' },
+ { name: 'key' },
+ { name: 'api_key' },
+ { name: 'password' },
+ { name: 'password_field' },
+ { name: 'safe_field' }
+ ]
+ end
+ end
+ end
+
+ let(:service) do
+ fake_service.new(properties: [
+ { token: 'token-value' },
+ { api_token: 'api_token-value' },
+ { key: 'key-value' },
+ { api_key: 'api_key-value' },
+ { password: 'password-value' },
+ { password_field: 'password_field-value' },
+ { safe_field: 'safe_field-value' }
+ ])
+ end
+
+ it 'filters out sensitive fields' do
+ expect(service.api_field_names).to eq(['safe_field'])
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 8d0eaf565a7..594f23718da 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -966,6 +966,14 @@ describe User do
expect(described_class.search(user3.username.upcase)).to eq([user3])
end
end
+
+ it 'returns no matches for an empty string' do
+ expect(described_class.search('')).to be_empty
+ end
+
+ it 'returns no matches for nil' do
+ expect(described_class.search(nil)).to be_empty
+ end
end
describe '.search_with_secondary_emails' do
@@ -1020,6 +1028,14 @@ describe User do
it 'does not return users with a matching part of secondary email' do
expect(search_with_secondary_emails(email.email[1..4])).not_to include([email.user])
end
+
+ it 'returns no matches for an empty string' do
+ expect(search_with_secondary_emails('')).to be_empty
+ end
+
+ it 'returns no matches for nil' do
+ expect(search_with_secondary_emails(nil)).to be_empty
+ end
end
describe '.find_by_ssh_key_id' do
@@ -1553,7 +1569,7 @@ describe User do
it { is_expected.to eq([private_group]) }
end
- describe '#authorized_projects', :truncate do
+ describe '#authorized_projects', :delete do
context 'with a minimum access level' do
it 'includes projects for which the user is an owner' do
user = create(:user)
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index ea75434e399..cc9d79da708 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -386,6 +386,17 @@ describe WikiPage do
end
end
+ describe '#formatted_content' do
+ it 'returns processed content of the page', :disable_gitaly do
+ subject.create({ title: "RDoc", content: "*bold*", format: "rdoc" })
+ page = wiki.find_page('RDoc')
+
+ expect(page.formatted_content).to eq("\n<p><strong>bold</strong></p>\n")
+
+ destroy_page('RDoc')
+ end
+ end
+
private
def remove_temp_repo(path)
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
new file mode 100644
index 00000000000..f56bc932f40
--- /dev/null
+++ b/spec/requests/api/applications_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe API::Applications, :api do
+ include ApiHelpers
+
+ let(:admin_user) { create(:user, admin: true) }
+ let(:user) { create(:user, admin: false) }
+
+ describe 'POST /applications' do
+ context 'authenticated and authorized user' do
+ it 'creates and returns an OAuth application' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url', scopes: ''
+ end.to change { Doorkeeper::Application.count }.by 1
+
+ application = Doorkeeper::Application.find_by(name: 'application_name', redirect_uri: 'http://application.url')
+
+ expect(response).to have_http_status 201
+ expect(json_response).to be_a Hash
+ expect(json_response['application_id']).to eq application.uid
+ expect(json_response['secret']).to eq application.secret
+ expect(json_response['callback_url']).to eq application.redirect_uri
+ end
+
+ it 'does not allow creating an application with the wrong redirect_uri format' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', redirect_uri: 'wrong_url_format', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['message']['redirect_uri'][0]).to eq('must be an absolute URI.')
+ end
+
+ it 'does not allow creating an application without a name' do
+ expect do
+ post api('/applications', admin_user), redirect_uri: 'http://application.url', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['error']).to eq('name is missing')
+ end
+
+ it 'does not allow creating an application without a redirect_uri' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['error']).to eq('redirect_uri is missing')
+ end
+
+ it 'does not allow creating an application without scopes' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url'
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['error']).to eq('scopes is missing')
+ end
+ end
+
+ context 'authorized user without authorization' do
+ it 'does not create application' do
+ expect do
+ post api('/applications', user), name: 'application_name', redirect_uri: 'http://application.url', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 403
+ end
+ end
+
+ context 'non-authenticated user' do
+ it 'does not create application' do
+ expect do
+ post api('/applications'), name: 'application_name', redirect_uri: 'http://application.url'
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 401
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 34db50dc082..ff5f207487b 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -62,7 +62,7 @@ describe API::Commits do
context "since optional parameter" do
it "returns project commits since provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
after = commits.second.created_at
get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
@@ -73,7 +73,7 @@ describe API::Commits do
end
it 'include correct pagination headers' do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
after = commits.second.created_at
commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
@@ -87,12 +87,12 @@ describe API::Commits do
context "until optional parameter" do
it "returns project commits until provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 20)
before = commits.second.created_at
get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- if commits.size >= 20
+ if commits.size == 20
expect(json_response.size).to eq(20)
else
expect(json_response.size).to eq(commits.size - 1)
@@ -103,7 +103,7 @@ describe API::Commits do
end
it 'include correct pagination headers' do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
before = commits.second.created_at
commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
@@ -181,7 +181,7 @@ describe API::Commits do
let(:page) { 3 }
it 'returns the third 5 commits' do
- commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
+ commit = project.repository.commits('HEAD', limit: per_page, offset: (page - 1) * per_page).first
expect(json_response.size).to eq(per_page)
expect(json_response.first['id']).to eq(commit.id)
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 1f1e6ea17e4..0772b3f2e64 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -110,7 +110,7 @@ describe API::DeployKeys do
end
it 'accepts can_push parameter' do
- key_attrs = attributes_for :write_access_key
+ key_attrs = attributes_for(:another_key).merge(can_push: true)
post api("/projects/#{project.id}/deploy_keys", admin), key_attrs
@@ -160,16 +160,6 @@ describe API::DeployKeys do
expect(json_response['title']).to eq('new title')
expect(json_response['can_push']).to eq(true)
end
-
- it 'updates a private ssh key from projects user has access with correct attributes' do
- create(:deploy_keys_project, project: project2, deploy_key: private_deploy_key)
-
- put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: 'new title', can_push: true }
-
- expect(json_response['id']).to eq(private_deploy_key.id)
- expect(json_response['title']).to eq('new title')
- expect(json_response['can_push']).to eq(true)
- end
end
describe 'DELETE /projects/:id/deploy_keys/:key_id' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 2783c51b8df..884a258fd12 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -317,35 +317,20 @@ describe API::Internal do
end
context "git pull" do
- context "gitaly disabled", :disable_gitaly do
- it "has the correct payload" do
- pull(key, project)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
- expect(json_response["gl_repository"]).to eq("project-#{project.id}")
- expect(json_response["gitaly"]).to be_nil
- expect(user).to have_an_activity_record
- end
- end
-
- context "gitaly enabled" do
- it "has the correct payload" do
- pull(key, project)
+ it "has the correct payload" do
+ pull(key, project)
- expect(response).to have_gitlab_http_status(200)
- expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
- expect(json_response["gl_repository"]).to eq("project-#{project.id}")
- expect(json_response["gitaly"]).not_to be_nil
- expect(json_response["gitaly"]["repository"]).not_to be_nil
- expect(json_response["gitaly"]["repository"]["storage_name"]).to eq(project.repository.gitaly_repository.storage_name)
- expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
- expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
- expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
- expect(user).to have_an_activity_record
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["gl_repository"]).to eq("project-#{project.id}")
+ expect(json_response["gitaly"]).not_to be_nil
+ expect(json_response["gitaly"]["repository"]).not_to be_nil
+ expect(json_response["gitaly"]["repository"]["storage_name"]).to eq(project.repository.gitaly_repository.storage_name)
+ expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
+ expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
+ expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
+ expect(user).to have_an_activity_record
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 43218755f4f..13db40d21a5 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1441,7 +1441,7 @@ describe API::Issues, :mailer do
context 'when source project does not exist' do
it 'returns 404 when trying to move an issue' do
- post api("/projects/123/issues/#{issue.iid}/move", user),
+ post api("/projects/12345/issues/#{issue.iid}/move", user),
to_project_id: target_project.id
expect(response).to have_gitlab_http_status(404)
@@ -1452,7 +1452,7 @@ describe API::Issues, :mailer do
context 'when target project does not exist' do
it 'returns 404 when trying to move an issue' do
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
- to_project_id: 123
+ to_project_id: 12345
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index e211c347266..4dd8deb6404 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -53,7 +53,7 @@ describe API::Jobs do
expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
- it 'avoids N+1 queries', skip_before_request: true do
+ it 'avoids N+1 queries', :skip_before_request do
first_build = create(:ci_build, :artifacts, pipeline: pipeline)
first_build.runner = create(:ci_runner)
first_build.user = create(:user)
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 5d4f81e07a6..73bd4785b11 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -65,6 +65,16 @@ describe API::Members do
expect(json_response.count).to eq(1)
expect(json_response.first['username']).to eq(master.username)
end
+
+ it 'finds all members with no query specified' do
+ get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: ''
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.count).to eq(2)
+ expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4eae3e50602..8e2982f1a5d 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -754,16 +754,28 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(400)
end
- context 'when target_branch is specified' do
+ context 'when target_branch and target_project_id is specified' do
+ let(:params) do
+ { title: 'Test merge_request',
+ target_branch: 'master',
+ source_branch: 'markdown',
+ author: user2,
+ target_project_id: unrelated_project.id }
+ end
+
it 'returns 422 if targeting a different fork' do
- post api("/projects/#{forked_project.id}/merge_requests", user2),
- title: 'Test merge_request',
- target_branch: 'master',
- source_branch: 'markdown',
- author: user2,
- target_project_id: unrelated_project.id
+ unrelated_project.add_developer(user2)
+
+ post api("/projects/#{forked_project.id}/merge_requests", user2), params
+
expect(response).to have_gitlab_http_status(422)
end
+
+ it 'returns 403 if targeting a different fork which user can not access' do
+ post api("/projects/#{forked_project.id}/merge_requests", user2), params
+
+ expect(response).to have_gitlab_http_status(403)
+ end
end
it "returns 201 when target_branch is specified and for the same project" do
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index e741ac4b7bd..4a2289ca137 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -1,9 +1,9 @@
require 'rails_helper'
describe API::ProjectSnippets do
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
- let(:admin) { create(:admin) }
+ set(:project) { create(:project, :public) }
+ set(:user) { create(:user) }
+ set(:admin) { create(:admin) }
describe "GET /projects/:project_id/snippets/:id/user_agent_detail" do
let(:snippet) { create(:project_snippet, :public, project: project) }
@@ -18,6 +18,13 @@ describe API::ProjectSnippets do
expect(json_response['akismet_submitted']).to eq(user_agent_detail.submitted)
end
+ it 'respects project scoping' do
+ other_project = create(:project)
+
+ get api("/projects/#{other_project.id}/snippets/#{snippet.id}/user_agent_detail", admin)
+ expect(response).to have_gitlab_http_status(404)
+ end
+
it "returns unautorized for non-admin users" do
get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/user_agent_detail", user)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index de1763015fa..97e7ffcd38e 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -150,6 +150,19 @@ describe API::Projects do
expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count')
end
+ context 'and with_issues_enabled=true' do
+ it 'only returns projects with issues enabled' do
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
+
+ get api('/projects?with_issues_enabled=true', user)
+
+ expect(response.status).to eq 200
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |p| p['id'] }).not_to include(project.id)
+ end
+ end
+
it "does not include statistics by default" do
get api('/projects', user)
@@ -352,6 +365,19 @@ describe API::Projects do
let(:current_user) { user2 }
let(:projects) { [public_project] }
end
+
+ context 'and with_issues_enabled=true' do
+ it 'does not return private issue projects' do
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::PRIVATE)
+
+ get api('/projects?with_issues_enabled=true', user2)
+
+ expect(response.status).to eq 200
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |p| p['id'] }).not_to include(project.id)
+ end
+ end
end
context 'when authenticated as admin' do
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 10e6a3c07c8..1d23e023bb6 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -80,6 +80,12 @@ describe API::ProtectedBranches do
it_behaves_like 'protected branch'
end
+
+ context 'when protected branch contains a period' do
+ let(:protected_name) { 'my.feature' }
+
+ it_behaves_like 'protected branch'
+ end
end
context 'when authenticated as a guest' do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 26d56c04862..236f8d7faf5 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -83,14 +83,14 @@ describe API::Services do
get api("/projects/#{project.id}/services/#{dashed_service}", admin)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list.map)
+ expect(json_response['properties'].keys).to match_array(service_instance.api_field_names)
end
it "returns properties of service #{service} other than passwords when authenticated as project owner" do
get api("/projects/#{project.id}/services/#{dashed_service}", user)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list_without_passwords)
+ expect(json_response['properties'].keys).to match_array(service_instance.api_field_names)
end
it "returns error when authenticated but not a project owner" do
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index c7a009e999e..6c57d443cbf 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -36,6 +36,7 @@ describe API::SystemHooks do
expect(json_response.first['url']).to eq(hook.url)
expect(json_response.first['push_events']).to be false
expect(json_response.first['tag_push_events']).to be false
+ expect(json_response.first['merge_requests_events']).to be false
expect(json_response.first['repository_update_events']).to be true
end
end
@@ -67,11 +68,28 @@ describe API::SystemHooks do
end
it 'sets default values for events' do
- post api('/hooks', admin), url: 'http://mep.mep', enable_ssl_verification: true
+ post api('/hooks', admin), url: 'http://mep.mep'
expect(response).to have_gitlab_http_status(201)
expect(json_response['enable_ssl_verification']).to be true
+ expect(json_response['push_events']).to be false
expect(json_response['tag_push_events']).to be false
+ expect(json_response['merge_requests_events']).to be false
+ end
+
+ it 'sets explicit values for events' do
+ post api('/hooks', admin),
+ url: 'http://mep.mep',
+ enable_ssl_verification: false,
+ push_events: true,
+ tag_push_events: true,
+ merge_requests_events: true
+
+ expect(response).to have_http_status(201)
+ expect(json_response['enable_ssl_verification']).to be false
+ expect(json_response['push_events']).to be true
+ expect(json_response['tag_push_events']).to be true
+ expect(json_response['merge_requests_events']).to be true
end
end
diff --git a/spec/requests/api/v3/builds_spec.rb b/spec/requests/api/v3/builds_spec.rb
index cdbc5692e19..af9e36a3b29 100644
--- a/spec/requests/api/v3/builds_spec.rb
+++ b/spec/requests/api/v3/builds_spec.rb
@@ -42,7 +42,7 @@ describe API::V3::Builds do
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
- it 'avoids N+1 queries', skip_before_request: true do
+ it 'avoids N+1 queries', :skip_before_request do
first_build = create(:ci_build, :artifacts, pipeline: pipeline)
first_build.runner = create(:ci_runner)
first_build.user = create(:user)
diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb
index 34c543bffe8..9ef3b859001 100644
--- a/spec/requests/api/v3/commits_spec.rb
+++ b/spec/requests/api/v3/commits_spec.rb
@@ -36,7 +36,7 @@ describe API::V3::Commits do
context "since optional parameter" do
it "returns project commits since provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
since = commits.second.created_at
get v3_api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user)
@@ -49,12 +49,12 @@ describe API::V3::Commits do
context "until optional parameter" do
it "returns project commits until provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 20)
before = commits.second.created_at
get v3_api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
- if commits.size >= 20
+ if commits.size == 20
expect(json_response.size).to eq(20)
else
expect(json_response.size).to eq(commits.size - 1)
diff --git a/spec/requests/api/v3/deploy_keys_spec.rb b/spec/requests/api/v3/deploy_keys_spec.rb
index 785bc1eb4ba..501af587ad4 100644
--- a/spec/requests/api/v3/deploy_keys_spec.rb
+++ b/spec/requests/api/v3/deploy_keys_spec.rb
@@ -107,7 +107,7 @@ describe API::V3::DeployKeys do
end
it 'accepts can_push parameter' do
- key_attrs = attributes_for :write_access_key
+ key_attrs = attributes_for(:another_key).merge(can_push: true)
post v3_api("/projects/#{project.id}/#{path}", admin), key_attrs
diff --git a/spec/requests/api/v3/members_spec.rb b/spec/requests/api/v3/members_spec.rb
index b91782ae511..de4339ecb8b 100644
--- a/spec/requests/api/v3/members_spec.rb
+++ b/spec/requests/api/v3/members_spec.rb
@@ -58,6 +58,16 @@ describe API::V3::Members do
expect(json_response.count).to eq(1)
expect(json_response.first['username']).to eq(master.username)
end
+
+ it 'finds all members with no query specified' do
+ get v3_api("/#{source_type.pluralize}/#{source.id}/members", developer), query: ''
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.count).to eq(2)
+ expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ end
end
end
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index b8b7d9d1c40..6b748369f0d 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -371,16 +371,28 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(400)
end
- context 'when target_branch is specified' do
+ context 'when target_branch and target_project_id is specified' do
+ let(:params) do
+ { title: 'Test merge_request',
+ target_branch: 'master',
+ source_branch: 'markdown',
+ author: user2,
+ target_project_id: unrelated_project.id }
+ end
+
it 'returns 422 if targeting a different fork' do
- post v3_api("/projects/#{forked_project.id}/merge_requests", user2),
- title: 'Test merge_request',
- target_branch: 'master',
- source_branch: 'markdown',
- author: user2,
- target_project_id: unrelated_project.id
+ unrelated_project.add_developer(user2)
+
+ post v3_api("/projects/#{forked_project.id}/merge_requests", user2), params
+
expect(response).to have_gitlab_http_status(422)
end
+
+ it 'returns 403 if targeting a different fork which user can not access' do
+ post v3_api("/projects/#{forked_project.id}/merge_requests", user2), params
+
+ expect(response).to have_gitlab_http_status(403)
+ end
end
it "returns 201 when target_branch is specified and for the same project" do
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 5e59bb0d585..bee918a20aa 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -781,11 +781,11 @@ describe 'Git LFS API and storage' do
end
context 'when deploy key has project push access' do
- let(:key) { create(:deploy_key, can_push: true) }
+ let(:key) { create(:deploy_key) }
let(:authorization) { authorize_deploy_key }
let(:update_user_permissions) do
- project.deploy_keys << key
+ project.deploy_keys_projects.create(deploy_key: key, can_push: true)
end
it_behaves_like 'pushes new LFS objects'
diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb
index d3aefa2c9eb..2bd8162d1b7 100644
--- a/spec/serializers/deploy_key_entity_spec.rb
+++ b/spec/serializers/deploy_key_entity_spec.rb
@@ -21,18 +21,21 @@ describe DeployKeyEntity do
user_id: deploy_key.user_id,
title: deploy_key.title,
fingerprint: deploy_key.fingerprint,
- can_push: deploy_key.can_push,
destroyed_when_orphaned: true,
almost_orphaned: false,
created_at: deploy_key.created_at,
updated_at: deploy_key.updated_at,
can_edit: false,
- projects: [
+ deploy_keys_projects: [
{
- id: project.id,
- name: project.name,
- full_path: project_path(project),
- full_name: project.full_name
+ can_push: false,
+ project:
+ {
+ id: project.id,
+ name: project.name,
+ full_path: project_path(project),
+ full_name: project.full_name
+ }
}
]
}
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 43b0c9a63a9..16bfbdf3089 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -72,13 +72,15 @@ describe Files::UpdateService do
end
end
- context 'when target branch is different than source branch' do
- let(:branch_name) { "#{project.default_branch}-new" }
+ context 'with gitaly disabled', :skip_gitaly_mock do
+ context 'when target branch is different than source branch' do
+ let(:branch_name) { "#{project.default_branch}-new" }
- it 'fires hooks only once' do
- expect(Gitlab::Git::HooksService).to receive(:new).once.and_call_original
+ it 'fires hooks only once' do
+ expect(Gitlab::Git::HooksService).to receive(:new).once.and_call_original
- subject.execute
+ subject.execute
+ end
end
end
end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index f3c98fa5416..388c9d63c7b 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -297,9 +297,11 @@ describe Issues::MoveService do
end
context 'project issue hooks' do
- let(:hook) { create(:project_hook, project: old_project, issues_events: true) }
+ let!(:hook) { create(:project_hook, project: old_project, issues_events: true) }
it 'executes project issue hooks' do
+ allow_any_instance_of(WebHookService).to receive(:execute)
+
# Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
# but since the entire spec run takes place in a transaction, we never
# actually get to the `after_commit` hook that queues these jobs.
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index dd8c803a2f7..5d226f34d2d 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -263,5 +263,66 @@ describe MergeRequests::CreateService do
expect(issue_ids).to match_array([first_issue.id, second_issue.id])
end
end
+
+ context 'when source and target projects are different' do
+ let(:target_project) { create(:project) }
+
+ let(:opts) do
+ {
+ title: 'Awesome merge_request',
+ source_branch: 'feature',
+ target_branch: 'master',
+ target_project_id: target_project.id
+ }
+ end
+
+ context 'when user can not access source project' do
+ before do
+ target_project.add_developer(assignee)
+ target_project.add_master(user)
+ end
+
+ it 'raises an error' do
+ expect { described_class.new(project, user, opts).execute }
+ .to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+
+ context 'when user can not access target project' do
+ before do
+ target_project.add_developer(assignee)
+ target_project.add_master(user)
+ end
+
+ it 'raises an error' do
+ expect { described_class.new(project, user, opts).execute }
+ .to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+ end
+
+ context 'when user sets source project id' do
+ let(:another_project) { create(:project) }
+
+ let(:opts) do
+ {
+ title: 'Awesome merge_request',
+ source_branch: 'feature',
+ target_branch: 'master',
+ source_project_id: another_project.id
+ }
+ end
+
+ before do
+ project.add_developer(assignee)
+ project.add_master(user)
+ end
+
+ it 'ignores source_project_id' do
+ merge_request = described_class.new(project, user, opts).execute
+
+ expect(merge_request.source_project_id).to eq(project.id)
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 7a01d3dd698..7c3374c6113 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -55,11 +55,12 @@ describe MergeRequests::RefreshService do
before do
allow(refresh_service).to receive(:execute_hooks)
- refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
- reload_mrs
end
it 'executes hooks with update action' do
+ refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
+ reload_mrs
+
expect(refresh_service).to have_received(:execute_hooks)
.with(@merge_request, 'update', old_rev: @oldrev)
@@ -72,6 +73,26 @@ describe MergeRequests::RefreshService do
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
end
+
+ context 'when source branch ref does not exists' do
+ before do
+ DeleteBranchService.new(@project, @user).execute(@merge_request.source_branch)
+ end
+
+ it 'closes MRs without source branch ref' do
+ expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
+ .to change { @merge_request.reload.state }
+ .from('opened')
+ .to('closed')
+
+ expect(@fork_merge_request.reload).to be_open
+ end
+
+ it 'does not change the merge request diff' do
+ expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
+ .not_to change { @merge_request.reload.merge_request_diff }
+ end
+ end
end
context 'when pipeline exists for the source branch' do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 43e2643f709..5c59455e3e1 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe NotificationService, :mailer do
+ include EmailSpec::Matchers
+
let(:notification) { described_class.new }
let(:assignee) { create(:user) }
@@ -31,6 +33,14 @@ describe NotificationService, :mailer do
send_notifications(@u_disabled)
should_not_email_anyone
end
+
+ it 'sends the proper notification reason header' do
+ send_notifications(@u_watcher)
+ should_only_email(@u_watcher)
+ email = find_email_for(@u_watcher)
+
+ expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::MENTIONED)
+ end
end
# Next shared examples are intended to test notifications of "participants"
@@ -511,12 +521,35 @@ describe NotificationService, :mailer do
should_not_email(issue.assignees.first)
end
+ it 'properly prioritizes notification reason' do
+ # have assignee be both assigned and mentioned
+ issue.update_attribute(:description, "/cc #{assignee.to_reference} #{@u_mentioned.to_reference}")
+
+ notification.new_issue(issue, @u_disabled)
+
+ email = find_email_for(assignee)
+ expect(email).to have_header('X-GitLab-NotificationReason', 'assigned')
+
+ email = find_email_for(@u_mentioned)
+ expect(email).to have_header('X-GitLab-NotificationReason', 'mentioned')
+ end
+
+ it 'adds "assigned" reason for assignees if any' do
+ notification.new_issue(issue, @u_disabled)
+
+ email = find_email_for(assignee)
+
+ expect(email).to have_header('X-GitLab-NotificationReason', 'assigned')
+ end
+
it "emails any mentioned users with the mention level" do
issue.description = @u_mentioned.to_reference
notification.new_issue(issue, @u_disabled)
- should_email(@u_mentioned)
+ email = find_email_for(@u_mentioned)
+ expect(email).not_to be_nil
+ expect(email).to have_header('X-GitLab-NotificationReason', 'mentioned')
end
it "emails the author if they've opted into notifications about their activity" do
@@ -620,6 +653,14 @@ describe NotificationService, :mailer do
should_not_email(@u_lazy_participant)
end
+ it 'adds "assigned" reason for new assignee' do
+ notification.reassigned_issue(issue, @u_disabled, [assignee])
+
+ email = find_email_for(assignee)
+
+ expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+
it 'emails previous assignee even if he has the "on mention" notif level' do
issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_disabled, [@u_watcher])
@@ -910,6 +951,14 @@ describe NotificationService, :mailer do
should_not_email(@u_lazy_participant)
end
+ it 'adds "assigned" reason for assignee, if any' do
+ notification.new_merge_request(merge_request, @u_disabled)
+
+ email = find_email_for(merge_request.assignee)
+
+ expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+
it "emails any mentioned users with the mention level" do
merge_request.description = @u_mentioned.to_reference
@@ -924,6 +973,9 @@ describe NotificationService, :mailer do
notification.new_merge_request(merge_request, merge_request.author)
should_email(merge_request.author)
+
+ email = find_email_for(merge_request.author)
+ expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::OWN_ACTIVITY)
end
it "doesn't email the author if they haven't opted into notifications about their activity" do
@@ -1009,6 +1061,14 @@ describe NotificationService, :mailer do
should_not_email(@u_lazy_participant)
end
+ it 'adds "assigned" reason for new assignee' do
+ notification.reassigned_merge_request(merge_request, merge_request.author)
+
+ email = find_email_for(merge_request.assignee)
+
+ expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
+ end
+
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index 7a8c54673f7..f7ff8b80bd7 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -93,26 +93,27 @@ describe Projects::AutocompleteService do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
- let!(:group_milestone) { create(:milestone, group: group) }
- let!(:project_milestone) { create(:milestone, project: project) }
+ let!(:group_milestone1) { create(:milestone, group: group, due_date: '2017-01-01', title: 'Second Title') }
+ let!(:group_milestone2) { create(:milestone, group: group, due_date: '2017-01-01', title: 'First Title') }
+ let!(:project_milestone) { create(:milestone, project: project, due_date: '2016-01-01') }
let(:milestone_titles) { described_class.new(project, user).milestones.map(&:title) }
- it 'includes project and group milestones' do
- expect(milestone_titles).to eq([group_milestone.title, project_milestone.title])
+ it 'includes project and group milestones and sorts them correctly' do
+ expect(milestone_titles).to eq([project_milestone.title, group_milestone2.title, group_milestone1.title])
end
it 'does not include closed milestones' do
- group_milestone.close
+ group_milestone1.close
- expect(milestone_titles).to eq([project_milestone.title])
+ expect(milestone_titles).to eq([project_milestone.title, group_milestone2.title])
end
it 'does not include milestones from other projects in the group' do
other_project = create(:project, group: group)
project_milestone.update!(project: other_project)
- expect(milestone_titles).to eq([group_milestone.title])
+ expect(milestone_titles).to eq([group_milestone2.title, group_milestone1.title])
end
end
end
diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb
new file mode 100644
index 00000000000..bb0e274c93e
--- /dev/null
+++ b/spec/services/projects/gitlab_projects_import_service_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe Projects::GitlabProjectsImportService do
+ set(:namespace) { build(:namespace) }
+ let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ subject { described_class.new(namespace.owner, { namespace_id: namespace.id, path: path, file: file }) }
+
+ describe '#execute' do
+ context 'with an invalid path' do
+ let(:path) { '/invalid-path/' }
+
+ it 'returns an invalid project' do
+ project = subject.execute
+
+ expect(project).not_to be_persisted
+ expect(project).not_to be_valid
+ end
+ end
+
+ context 'with a valid path' do
+ let(:path) { 'test-path' }
+
+ it 'creates a project' do
+ project = subject.execute
+
+ expect(project).to be_persisted
+ expect(project).to be_valid
+ end
+ end
+ end
+end
diff --git a/spec/services/test_hooks/system_service_spec.rb b/spec/services/test_hooks/system_service_spec.rb
index ff8b9595538..74d7715e50f 100644
--- a/spec/services/test_hooks/system_service_spec.rb
+++ b/spec/services/test_hooks/system_service_spec.rb
@@ -60,5 +60,25 @@ describe TestHooks::SystemService do
expect(service.execute).to include(success_result)
end
end
+
+ context 'merge_requests_events' do
+ let(:trigger) { 'merge_requests_events' }
+
+ it 'returns error message if the user does not have any repository with a merge request' do
+ expect(hook).not_to receive(:execute)
+ expect(service.execute).to include({ status: :error, message: 'Ensure one of your projects has merge requests.' })
+ end
+
+ it 'executes hook' do
+ trigger_key = :merge_request_hooks
+ sample_data = { data: 'sample' }
+ create(:project_member, user: current_user, project: project)
+ create(:merge_request, source_project: project)
+ allow_any_instance_of(MergeRequest).to receive(:to_hook_data).and_return(sample_data)
+
+ expect(hook).to receive(:execute).with(sample_data, trigger_key).and_return(success_result)
+ expect(service.execute).to include(success_result)
+ end
+ end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 6186fb92bad..85de0a14631 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -100,6 +100,16 @@ RSpec.configure do |config|
config.before(:example) do
# Skip pre-receive hook check so we can use the web editor and merge.
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
+
+ allow_any_instance_of(Gitlab::Git::GitlabProjects).to receive(:fork_repository).and_wrap_original do |m, *args|
+ m.call(*args)
+
+ shard_path, repository_relative_path = args
+ # We can't leave the hooks in place after a fork, as those would fail in tests
+ # The "internal" API is not available
+ FileUtils.rm_rf(File.join(shard_path, repository_relative_path, 'hooks'))
+ end
+
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
end
diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/cycle_analytics_helpers.rb
index 26fd271ce31..d5ef80cfab2 100644
--- a/spec/support/cycle_analytics_helpers.rb
+++ b/spec/support/cycle_analytics_helpers.rb
@@ -5,10 +5,16 @@ module CycleAnalyticsHelpers
end
def create_commit(message, project, user, branch_name, count: 1)
- oldrev = project.repository.commit(branch_name).sha
+ repository = project.repository
+ oldrev = repository.commit(branch_name).sha
+
+ if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files)
+ mock_gitaly_multi_action_dates(repository.raw)
+ end
+
commit_shas = Array.new(count) do |index|
- commit_sha = project.repository.create_file(user, generate(:branch), "content", message: message, branch_name: branch_name)
- project.repository.commit(commit_sha)
+ commit_sha = repository.create_file(user, generate(:branch), "content", message: message, branch_name: branch_name)
+ repository.commit(commit_sha)
commit_sha
end
@@ -98,6 +104,25 @@ module CycleAnalyticsHelpers
pipeline: dummy_pipeline,
protected: false)
end
+
+ def mock_gitaly_multi_action_dates(raw_repository)
+ allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args|
+ new_date = Time.now
+ branch_update = m.call(*args)
+
+ if branch_update.newrev
+ _, opts = args
+ commit = raw_repository.commit(branch_update.newrev).rugged_commit
+ branch_update.newrev = commit.amend(
+ update_ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}#{opts[:branch_name]}",
+ author: commit.author.merge(time: new_date),
+ committer: commit.committer.merge(time: new_date)
+ )
+ end
+
+ branch_update
+ end
+ end
end
RSpec.configure do |config|
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index edaee03ea6c..1809ae1d141 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -1,10 +1,26 @@
+require 'database_cleaner/active_record/deletion'
+
+module FakeInformationSchema
+ # Work around a bug in DatabaseCleaner when using the deletion strategy:
+ # https://github.com/DatabaseCleaner/database_cleaner/issues/347
+ #
+ # On MySQL, if the information schema is said to exist, we use an inaccurate
+ # row count leading to some tables not being cleaned when they should
+ def information_schema_exists?(_connection)
+ false
+ end
+end
+
+DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema)
+
RSpec.configure do |config|
+ # Ensure all sequences are reset at the start of the suite run
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.append_after(:context) do
- DatabaseCleaner.clean_with(:truncation, cache_tables: false)
+ DatabaseCleaner.clean_with(:deletion, cache_tables: false)
end
config.before(:each) do
@@ -12,15 +28,15 @@ RSpec.configure do |config|
end
config.before(:each, :js) do
- DatabaseCleaner.strategy = :truncation
+ DatabaseCleaner.strategy = :deletion
end
- config.before(:each, :truncate) do
- DatabaseCleaner.strategy = :truncation
+ config.before(:each, :delete) do
+ DatabaseCleaner.strategy = :deletion
end
config.before(:each, :migration) do
- DatabaseCleaner.strategy = :truncation, { cache_tables: false }
+ DatabaseCleaner.strategy = :deletion, { cache_tables: false }
end
config.before(:each) do
diff --git a/spec/support/devise_helpers.rb b/spec/support/devise_helpers.rb
index 890a2d9d287..66874e10f38 100644
--- a/spec/support/devise_helpers.rb
+++ b/spec/support/devise_helpers.rb
@@ -2,13 +2,16 @@ module DeviseHelpers
# explicitly tells Devise which mapping to use
# this is needed when we are testing a Devise controller bypassing the router
def set_devise_mapping(context:)
- env =
- if context.respond_to?(:env_config)
- context.env_config
- elsif context.respond_to?(:env)
- context.env
- end
+ env = env_from_context(context)
env['devise.mapping'] = Devise.mappings[:user] if env
end
+
+ def env_from_context(context)
+ if context.respond_to?(:env_config)
+ context.env_config
+ elsif context.respond_to?(:env)
+ context.env
+ end
+ end
end
diff --git a/spec/support/email_helpers.rb b/spec/support/email_helpers.rb
index b39052923dd..1fb8252459f 100644
--- a/spec/support/email_helpers.rb
+++ b/spec/support/email_helpers.rb
@@ -30,4 +30,8 @@ module EmailHelpers
def email_recipients(kind: :to)
ActionMailer::Base.deliveries.flat_map(&kind)
end
+
+ def find_email_for(user)
+ ActionMailer::Base.deliveries.find { |d| d.to.include?(user.notification_email) }
+ end
end
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index fa94aa2ae3d..c8662d41769 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -143,15 +143,17 @@ shared_examples 'discussion comments' do |resource_name|
end
if resource_name == 'merge request'
+ let(:note_id) { find("#{comments_selector} .note", match: :first)['data-note-id'] }
+
it 'shows resolved discussion when toggled' do
click_button "Resolve discussion"
- expect(page).to have_selector('.note-row-1', visible: true)
+ expect(page).to have_selector(".note-row-#{note_id}", visible: true)
refresh
click_button "Toggle discussion"
- expect(page).to have_selector('.note-row-1', visible: true)
+ expect(page).to have_selector(".note-row-#{note_id}", visible: true)
end
end
end
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index 50702a0ac88..b52b6a28c54 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -125,6 +125,13 @@ module LoginHelpers
})
end
+ def stub_omniauth_provider(provider, context: Rails.application)
+ env = env_from_context(context)
+
+ set_devise_mapping(context: context)
+ env['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
+ end
+
def stub_omniauth_saml_config(messages)
set_devise_mapping(context: Rails.application)
Rails.application.routes.disable_clear_and_finalize = true
diff --git a/spec/support/project_forks_helper.rb b/spec/support/project_forks_helper.rb
index d6680735aa1..2c501a2a27c 100644
--- a/spec/support/project_forks_helper.rb
+++ b/spec/support/project_forks_helper.rb
@@ -38,10 +38,6 @@ module ProjectForksHelper
# so we have to explicitely call this method to clear the @exists variable.
# of the instance we're returning here.
forked_project.repository.after_import
-
- # We can't leave the hooks in place after a fork, as those would fail in tests
- # The "internal" API is not available
- FileUtils.rm_rf("#{forked_project.repository.path}/hooks")
end
forked_project
diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb
index 3f1fd169b72..23f9b46ae0c 100644
--- a/spec/support/services_shared_context.rb
+++ b/spec/support/services_shared_context.rb
@@ -3,13 +3,9 @@ Service.available_services_names.each do |service|
let(:dashed_service) { service.dasherize }
let(:service_method) { "#{service}_service".to_sym }
let(:service_klass) { "#{service}_service".classify.constantize }
- let(:service_fields) { service_klass.new.fields }
+ let(:service_instance) { service_klass.new }
+ let(:service_fields) { service_instance.fields }
let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } }
- let(:service_attrs_list_without_passwords) do
- service_fields
- .select { |field| field[:type] != 'password' }
- .map { |field| field[:name].to_sym}
- end
let(:service_attrs) do
service_attrs_list.inject({}) do |hash, k|
if k =~ /^(token*|.*_token|.*_key)/
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
new file mode 100644
index 00000000000..5b0b609f7f2
--- /dev/null
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -0,0 +1,99 @@
+RSpec.shared_examples 'a creatable merge request' do
+ include WaitForRequests
+
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:target_project) { create(:project, :public, :repository) }
+ let(:source_project) { target_project }
+ let!(:milestone) { create(:milestone, project: target_project) }
+ let!(:label) { create(:label, project: target_project) }
+ let!(:label2) { create(:label, project: target_project) }
+
+ before do
+ source_project.add_master(user)
+ target_project.add_master(user)
+ target_project.add_master(user2)
+
+ sign_in(user)
+ visit project_new_merge_request_path(
+ target_project,
+ merge_request: {
+ source_project_id: source_project.id,
+ target_project_id: target_project.id,
+ source_branch: 'fix',
+ target_branch: 'master'
+ })
+ end
+
+ it 'creates new merge request', :js do
+ click_button 'Assignee'
+ page.within '.dropdown-menu-user' do
+ click_link user2.name
+ end
+ expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user2.id.to_s)
+ page.within '.js-assignee-search' do
+ expect(page).to have_content user2.name
+ end
+
+ click_link 'Assign to me'
+ expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
+ page.within '.js-assignee-search' do
+ expect(page).to have_content user.name
+ end
+
+ click_button 'Milestone'
+ page.within '.issue-milestone' do
+ click_link milestone.title
+ end
+ expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
+ page.within '.js-milestone-select' do
+ expect(page).to have_content milestone.title
+ end
+
+ click_button 'Labels'
+ page.within '.dropdown-menu-labels' do
+ click_link label.title
+ click_link label2.title
+ end
+ page.within '.js-label-select' do
+ expect(page).to have_content label.title
+ end
+ expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
+ expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
+
+ click_button 'Submit merge request'
+
+ page.within '.issuable-sidebar' do
+ page.within '.assignee' do
+ expect(page).to have_content user.name
+ end
+
+ page.within '.milestone' do
+ expect(page).to have_content milestone.title
+ end
+
+ page.within '.labels' do
+ expect(page).to have_content label.title
+ expect(page).to have_content label2.title
+ end
+ end
+ end
+
+ it 'updates the branches when selecting a new target project' do
+ target_project_member = target_project.owner
+ CreateBranchService.new(target_project, target_project_member)
+ .execute('a-brand-new-branch-to-test', 'master')
+ visit project_new_merge_request_path(source_project)
+
+ first('.js-target-project').click
+ find('.dropdown-target-project .dropdown-content a', text: target_project.full_path).click
+
+ wait_for_requests
+
+ first('.js-target-branch').click
+
+ within('.dropdown-target-branch .dropdown-content') do
+ expect(page).to have_content('a-brand-new-branch-to-test')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
new file mode 100644
index 00000000000..645db41cddc
--- /dev/null
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -0,0 +1,140 @@
+RSpec.shared_examples 'an editable merge request' do
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let!(:milestone) { create(:milestone, project: target_project) }
+ let!(:label) { create(:label, project: target_project) }
+ let!(:label2) { create(:label, project: target_project) }
+ let(:target_project) { create(:project, :public, :repository) }
+ let(:source_project) { target_project }
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: source_project,
+ target_project: target_project,
+ source_branch: 'fix',
+ target_branch: 'master')
+ end
+
+ before do
+ source_project.add_master(user)
+ target_project.add_master(user)
+ target_project.add_master(user2)
+
+ sign_in(user)
+ visit edit_project_merge_request_path(target_project, merge_request)
+ end
+
+ it 'updates merge request', :js do
+ click_button 'Assignee'
+ page.within '.dropdown-menu-user' do
+ click_link user.name
+ end
+ expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
+ page.within '.js-assignee-search' do
+ expect(page).to have_content user.name
+ end
+
+ click_button 'Milestone'
+ page.within '.issue-milestone' do
+ click_link milestone.title
+ end
+ expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
+ page.within '.js-milestone-select' do
+ expect(page).to have_content milestone.title
+ end
+
+ click_button 'Labels'
+ page.within '.dropdown-menu-labels' do
+ click_link label.title
+ click_link label2.title
+ end
+ expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
+ expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
+ page.within '.js-label-select' do
+ expect(page).to have_content label.title
+ end
+
+ click_button 'Save changes'
+
+ page.within '.issuable-sidebar' do
+ page.within '.assignee' do
+ expect(page).to have_content user.name
+ end
+
+ page.within '.milestone' do
+ expect(page).to have_content milestone.title
+ end
+
+ page.within '.labels' do
+ expect(page).to have_content label.title
+ expect(page).to have_content label2.title
+ end
+ end
+ end
+
+ it 'description has autocomplete', :js do
+ find('#merge_request_description').native.send_keys('')
+ fill_in 'merge_request_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
+
+ it 'has class js-quick-submit in form' do
+ expect(page).to have_selector('.js-quick-submit')
+ end
+
+ it 'warns about version conflict' do
+ merge_request.update(title: "New title")
+
+ fill_in 'merge_request_title', with: 'bug 345'
+ fill_in 'merge_request_description', with: 'bug description'
+
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Someone edited the merge request the same time you did'
+ end
+
+ it 'preserves description textarea height', :js do
+ long_description = %q(
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ac ornare ligula, ut tempus arcu. Etiam ultricies accumsan dolor vitae faucibus. Donec at elit lacus. Mauris orci ante, aliquam quis lorem eget, convallis faucibus arcu. Aenean at pulvinar lacus. Ut viverra quam massa, molestie ornare tortor dignissim a. Suspendisse tristique pellentesque tellus, id lacinia metus elementum id. Nam tristique, arcu rhoncus faucibus viverra, lacus ipsum sagittis ligula, vitae convallis odio lacus a nibh. Ut tincidunt est purus, ac vestibulum augue maximus in. Suspendisse vel erat et mi ultricies semper. Pellentesque volutpat pellentesque consequat.
+
+ Cras congue nec ligula tristique viverra. Curabitur fringilla fringilla fringilla. Donec rhoncus dignissim orci ut accumsan. Ut rutrum urna a rhoncus varius. Maecenas blandit, mauris nec accumsan gravida, augue nibh finibus magna, sed maximus turpis libero nec neque. Suspendisse at semper est. Nunc imperdiet dapibus dui, varius sollicitudin erat luctus non. Sed pellentesque ligula eget posuere facilisis. Donec dictum commodo volutpat. Donec egestas dui ac magna sollicitudin bibendum. Vivamus purus neque, ullamcorper ac feugiat et, tempus sit amet metus. Praesent quis viverra neque. Sed bibendum viverra est, eu aliquam mi ornare vitae. Proin et dapibus ipsum. Nunc tortor diam, malesuada nec interdum vel, placerat quis justo. Ut viverra at erat eu laoreet.
+
+ Pellentesque commodo, diam sit amet dignissim condimentum, tortor justo pretium est, non venenatis metus eros ut nunc. Etiam ut neque eget sem dapibus aliquam. Curabitur vel elit lorem. Nulla nec enim elit. Sed ut ex id justo facilisis convallis at ac augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nullam cursus egestas turpis non tristique. Suspendisse in erat sem. Fusce libero elit, fermentum gravida mauris id, auctor iaculis felis. Nullam vulputate tempor laoreet.
+
+ Nam tempor et magna sed convallis. Fusce sit amet sollicitudin risus, a ullamcorper lacus. Morbi gravida quis sem eget porttitor. Donec eu egestas mauris, in elementum tortor. Sed eget ex mi. Mauris iaculis tortor ut est auctor, nec dignissim quam sagittis. Suspendisse vel metus non quam suscipit tincidunt. Cras molestie lacus non justo finibus sodales quis vitae erat. In a porttitor nisi, id sollicitudin urna. Ut at felis tellus. Suspendisse potenti.
+
+ Maecenas leo ligula, varius at neque vitae, ornare maximus justo. Nullam convallis luctus risus et vulputate. Duis suscipit faucibus iaculis. Etiam quis tortor faucibus, tristique tellus sit amet, sodales neque. Nulla dapibus nisi vel aliquet consequat. Etiam faucibus, metus eget condimentum iaculis, enim urna lobortis sem, id efficitur eros sapien nec nisi. Aenean ut finibus ex.
+ )
+
+ fill_in 'merge_request_description', with: long_description
+
+ height = get_textarea_height
+ find('.js-md-preview-button').click
+ find('.js-md-write-button').click
+ new_height = get_textarea_height
+
+ expect(height).to eq(new_height)
+ end
+
+ context 'when "Remove source branch" is set' do
+ before do
+ merge_request.update!(merge_params: { 'force_remove_source_branch' => '1' })
+ end
+
+ it 'allows to unselect "Remove source branch"', :js do
+ expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
+
+ visit edit_project_merge_request_path(target_project, merge_request)
+ uncheck 'Remove source branch when merge request is accepted'
+
+ click_button 'Save changes'
+
+ expect(page).to have_unchecked_field 'remove-source-branch-input'
+ expect(page).to have_content 'Remove source branch'
+ end
+ end
+end
+
+def get_textarea_height
+ page.evaluate_script('document.getElementById("merge_request_description").offsetHeight')
+end
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index 17f3a861ba8..e827a8da0b7 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -57,6 +57,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
@issue = issue_service.execute
@issues_sample_data = issue_service.hook_data(@issue, 'open')
+ project.add_developer(user)
opts = {
title: 'Awesome merge_request',
description: 'please fix',
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index fd6368e7b40..9e5f08fbc51 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -20,7 +20,7 @@ module TestEnv
'improve/awesome' => '5937ac0',
'merged-target' => '21751bf',
'markdown' => '0ed8c6c',
- 'lfs' => 'be93687',
+ 'lfs' => '55bc176',
'master' => 'b83d6e3',
'merge-test' => '5937ac0',
"'test'" => 'e56497b',
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index 6aba86fdc3c..b37d6ac831f 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -76,7 +76,11 @@ describe 'gitlab:gitaly namespace rake task' do
end
context 'when Rails.env is test' do
- let(:command) { %w[make BUNDLE_FLAGS=--no-deployment] }
+ let(:command) do
+ %W[make
+ BUNDLE_FLAGS=--no-deployment
+ BUNDLE_PATH=#{Bundler.bundle_path}]
+ end
before do
allow(Rails.env).to receive(:test?).and_return(true)
diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb
index 14fd5f3600f..98a4373e9d0 100644
--- a/spec/uploaders/job_artifact_uploader_spec.rb
+++ b/spec/uploaders/job_artifact_uploader_spec.rb
@@ -8,7 +8,7 @@ describe JobArtifactUploader do
describe '#store_dir' do
subject { uploader.store_dir }
- let(:path) { "#{job_artifact.created_at.utc.strftime('%Y_%m_%d')}/#{job_artifact.project_id}/#{job_artifact.id}" }
+ let(:path) { "#{job_artifact.created_at.utc.strftime('%Y_%m_%d')}/#{job_artifact.job_id}/#{job_artifact.id}" }
context 'when using local storage' do
it { is_expected.to start_with(local_path) }
@@ -45,7 +45,7 @@ describe JobArtifactUploader do
it { is_expected.to start_with(local_path) }
it { is_expected.to include("/#{job_artifact.created_at.utc.strftime('%Y_%m_%d')}/") }
- it { is_expected.to include("/#{job_artifact.project_id}/") }
+ it { is_expected.to include("/#{job_artifact.job_id}/#{job_artifact.id}/") }
it { is_expected.to end_with("ci_build_artifacts.zip") }
end
end
diff --git a/spec/views/projects/pipelines_settings/_show.html.haml_spec.rb b/spec/views/projects/pipelines_settings/_show.html.haml_spec.rb
index 95f0be49412..7b300150874 100644
--- a/spec/views/projects/pipelines_settings/_show.html.haml_spec.rb
+++ b/spec/views/projects/pipelines_settings/_show.html.haml_spec.rb
@@ -13,8 +13,8 @@ describe 'projects/pipelines_settings/_show' do
render
expect(rendered).to have_css('.settings-message')
- expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name and the')
- expect(rendered).to have_link('Kubernetes service')
+ expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name and a')
+ expect(rendered).to have_link('Kubernetes cluster')
end
end
@@ -27,8 +27,8 @@ describe 'projects/pipelines_settings/_show' do
render
expect(rendered).to have_css('.settings-message')
- expect(rendered).to have_text('Auto Review Apps and Auto Deploy need the')
- expect(rendered).to have_link('Kubernetes service')
+ expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a')
+ expect(rendered).to have_link('Kubernetes cluster')
end
end
end
diff --git a/spec/workers/new_issue_worker_spec.rb b/spec/workers/new_issue_worker_spec.rb
index 4e15ccc534b..baa8ddb59e5 100644
--- a/spec/workers/new_issue_worker_spec.rb
+++ b/spec/workers/new_issue_worker_spec.rb
@@ -44,8 +44,9 @@ describe NewIssueWorker do
expect { worker.perform(issue.id, user.id) }.to change { Event.count }.from(0).to(1)
end
- it 'creates a notification for the assignee' do
- expect(Notify).to receive(:new_issue_email).with(mentioned.id, issue.id).and_return(double(deliver_later: true))
+ it 'creates a notification for the mentioned user' do
+ expect(Notify).to receive(:new_issue_email).with(mentioned.id, issue.id, NotificationReason::MENTIONED)
+ .and_return(double(deliver_later: true))
worker.perform(issue.id, user.id)
end
diff --git a/spec/workers/new_merge_request_worker_spec.rb b/spec/workers/new_merge_request_worker_spec.rb
index 9e0cbde45b1..c3f29a40d58 100644
--- a/spec/workers/new_merge_request_worker_spec.rb
+++ b/spec/workers/new_merge_request_worker_spec.rb
@@ -46,8 +46,10 @@ describe NewMergeRequestWorker do
expect { worker.perform(merge_request.id, user.id) }.to change { Event.count }.from(0).to(1)
end
- it 'creates a notification for the assignee' do
- expect(Notify).to receive(:new_merge_request_email).with(mentioned.id, merge_request.id).and_return(double(deliver_later: true))
+ it 'creates a notification for the mentioned user' do
+ expect(Notify).to receive(:new_merge_request_email)
+ .with(mentioned.id, merge_request.id, NotificationReason::MENTIONED)
+ .and_return(double(deliver_later: true))
worker.perform(merge_request.id, user.id)
end
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 7274a9f00f9..2b1a617ee62 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -49,9 +49,22 @@ describe RepositoryImportWorker do
expect do
subject.perform(project.id)
- end.to raise_error(StandardError, error)
+ end.to raise_error(RuntimeError, error)
expect(project.reload.import_jid).not_to be_nil
end
+
+ it 'updates the error on Import/Export' do
+ error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
+
+ project.update_attributes(import_jid: '123', import_type: 'gitlab_project')
+ expect_any_instance_of(Projects::ImportService).to receive(:execute).and_return({ status: :error, message: error })
+
+ expect do
+ subject.perform(project.id)
+ end.to raise_error(RuntimeError, error)
+
+ expect(project.reload.import_error).not_to be_nil
+ end
end
context 'when using an asynchronous importer' do
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index eec356b9f47..a7cd2bc972c 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -42,6 +42,7 @@ stages:
- build
- test
- review
+ - dast
- staging
- canary
- production
@@ -130,6 +131,23 @@ sast:container:
artifacts:
paths: [gl-sast-container-report.json]
+dast:
+ stage: dast
+ allow_failure: true
+ image: owasp/zap2docker-stable
+ variables:
+ POSTGRES_DB: "false"
+ script:
+ - dast
+ artifacts:
+ paths: [gl-dast-report.json]
+ only:
+ refs:
+ - branches
+ kubernetes: active
+ except:
+ - master
+
review:
stage: review
script:
@@ -270,8 +288,8 @@ production:
docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1
apk add -U wget ca-certificates
docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
- wget https://github.com/arminc/clair-scanner/releases/download/v6/clair-scanner_linux_386
- mv clair-scanner_linux_386 clair-scanner
+ wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
+ mv clair-scanner_linux_amd64 clair-scanner
chmod +x clair-scanner
touch clair-whitelist.yml
./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
@@ -462,6 +480,8 @@ production:
}
function create_secret() {
+ echo "Create secret..."
+
kubectl create secret -n "$KUBE_NAMESPACE" \
docker-registry gitlab-registry \
--docker-server="$CI_REGISTRY" \
@@ -471,6 +491,14 @@ production:
-o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
}
+ function dast() {
+ export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
+
+ mkdir /zap/wrk/
+ /zap/zap-baseline.py -J gl-dast-report.json -t "$CI_ENVIRONMENT_URL" || true
+ cp /zap/wrk/gl-dast-report.json .
+ }
+
function performance() {
export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
diff --git a/yarn.lock b/yarn.lock
index 64b66ee446a..d10a4372a40 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -54,14 +54,21 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.5.0":
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.5.0.tgz#6635df6aad6c71fb293ff113efd4311a744c0e2c"
+"@gitlab-org/gitlab-svgs@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.7.0.tgz#dbb1330a1b1ee478378dddab53fe1a881e810f5d"
"@types/jquery@^2.0.40":
version "2.0.48"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.48.tgz#3e90d8cde2d29015e5583017f7830cb3975b2eef"
+JSONStream@^1.0.3:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea"
+ dependencies:
+ jsonparse "^1.2.0"
+ through ">=2.2.7 <3"
+
abbrev@1, abbrev@1.0.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
@@ -101,10 +108,21 @@ acorn@^5.2.1:
version "5.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822"
+addressparser@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746"
+
after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
+agent-base@2:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7"
+ dependencies:
+ extend "~3.0.0"
+ semver "~5.0.1"
+
ajv-keywords@^1.0.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
@@ -129,6 +147,15 @@ ajv@^5.0.0, ajv@^5.1.5:
json-schema-traverse "^0.3.0"
json-stable-stringify "^1.0.1"
+ajv@^5.1.0:
+ version "5.5.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
+ dependencies:
+ co "^4.6.0"
+ fast-deep-equal "^1.0.0"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.3.0"
+
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -161,6 +188,10 @@ ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -211,6 +242,10 @@ arr-flatten@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b"
+array-filter@~0.0.0:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
+
array-find-index@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
@@ -227,6 +262,14 @@ array-flatten@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296"
+array-map@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
+
+array-reduce@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
+
array-slice@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
@@ -237,7 +280,7 @@ array-union@^1.0.1:
dependencies:
array-uniq "^1.0.1"
-array-uniq@^1.0.1:
+array-uniq@^1.0.1, array-uniq@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
@@ -245,9 +288,9 @@ array-unique@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
-arraybuffer.slice@0.0.6:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca"
+arraybuffer.slice@~0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
@@ -273,17 +316,31 @@ assert-plus@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
-assert@^1.1.1:
+assert@^1.1.1, assert@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
dependencies:
util "0.10.3"
+ast-types@0.x.x:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd"
+
+astw@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917"
+ dependencies:
+ acorn "^4.0.3"
+
async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
-async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2:
+async-limiter@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
+
+async@1.x, async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@@ -297,6 +354,12 @@ async@~0.9.0:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
+async@~2.1.2:
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
+ dependencies:
+ lodash "^4.14.0"
+
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -320,7 +383,11 @@ aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
-aws4@^1.2.1:
+aws-sign2@~0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+
+aws4@^1.2.1, aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
@@ -330,6 +397,12 @@ axios-mock-adapter@^1.10.0:
dependencies:
deep-equal "^1.0.1"
+axios@^0.15.3:
+ version "0.15.3"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053"
+ dependencies:
+ follow-redirects "1.0.0"
+
axios@^0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.1.tgz#2d8e3e5d0bdbd7327f91bc814f5c57660f81824d"
@@ -984,6 +1057,12 @@ binary-extensions@^1.0.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0"
+bl@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398"
+ dependencies:
+ readable-stream "~2.0.5"
+
blackst0ne-mermaid@^7.1.0-fixed:
version "7.1.0-fixed"
resolved "https://registry.yarnpkg.com/blackst0ne-mermaid/-/blackst0ne-mermaid-7.1.0-fixed.tgz#3707b3a113d78610e3068e18a588f46b4688de49"
@@ -1049,6 +1128,18 @@ boom@2.x.x:
dependencies:
hoek "2.x.x"
+boom@4.x.x:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
+ dependencies:
+ hoek "4.x.x"
+
+boom@5.x.x:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
+ dependencies:
+ hoek "4.x.x"
+
bootstrap-sass@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.6.tgz#363b0d300e868d3e70134c1a742bb17288444fd1"
@@ -1078,6 +1169,22 @@ brorand@^1.0.1:
version "1.0.7"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.7.tgz#6677fa5e4901bdbf9c9ec2a748e28dca407a9bfc"
+browser-pack@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65fbff4531"
+ dependencies:
+ JSONStream "^1.0.3"
+ combine-source-map "~0.7.1"
+ defined "^1.0.0"
+ through2 "^2.0.0"
+ umd "^3.0.0"
+
+browser-resolve@^1.11.0, browser-resolve@^1.7.0:
+ version "1.11.2"
+ resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
+ dependencies:
+ resolve "1.1.7"
+
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a"
@@ -1129,6 +1236,64 @@ browserify-zlib@^0.1.4:
dependencies:
pako "~0.2.0"
+browserify-zlib@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+ dependencies:
+ pako "~1.0.5"
+
+browserify@^14.5.0:
+ version "14.5.0"
+ resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5"
+ dependencies:
+ JSONStream "^1.0.3"
+ assert "^1.4.0"
+ browser-pack "^6.0.1"
+ browser-resolve "^1.11.0"
+ browserify-zlib "~0.2.0"
+ buffer "^5.0.2"
+ cached-path-relative "^1.0.0"
+ concat-stream "~1.5.1"
+ console-browserify "^1.1.0"
+ constants-browserify "~1.0.0"
+ crypto-browserify "^3.0.0"
+ defined "^1.0.0"
+ deps-sort "^2.0.0"
+ domain-browser "~1.1.0"
+ duplexer2 "~0.1.2"
+ events "~1.1.0"
+ glob "^7.1.0"
+ has "^1.0.0"
+ htmlescape "^1.1.0"
+ https-browserify "^1.0.0"
+ inherits "~2.0.1"
+ insert-module-globals "^7.0.0"
+ labeled-stream-splicer "^2.0.0"
+ module-deps "^4.0.8"
+ os-browserify "~0.3.0"
+ parents "^1.0.1"
+ path-browserify "~0.0.0"
+ process "~0.11.0"
+ punycode "^1.3.2"
+ querystring-es3 "~0.2.0"
+ read-only-stream "^2.0.0"
+ readable-stream "^2.0.2"
+ resolve "^1.1.4"
+ shasum "^1.0.0"
+ shell-quote "^1.6.1"
+ stream-browserify "^2.0.0"
+ stream-http "^2.0.0"
+ string_decoder "~1.0.0"
+ subarg "^1.0.0"
+ syntax-error "^1.1.1"
+ through2 "^2.0.0"
+ timers-browserify "^1.0.1"
+ tty-browserify "~0.0.0"
+ url "~0.11.0"
+ util "~0.10.1"
+ vm-browserify "~0.0.1"
+ xtend "^4.0.0"
+
browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
version "1.7.7"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
@@ -1152,6 +1317,25 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
+buffer@^5.0.2:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24"
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+
+buildmail@4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72"
+ dependencies:
+ addressparser "1.0.1"
+ libbase64 "0.1.0"
+ libmime "3.0.0"
+ libqp "1.1.0"
+ nodemailer-fetch "1.6.0"
+ nodemailer-shared "1.1.0"
+ punycode "1.4.1"
+
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@@ -1168,6 +1352,14 @@ bytes@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a"
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+
+cached-path-relative@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7"
+
caller-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
@@ -1218,6 +1410,10 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000649"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a"
+caseless@~0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
+
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -1239,7 +1435,7 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
-chalk@^2.0.0, chalk@^2.3.0:
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
dependencies:
@@ -1272,6 +1468,10 @@ circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+circular-json@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.4.0.tgz#c448ea998b7fe31ecf472ec29c6b608e2e2a62fd"
+
clap@^1.0.9:
version "1.1.3"
resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b"
@@ -1324,6 +1524,10 @@ co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+co@~3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda"
+
coa@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3"
@@ -1376,6 +1580,15 @@ combine-lists@^1.0.0:
dependencies:
lodash "^4.5.0"
+combine-source-map@~0.7.1:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
+ dependencies:
+ convert-source-map "~1.1.0"
+ inline-source-map "~0.6.0"
+ lodash.memoize "~3.0.3"
+ source-map "~0.5.3"
+
combined-stream@^1.0.5, combined-stream@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
@@ -1396,10 +1609,6 @@ component-bind@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
-component-emitter@1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3"
-
component-emitter@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
@@ -1445,6 +1654,14 @@ concat-stream@^1.5.2:
readable-stream "^2.2.2"
typedarray "^0.0.6"
+concat-stream@~1.5.0, concat-stream@~1.5.1:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
+ dependencies:
+ inherits "~2.0.1"
+ readable-stream "~2.0.0"
+ typedarray "~0.0.5"
+
configstore@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021"
@@ -1487,7 +1704,7 @@ consolidate@^0.14.0:
dependencies:
bluebird "^3.1.1"
-constants-browserify@^1.0.0:
+constants-browserify@^1.0.0, constants-browserify@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@@ -1507,6 +1724,10 @@ convert-source-map@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
+convert-source-map@~1.1.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
+
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -1598,6 +1819,28 @@ cryptiles@2.x.x:
dependencies:
boom "2.x.x"
+cryptiles@3.x.x:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
+ dependencies:
+ boom "5.x.x"
+
+crypto-browserify@^3.0.0:
+ version "3.12.0"
+ resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
+ dependencies:
+ browserify-cipher "^1.0.0"
+ browserify-sign "^4.0.0"
+ create-ecdh "^4.0.0"
+ create-hash "^1.1.0"
+ create-hmac "^1.1.0"
+ diffie-hellman "^5.0.0"
+ inherits "^2.0.1"
+ pbkdf2 "^3.0.3"
+ public-encrypt "^4.0.0"
+ randombytes "^2.0.0"
+ randomfill "^1.0.3"
+
crypto-browserify@^3.11.0:
version "3.11.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522"
@@ -1848,6 +2091,14 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
+data-uri-to-buffer@1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
+
+date-format@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8"
+
date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
@@ -1856,18 +2107,18 @@ de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
-debug@2.2.0:
+debug@2, debug@~2.6.4, debug@~2.6.6, debug@~2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ dependencies:
+ ms "2.0.0"
+
+debug@2.2.0, debug@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
dependencies:
ms "0.7.1"
-debug@2.3.3:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c"
- dependencies:
- ms "0.7.2"
-
debug@2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e"
@@ -1929,6 +2180,14 @@ defined@^1.0.0, defined@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+degenerator@~1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
+ dependencies:
+ ast-types "0.x.x"
+ escodegen "1.x.x"
+ esprima "3.x.x"
+
del@^2.0.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
@@ -1972,6 +2231,15 @@ depd@1.1.1, depd@~1.1.0, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
+deps-sort@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5"
+ dependencies:
+ JSONStream "^1.0.3"
+ shasum "^1.0.0"
+ subarg "^1.0.0"
+ through2 "^2.0.0"
+
des.js@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
@@ -1993,6 +2261,13 @@ detect-node@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
+detective@^4.0.0:
+ version "4.7.1"
+ resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e"
+ dependencies:
+ acorn "^5.2.1"
+ defined "^1.0.0"
+
di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
@@ -2060,7 +2335,7 @@ dom-serializer@0:
domelementtype "~1.1.1"
entities "~1.1.1"
-domain-browser@^1.1.1:
+domain-browser@^1.1.1, domain-browser@~1.1.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
@@ -2085,10 +2360,20 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
+double-ended-queue@^2.1.0-0:
+ version "2.1.0-0"
+ resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
+
dropzone@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3"
+duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+ dependencies:
+ readable-stream "^2.0.2"
+
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@@ -2151,44 +2436,44 @@ end-of-stream@^1.0.0:
dependencies:
once "^1.4.0"
-engine.io-client@1.8.3:
- version "1.8.3"
- resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab"
+engine.io-client@~3.1.0:
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.4.tgz#4fcf1370b47163bd2ce9be2733972430350d4ea1"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
- debug "2.3.3"
- engine.io-parser "1.3.2"
+ debug "~2.6.9"
+ engine.io-parser "~2.1.1"
has-cors "1.1.0"
indexof "0.0.1"
- parsejson "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
- ws "1.1.2"
- xmlhttprequest-ssl "1.5.3"
+ ws "~3.3.1"
+ xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
-engine.io-parser@1.3.2:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a"
+engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196"
dependencies:
after "0.8.2"
- arraybuffer.slice "0.0.6"
+ arraybuffer.slice "~0.0.7"
base64-arraybuffer "0.1.5"
blob "0.0.4"
- has-binary "0.1.7"
- wtf-8 "1.0.0"
+ has-binary2 "~1.0.2"
-engine.io@1.8.3:
- version "1.8.3"
- resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4"
+engine.io@~3.1.0:
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.4.tgz#3d0211b70a552ce841ffc7da8627b301a9a4162e"
dependencies:
accepts "1.3.3"
base64id "1.0.0"
cookie "0.3.1"
- debug "2.3.3"
- engine.io-parser "1.3.2"
- ws "1.1.2"
+ debug "~2.6.9"
+ engine.io-parser "~2.1.0"
+ ws "~3.3.1"
+ optionalDependencies:
+ uws "~0.14.4"
enhanced-resolve@^3.4.0:
version "3.4.1"
@@ -2320,6 +2605,17 @@ escodegen@1.8.x:
optionalDependencies:
source-map "~0.2.0"
+escodegen@1.x.x:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852"
+ dependencies:
+ esprima "^3.1.3"
+ estraverse "^4.2.0"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.5.6"
+
escope@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
@@ -2477,6 +2773,10 @@ esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+esprima@3.x.x, esprima@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
esprima@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
@@ -2541,7 +2841,7 @@ eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
-events@^1.0.0:
+events@^1.0.0, events@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
@@ -2640,7 +2940,7 @@ express@^4.13.3, express@^4.15.2:
utils-merge "1.0.0"
vary "~1.1.1"
-extend@^3.0.0, extend@~3.0.0:
+extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@@ -2658,6 +2958,10 @@ fast-deep-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
+fast-json-stable-stringify@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+
fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
@@ -2704,6 +3008,10 @@ file-loader@^0.11.1:
dependencies:
loader-utils "^1.0.2"
+file-uri-to-path@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+
filename-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775"
@@ -2783,6 +3091,12 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
+follow-redirects@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37"
+ dependencies:
+ debug "^2.2.0"
+
follow-redirects@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.6.tgz#4dcdc7e4ab3dd6765a97ff89c3b4c258117c79bf"
@@ -2813,6 +3127,14 @@ forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+form-data@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.11"
+
form-data@~2.1.1:
version "2.1.4"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
@@ -2821,6 +3143,14 @@ form-data@~2.1.1:
combined-stream "^1.0.5"
mime-types "^2.1.12"
+form-data@~2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
forwarded@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363"
@@ -2877,6 +3207,13 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
mkdirp ">=0.5 0"
rimraf "2"
+ftp@~0.3.10:
+ version "0.3.10"
+ resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d"
+ dependencies:
+ readable-stream "1.1.x"
+ xregexp "2.0.0"
+
function-bind@^1.0.2, function-bind@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
@@ -2924,6 +3261,17 @@ get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+get-uri@2:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59"
+ dependencies:
+ data-uri-to-buffer "1"
+ debug "2"
+ extend "3"
+ file-uri-to-path "1"
+ ftp "~0.3.10"
+ readable-stream "2"
+
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
@@ -2974,7 +3322,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@~7.1.2:
+glob@^7.1.0, glob@~7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@@ -3035,7 +3383,7 @@ got@^3.2.0:
read-all-stream "^3.0.0"
timed-out "^2.0.0"
-got@^7.0.0:
+got@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
dependencies:
@@ -3092,6 +3440,19 @@ har-schema@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+har-schema@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+
+har-validator@~2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
+ dependencies:
+ chalk "^1.1.1"
+ commander "^2.9.0"
+ is-my-json-valid "^2.12.4"
+ pinkie-promise "^2.0.0"
+
har-validator@~4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
@@ -3099,17 +3460,24 @@ har-validator@~4.2.1:
ajv "^4.9.1"
har-schema "^1.0.5"
+har-validator@~5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
+ dependencies:
+ ajv "^5.1.0"
+ har-schema "^2.0.0"
+
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
dependencies:
ansi-regex "^2.0.0"
-has-binary@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c"
+has-binary2@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98"
dependencies:
- isarray "0.0.1"
+ isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
@@ -3137,7 +3505,7 @@ has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
-has@^1.0.1, has@~1.0.1:
+has@^1.0.0, has@^1.0.1, has@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
dependencies:
@@ -3162,14 +3530,34 @@ hawk@~3.1.3:
hoek "2.x.x"
sntp "1.x.x"
+hawk@~6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
+ dependencies:
+ boom "4.x.x"
+ cryptiles "3.x.x"
+ hoek "4.x.x"
+ sntp "2.x.x"
+
he@^1.1.0, he@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
+hipchat-notifier@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e"
+ dependencies:
+ lodash "^4.0.0"
+ request "^2.0.0"
+
hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+hoek@4.x.x:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
+
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -3198,7 +3586,11 @@ html-entities@1.2.0, html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
-htmlparser2@^3.8.2:
+htmlescape@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
+
+htmlparser2@^3.8.2, htmlparser2@^3.9.0:
version "3.9.2"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
dependencies:
@@ -3213,6 +3605,15 @@ http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+http-errors@1.6.2, http-errors@~1.6.2:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
+ dependencies:
+ depd "1.1.1"
+ inherits "2.0.3"
+ setprototypeof "1.0.3"
+ statuses ">= 1.3.1 < 2"
+
http-errors@~1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257"
@@ -3222,14 +3623,13 @@ http-errors@~1.6.1:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
-http-errors@~1.6.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
+http-proxy-agent@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a"
dependencies:
- depd "1.1.1"
- inherits "2.0.3"
- setprototypeof "1.0.3"
- statuses ">= 1.3.1 < 2"
+ agent-base "2"
+ debug "2"
+ extend "3"
http-proxy-middleware@~0.17.4:
version "0.17.4"
@@ -3255,14 +3655,49 @@ http-signature@~1.1.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
+http-signature@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+httpntlm@1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2"
+ dependencies:
+ httpreq ">=0.4.22"
+ underscore "~1.7.0"
+
+httpreq@>=0.4.22:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-0.4.24.tgz#4335ffd82cd969668a39465c929ac61d6393627f"
+
https-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
+https-browserify@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+
+https-proxy-agent@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+
iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
+iconv-lite@0.4.19:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
+
icss-replace-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5"
@@ -3312,6 +3747,14 @@ infinity-agent@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216"
+inflection@~1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f"
+
+inflection@~1.3.0:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.3.8.tgz#cbd160da9f75b14c3cc63578d4f396784bf3014e"
+
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -3331,6 +3774,12 @@ ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+inline-source-map@~0.6.0:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5"
+ dependencies:
+ source-map "~0.5.3"
+
inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
@@ -3349,6 +3798,19 @@ inquirer@^0.12.0:
strip-ansi "^3.0.0"
through "^2.3.6"
+insert-module-globals@^7.0.0:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3"
+ dependencies:
+ JSONStream "^1.0.3"
+ combine-source-map "~0.7.1"
+ concat-stream "~1.5.1"
+ is-buffer "^1.1.0"
+ lexical-scope "^1.2.0"
+ process "~0.11.0"
+ through2 "^2.0.0"
+ xtend "^4.0.0"
+
internal-ip@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c"
@@ -3369,7 +3831,11 @@ invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
-ip@^1.1.0, ip@^1.1.5:
+ip@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590"
+
+ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -3402,6 +3868,10 @@ is-buffer@^1.0.2, is-buffer@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+is-buffer@^1.1.0:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+
is-builtin-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
@@ -3479,6 +3949,15 @@ is-my-json-valid@^2.10.0:
jsonpointer "^4.0.0"
xtend "^4.0.0"
+is-my-json-valid@^2.12.4:
+ version "2.17.1"
+ resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz#3da98914a70a22f0a8563ef1511a246c6fc55471"
+ dependencies:
+ generate-function "^2.0.0"
+ generate-object-property "^1.1.0"
+ jsonpointer "^4.0.0"
+ xtend "^4.0.0"
+
is-npm@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
@@ -3587,7 +4066,7 @@ is-windows@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c"
-isarray@0.0.1:
+isarray@0.0.1, isarray@~0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -3595,6 +4074,10 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+isarray@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
+
isbinaryfile@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
@@ -3613,33 +4096,33 @@ isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
-istanbul-api@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.1.tgz#d36e2f1560d1a43ce304c4ff7338182de61c8f73"
+istanbul-api@^1.1.14:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.2.1.tgz#0c60a0515eb11c7d65c6b50bba2c6e999acd8620"
dependencies:
async "^2.1.4"
fileset "^2.0.2"
- istanbul-lib-coverage "^1.0.0"
- istanbul-lib-hook "^1.0.0"
- istanbul-lib-instrument "^1.3.0"
- istanbul-lib-report "^1.0.0-alpha.3"
- istanbul-lib-source-maps "^1.1.0"
- istanbul-reports "^1.0.0"
+ istanbul-lib-coverage "^1.1.1"
+ istanbul-lib-hook "^1.1.0"
+ istanbul-lib-instrument "^1.9.1"
+ istanbul-lib-report "^1.1.2"
+ istanbul-lib-source-maps "^1.2.2"
+ istanbul-reports "^1.1.3"
js-yaml "^3.7.0"
mkdirp "^0.5.1"
once "^1.4.0"
-istanbul-lib-coverage@^1.0.0, istanbul-lib-coverage@^1.0.0-alpha, istanbul-lib-coverage@^1.0.0-alpha.0, istanbul-lib-coverage@^1.1.1:
+istanbul-lib-coverage@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
-istanbul-lib-hook@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0.tgz#fc5367ee27f59268e8f060b0c7aaf051d9c425c5"
+istanbul-lib-hook@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b"
dependencies:
append-transform "^0.4.0"
-istanbul-lib-instrument@^1.3.0, istanbul-lib-instrument@^1.7.5:
+istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e"
dependencies:
@@ -3651,29 +4134,28 @@ istanbul-lib-instrument@^1.3.0, istanbul-lib-instrument@^1.7.5:
istanbul-lib-coverage "^1.1.1"
semver "^5.3.0"
-istanbul-lib-report@^1.0.0-alpha.3:
- version "1.0.0-alpha.3"
- resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.0.0-alpha.3.tgz#32d5f6ec7f33ca3a602209e278b2e6ff143498af"
+istanbul-lib-report@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.2.tgz#922be27c13b9511b979bd1587359f69798c1d425"
dependencies:
- async "^1.4.2"
- istanbul-lib-coverage "^1.0.0-alpha"
+ istanbul-lib-coverage "^1.1.1"
mkdirp "^0.5.1"
path-parse "^1.0.5"
- rimraf "^2.4.3"
supports-color "^3.1.2"
-istanbul-lib-source-maps@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.1.0.tgz#9d429218f35b823560ea300a96ff0c3bbdab785f"
+istanbul-lib-source-maps@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.2.tgz#750578602435f28a0c04ee6d7d9e0f2960e62c1c"
dependencies:
- istanbul-lib-coverage "^1.0.0-alpha.0"
+ debug "^3.1.0"
+ istanbul-lib-coverage "^1.1.1"
mkdirp "^0.5.1"
- rimraf "^2.4.4"
+ rimraf "^2.6.1"
source-map "^0.5.3"
-istanbul-reports@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.1.tgz#9a17176bc4a6cbebdae52b2f15961d52fa623fbc"
+istanbul-reports@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.3.tgz#3b9e1e8defb6d18b1d425da8e8b32c5a163f2d10"
dependencies:
handlebars "^4.0.3"
@@ -3703,9 +4185,9 @@ isurl@^1.0.0-alpha5:
has-to-string-tag-x "^1.2.0"
is-object "^1.0.1"
-jasmine-core@^2.6.3:
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.3.tgz#45072950e4a42b1e322fe55c001100a465d77815"
+jasmine-core@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.0.tgz#bfbb56defcd30789adec5a3fbba8504233289c72"
jasmine-jquery@^2.1.1:
version "2.1.1"
@@ -3781,11 +4263,17 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
-json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
+json-stable-stringify@~0.0.0:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45"
+ dependencies:
+ jsonify "~0.0.0"
+
+json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
-json3@3.3.2, json3@^3.3.2:
+json3@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
@@ -3803,6 +4291,10 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+jsonparse@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
+
jsonpointer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
@@ -3830,28 +4322,31 @@ jszip@^3.1.3:
pako "~1.0.2"
readable-stream "~2.0.6"
-karma-chrome-launcher@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf"
+karma-chrome-launcher@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf"
dependencies:
fs-access "^1.0.0"
which "^1.2.1"
-karma-coverage-istanbul-reporter@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.0.tgz#5766263338adeb0026f7e4ac7a89a5f056c5642c"
+karma-coverage-istanbul-reporter@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.3.3.tgz#daf26051d5a0daa5838a4ce81aa4a41724bdf36b"
dependencies:
- istanbul-api "^1.1.1"
+ istanbul-api "^1.1.14"
+ minimatch "^3.0.4"
-karma-jasmine@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf"
+karma-jasmine@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.1.tgz#6fe840e75a11600c9d91e84b33c458e1c46a3529"
-karma-mocha-reporter@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.2.tgz#876de9a287244e54a608591732a98e66611f6abe"
+karma-mocha-reporter@^2.2.5:
+ version "2.2.5"
+ resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz#15120095e8ed819186e47a0b012f3cd741895560"
dependencies:
- chalk "1.1.3"
+ chalk "^2.1.0"
+ log-symbols "^2.1.0"
+ strip-ansi "^4.0.0"
karma-sourcemap-loader@^0.3.7:
version "0.3.7"
@@ -3859,22 +4354,23 @@ karma-sourcemap-loader@^0.3.7:
dependencies:
graceful-fs "^4.1.2"
-karma-webpack@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.4.tgz#3e2d4f48ba94a878e1c66bb8e1ae6128987a175b"
+karma-webpack@2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.7.tgz#dc3a492b478f10e8e3ccb9f58171b623f7070a1f"
dependencies:
async "~0.9.0"
loader-utils "^0.2.5"
lodash "^3.8.0"
- source-map "^0.1.41"
- webpack-dev-middleware "^1.0.11"
+ source-map "^0.5.6"
+ webpack-dev-middleware "^1.12.0"
-karma@^1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.0.tgz#6f7a1a406446fa2e187ec95398698f4cee476269"
+karma@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.0.tgz#a02698dd7f0f05ff5eb66ab8f65582490b512e58"
dependencies:
bluebird "^3.3.0"
body-parser "^1.16.1"
+ browserify "^14.5.0"
chokidar "^1.4.1"
colors "^1.1.0"
combine-lists "^1.0.0"
@@ -3887,8 +4383,8 @@ karma@^1.7.0:
graceful-fs "^4.1.2"
http-proxy "^1.13.0"
isbinaryfile "^3.0.0"
- lodash "^3.8.0"
- log4js "^0.6.31"
+ lodash "^4.17.4"
+ log4js "^2.3.9"
mime "^1.3.4"
minimatch "^3.0.2"
optimist "^0.6.1"
@@ -3896,9 +4392,9 @@ karma@^1.7.0:
range-parser "^1.2.0"
rimraf "^2.6.0"
safe-buffer "^5.0.1"
- socket.io "1.7.3"
- source-map "^0.5.3"
- tmp "0.0.31"
+ socket.io "2.0.4"
+ source-map "^0.6.1"
+ tmp "0.0.33"
useragent "^2.1.12"
kind-of@^3.0.2:
@@ -3913,6 +4409,14 @@ klaw@^1.0.0:
optionalDependencies:
graceful-fs "^4.1.9"
+labeled-stream-splicer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
+ dependencies:
+ inherits "^2.0.1"
+ isarray "~0.0.1"
+ stream-splicer "^2.0.0"
+
latest-version@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb"
@@ -3936,6 +4440,28 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lexical-scope@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4"
+ dependencies:
+ astw "^2.0.0"
+
+libbase64@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6"
+
+libmime@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.0.0.tgz#51a1a9e7448ecbd32cda54421675bb21bc093da6"
+ dependencies:
+ iconv-lite "0.4.15"
+ libbase64 "0.1.0"
+ libqp "1.1.0"
+
+libqp@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8"
+
lie@~3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
@@ -4054,6 +4580,10 @@ lodash.capitalize@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9"
+lodash.clonedeep@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+
lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
@@ -4069,6 +4599,10 @@ lodash.defaults@^3.1.2:
lodash.assign "^3.0.0"
lodash.restparam "^3.0.0"
+lodash.escaperegexp@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
+
lodash.get@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-3.7.0.tgz#3ce68ae2c91683b281cc5394128303cbf75e691f"
@@ -4103,6 +4637,14 @@ lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+lodash.memoize@~3.0.3:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
+
+lodash.mergewith@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
+
lodash.restparam@^3.0.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
@@ -4122,7 +4664,7 @@ lodash.words@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036"
-lodash@4.17.4, lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@4.17.4, lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -4130,12 +4672,37 @@ lodash@^3.8.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-log4js@^0.6.31:
- version "0.6.38"
- resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd"
+log-symbols@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6"
dependencies:
- readable-stream "~1.0.2"
- semver "~4.3.3"
+ chalk "^2.0.1"
+
+log4js@^2.3.9:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.4.1.tgz#b0c4e88133e0e3056afdc6f91f7f377576158778"
+ dependencies:
+ circular-json "^0.4.0"
+ date-format "^1.2.0"
+ debug "^3.1.0"
+ semver "^5.3.0"
+ streamroller "^0.7.0"
+ optionalDependencies:
+ axios "^0.15.3"
+ hipchat-notifier "^1.1.0"
+ loggly "^1.1.0"
+ mailgun-js "^0.7.0"
+ nodemailer "^2.5.0"
+ redis "^2.7.1"
+ slack-node "~0.2.0"
+
+loggly@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/loggly/-/loggly-1.1.1.tgz#0a0fc1d3fa3a5ec44fdc7b897beba2a4695cebee"
+ dependencies:
+ json-stringify-safe "5.0.x"
+ request "2.75.x"
+ timespan "2.3.x"
loglevel@^1.4.1:
version "1.4.1"
@@ -4173,10 +4740,35 @@ lru-cache@^4.0.1, lru-cache@^4.1.1:
pseudomap "^1.0.2"
yallist "^2.1.2"
+lru-cache@~2.6.5:
+ version "2.6.5"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
+
macaddress@^0.2.8:
version "0.2.8"
resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
+mailcomposer@4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4"
+ dependencies:
+ buildmail "4.0.1"
+ libmime "3.0.0"
+
+mailgun-js@^0.7.0:
+ version "0.7.15"
+ resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb"
+ dependencies:
+ async "~2.1.2"
+ debug "~2.2.0"
+ form-data "~2.1.1"
+ inflection "~1.10.0"
+ is-stream "^1.1.0"
+ path-proxy "~1.0.0"
+ proxy-agent "~2.0.0"
+ q "~1.4.0"
+ tsscmp "~1.0.0"
+
make-dir@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978"
@@ -4276,6 +4868,16 @@ mime-db@~1.27.0:
version "1.27.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+mime-db@~1.30.0:
+ version "1.30.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
+
+mime-types@^2.1.11, mime-types@~2.1.17:
+ version "2.1.17"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
+ dependencies:
+ mime-db "~1.30.0"
+
mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7:
version "2.1.15"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
@@ -4286,6 +4888,10 @@ mime@1.3.4, mime@1.3.x, mime@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
+mime@^1.5.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+
mimic-fn@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
@@ -4314,7 +4920,7 @@ minimist@0.0.8, minimist@~0.0.1:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
+minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -4324,6 +4930,26 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd
dependencies:
minimist "0.0.8"
+module-deps@^4.0.8:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd"
+ dependencies:
+ JSONStream "^1.0.3"
+ browser-resolve "^1.7.0"
+ cached-path-relative "^1.0.0"
+ concat-stream "~1.5.0"
+ defined "^1.0.0"
+ detective "^4.0.0"
+ duplexer2 "^0.1.2"
+ inherits "^2.0.1"
+ parents "^1.0.0"
+ readable-stream "^2.0.2"
+ resolve "^1.1.3"
+ stream-combiner2 "^1.1.1"
+ subarg "^1.0.0"
+ through2 "^2.0.0"
+ xtend "^4.0.0"
+
moment@2.x, moment@^2.18.1:
version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
@@ -4340,10 +4966,6 @@ ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
-ms@0.7.2:
- version "0.7.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
-
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -4385,6 +5007,10 @@ nested-error-stacks@^1.0.0:
dependencies:
inherits "~2.0.1"
+netmask@~1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
+
node-dir@^0.1.10:
version "0.1.17"
resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
@@ -4466,6 +5092,59 @@ node-pre-gyp@^0.6.36:
tar "^2.2.1"
tar-pack "^3.4.0"
+node-uuid@~1.4.7:
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
+
+nodemailer-direct-transport@3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86"
+ dependencies:
+ nodemailer-shared "1.1.0"
+ smtp-connection "2.12.0"
+
+nodemailer-fetch@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4"
+
+nodemailer-shared@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0"
+ dependencies:
+ nodemailer-fetch "1.6.0"
+
+nodemailer-smtp-pool@2.8.2:
+ version "2.8.2"
+ resolved "https://registry.yarnpkg.com/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz#2eb94d6cf85780b1b4725ce853b9cbd5e8da8c72"
+ dependencies:
+ nodemailer-shared "1.1.0"
+ nodemailer-wellknown "0.1.10"
+ smtp-connection "2.12.0"
+
+nodemailer-smtp-transport@2.7.2:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz#03d71c76314f14ac7dbc7bf033a6a6d16d67fb77"
+ dependencies:
+ nodemailer-shared "1.1.0"
+ nodemailer-wellknown "0.1.10"
+ smtp-connection "2.12.0"
+
+nodemailer-wellknown@0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz#586db8101db30cb4438eb546737a41aad0cf13d5"
+
+nodemailer@^2.5.0:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-2.7.2.tgz#f242e649aeeae39b6c7ed740ef7b061c404d30f9"
+ dependencies:
+ libmime "3.0.0"
+ mailcomposer "4.0.1"
+ nodemailer-direct-transport "3.3.2"
+ nodemailer-shared "1.1.0"
+ nodemailer-smtp-pool "2.8.2"
+ nodemailer-smtp-transport "2.7.2"
+ socks "1.1.9"
+
nodemon@^1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.11.0.tgz#226c562bd2a7b13d3d7518b49ad4828a3623d06c"
@@ -4555,14 +5234,10 @@ number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-oauth-sign@~0.8.1:
+oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
-object-assign@4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
-
object-assign@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
@@ -4643,10 +5318,6 @@ optionator@^0.8.1, optionator@^0.8.2:
type-check "~0.3.2"
wordwrap "~1.0.0"
-options@>=0.0.5:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f"
-
original@>=0.0.5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b"
@@ -4657,6 +5328,10 @@ os-browserify@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
+os-browserify@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+
os-homedir@^1.0.0, os-homedir@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
@@ -4675,7 +5350,7 @@ os-locale@^2.0.0:
lcid "^1.0.0"
mem "^1.1.0"
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@@ -4714,6 +5389,30 @@ p-timeout@^1.1.1:
dependencies:
p-finally "^1.0.0"
+pac-proxy-agent@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+ get-uri "2"
+ http-proxy-agent "1"
+ https-proxy-agent "1"
+ pac-resolver "~2.0.0"
+ raw-body "2"
+ socks-proxy-agent "2"
+
+pac-resolver@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd"
+ dependencies:
+ co "~3.0.6"
+ degenerator "~1.0.2"
+ ip "1.0.1"
+ netmask "~1.0.4"
+ thunkify "~2.1.1"
+
package-json@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0"
@@ -4729,6 +5428,16 @@ pako@~1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.5.tgz#d2205dfe5b9da8af797e7c163db4d1f84e4600bc"
+pako@~1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
+
+parents@^1.0.0, parents@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751"
+ dependencies:
+ path-platform "~0.11.15"
+
parse-asn1@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23"
@@ -4754,12 +5463,6 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
-parsejson@0.0.3:
- version "0.0.3"
- resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab"
- dependencies:
- better-assert "~1.0.0"
-
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
@@ -4776,7 +5479,7 @@ parseurl@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
-path-browserify@0.0.0:
+path-browserify@0.0.0, path-browserify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
@@ -4806,6 +5509,16 @@ path-parse@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+path-platform@~0.11.15:
+ version "0.11.15"
+ resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2"
+
+path-proxy@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e"
+ dependencies:
+ inflection "~1.3.0"
+
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
@@ -4840,6 +5553,10 @@ performance-now@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+
pify@^2.0.0, pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -5155,6 +5872,14 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0
source-map "^0.5.6"
supports-color "^3.2.3"
+postcss@^6.0.14:
+ version "6.0.15"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.15.tgz#f460cd6269fede0d1bf6defff0b934a9845d974d"
+ dependencies:
+ chalk "^2.3.0"
+ source-map "^0.6.1"
+ supports-color "^5.1.0"
+
postcss@^6.0.8:
version "6.0.14"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885"
@@ -5212,6 +5937,19 @@ proxy-addr@~1.1.5:
forwarded "~0.1.0"
ipaddr.js "1.4.0"
+proxy-agent@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+ http-proxy-agent "1"
+ https-proxy-agent "1"
+ lru-cache "~2.6.5"
+ pac-proxy-agent "1"
+ socks-proxy-agent "2"
+
prr@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
@@ -5240,7 +5978,7 @@ punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
-punycode@^1.2.4, punycode@^1.4.1:
+punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
@@ -5248,6 +5986,10 @@ q@^1.1.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
+q@~1.4.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
+
qjobs@^1.1.4:
version "1.1.5"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
@@ -5260,6 +6002,14 @@ qs@6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49"
+qs@~6.2.0:
+ version "6.2.3"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
+
+qs@~6.5.1:
+ version "6.5.1"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
+
query-string@^4.1.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.2.tgz#ec0fd765f58a50031a3968c2431386f8947a5cdd"
@@ -5267,7 +6017,7 @@ query-string@^4.1.0:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
-querystring-es3@^0.2.0:
+querystring-es3@^0.2.0, querystring-es3@~0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -5294,6 +6044,19 @@ randombytes@^2.0.0, randombytes@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec"
+randombytes@^2.0.5:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
+ dependencies:
+ safe-buffer "^5.1.0"
+
+randomfill@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62"
+ dependencies:
+ randombytes "^2.0.5"
+ safe-buffer "^5.1.0"
+
range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
@@ -5304,11 +6067,18 @@ raphael@^2.2.7:
dependencies:
eve-raphael "0.5.0"
-raven-js@^3.14.0:
- version "3.14.0"
- resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.14.0.tgz#94dda81d975fdc4a42f193db437cf70021d654e0"
+raven-js@^3.22.1:
+ version "3.22.1"
+ resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
+
+raw-body@2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
dependencies:
- json-stringify-safe "^5.0.1"
+ bytes "3.0.0"
+ http-errors "1.6.2"
+ iconv-lite "0.4.19"
+ unpipe "1.0.0"
raw-body@~2.2.0:
version "2.2.0"
@@ -5353,6 +6123,12 @@ read-all-stream@^3.0.0:
pinkie-promise "^2.0.0"
readable-stream "^2.0.0"
+read-only-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0"
+ dependencies:
+ readable-stream "^2.0.2"
+
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -5383,7 +6159,16 @@ read-pkg@^2.0.0:
normalize-package-data "^2.3.2"
path-type "^2.0.0"
-readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.0, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.9:
+readable-stream@1.1.x:
+ version "1.1.14"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "0.0.1"
+ string_decoder "~0.10.x"
+
+readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.0, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.0:
version "2.3.3"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
dependencies:
@@ -5395,7 +6180,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.0, readable
string_decoder "~1.0.3"
util-deprecate "~1.0.1"
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
dependencies:
@@ -5406,15 +6191,6 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
-readable-stream@~1.0.2:
- version "1.0.34"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.1"
- isarray "0.0.1"
- string_decoder "~0.10.x"
-
readdirp@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
@@ -5451,6 +6227,22 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
+redis-commands@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.1.tgz#81d826f45fa9c8b2011f4cd7a0fe597d241d442b"
+
+redis-parser@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b"
+
+redis@^2.7.1:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02"
+ dependencies:
+ double-ended-queue "^2.1.0-0"
+ redis-commands "^1.2.0"
+ redis-parser "^2.6.0"
+
reduce-css-calc@^1.2.6:
version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
@@ -5548,6 +6340,59 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
+request@2.75.x:
+ version "2.75.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93"
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ bl "~1.1.2"
+ caseless "~0.11.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.0.0"
+ har-validator "~2.0.6"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ node-uuid "~1.4.7"
+ oauth-sign "~0.8.1"
+ qs "~6.2.0"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "~0.4.1"
+
+request@^2.0.0, request@^2.74.0:
+ version "2.83.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.6.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.1"
+ forever-agent "~0.6.1"
+ form-data "~2.3.1"
+ har-validator "~5.0.3"
+ hawk "~6.0.2"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.17"
+ oauth-sign "~0.8.2"
+ performance-now "^2.1.0"
+ qs "~6.5.1"
+ safe-buffer "^5.1.1"
+ stringstream "~0.0.5"
+ tough-cookie "~2.3.3"
+ tunnel-agent "^0.6.0"
+ uuid "^3.1.0"
+
request@^2.81.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
@@ -5575,6 +6420,15 @@ request@^2.81.0:
tunnel-agent "^0.6.0"
uuid "^3.0.0"
+requestretry@^1.2.2:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.12.2.tgz#13ce38a4ce4e809f3c9ec6d4ca3b7b9ba4acf26c"
+ dependencies:
+ extend "^3.0.0"
+ lodash "^4.15.0"
+ request "^2.74.0"
+ when "^3.7.7"
+
require-all@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/require-all/-/require-all-2.2.0.tgz#b4420c233ac0282d0ff49b277fb880a8b5de0894"
@@ -5606,11 +6460,11 @@ resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
-resolve@1.1.x:
+resolve@1.1.7, resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
-resolve@^1.1.6, resolve@^1.2.0, resolve@^1.4.0:
+resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.2.0, resolve@^1.4.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
dependencies:
@@ -5641,7 +6495,7 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
-rimraf@2, rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1:
+rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
dependencies:
@@ -5661,7 +6515,7 @@ rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
-safe-buffer@5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+safe-buffer@5.1.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
@@ -5669,6 +6523,18 @@ safe-buffer@^5.0.1, safe-buffer@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+sanitize-html@^1.16.1:
+ version "1.16.3"
+ resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.16.3.tgz#96c1b44a36ff7312e1c22a14b05274370ac8bd56"
+ dependencies:
+ htmlparser2 "^3.9.0"
+ lodash.clonedeep "^4.5.0"
+ lodash.escaperegexp "^4.1.2"
+ lodash.mergewith "^4.6.0"
+ postcss "^6.0.14"
+ srcset "^1.0.0"
+ xtend "^4.0.0"
+
sax@~1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
@@ -5707,9 +6573,9 @@ semver-diff@^2.0.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
-semver@~4.3.3:
- version "4.3.6"
- resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
+semver@~5.0.1:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
send@0.15.4:
version "0.15.4"
@@ -5772,6 +6638,20 @@ sha.js@^2.3.6:
dependencies:
inherits "^2.0.1"
+sha.js@~2.4.4:
+ version "2.4.9"
+ resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d"
+ dependencies:
+ inherits "^2.0.1"
+ safe-buffer "^5.0.1"
+
+shasum@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f"
+ dependencies:
+ json-stable-stringify "~0.0.0"
+ sha.js "~2.4.4"
+
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -5782,6 +6662,15 @@ shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+shell-quote@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
+ dependencies:
+ array-filter "~0.0.0"
+ array-map "~0.0.0"
+ array-reduce "~0.0.0"
+ jsonify "~0.0.0"
+
shelljs@^0.7.5:
version "0.7.8"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3"
@@ -5794,6 +6683,12 @@ signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+slack-node@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/slack-node/-/slack-node-0.2.0.tgz#de4b8dddaa8b793f61dbd2938104fdabf37dfa30"
+ dependencies:
+ requestretry "^1.2.2"
+
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -5806,55 +6701,69 @@ slide@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
+smart-buffer@^1.0.13, smart-buffer@^1.0.4:
+ version "1.1.15"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
+
+smtp-connection@2.12.0:
+ version "2.12.0"
+ resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1"
+ dependencies:
+ httpntlm "1.6.1"
+ nodemailer-shared "1.1.0"
+
sntp@1.x.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
dependencies:
hoek "2.x.x"
-socket.io-adapter@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b"
+sntp@2.x.x:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
dependencies:
- debug "2.3.3"
- socket.io-parser "2.3.1"
+ hoek "4.x.x"
+
+socket.io-adapter@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b"
-socket.io-client@1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377"
+socket.io-client@2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.4.tgz#0918a552406dc5e540b380dcd97afc4a64332f8e"
dependencies:
backo2 "1.0.2"
+ base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
- debug "2.3.3"
- engine.io-client "1.8.3"
- has-binary "0.1.7"
+ debug "~2.6.4"
+ engine.io-client "~3.1.0"
+ has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
+ parseqs "0.0.5"
parseuri "0.0.5"
- socket.io-parser "2.3.1"
+ socket.io-parser "~3.1.1"
to-array "0.1.4"
-socket.io-parser@2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0"
+socket.io-parser@~3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.2.tgz#dbc2282151fc4faebbe40aeedc0772eba619f7f2"
dependencies:
- component-emitter "1.1.2"
- debug "2.2.0"
- isarray "0.0.1"
- json3 "3.3.2"
+ component-emitter "1.2.1"
+ debug "~2.6.4"
+ has-binary2 "~1.0.2"
+ isarray "2.0.1"
-socket.io@1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b"
+socket.io@2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.4.tgz#c1a4590ceff87ecf13c72652f046f716b29e6014"
dependencies:
- debug "2.3.3"
- engine.io "1.8.3"
- has-binary "0.1.7"
- object-assign "4.1.0"
- socket.io-adapter "0.5.0"
- socket.io-client "1.7.3"
- socket.io-parser "2.3.1"
+ debug "~2.6.6"
+ engine.io "~3.1.0"
+ socket.io-adapter "~1.1.0"
+ socket.io-client "2.0.4"
+ socket.io-parser "~3.1.1"
sockjs-client@1.0.1:
version "1.0.1"
@@ -5885,6 +6794,28 @@ sockjs@0.3.18:
faye-websocket "^0.10.0"
uuid "^2.0.2"
+socks-proxy-agent@2:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3"
+ dependencies:
+ agent-base "2"
+ extend "3"
+ socks "~1.1.5"
+
+socks@1.1.9:
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691"
+ dependencies:
+ ip "^1.1.2"
+ smart-buffer "^1.0.4"
+
+socks@~1.1.5:
+ version "1.1.10"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
+ dependencies:
+ ip "^1.1.4"
+ smart-buffer "^1.0.13"
+
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
@@ -5909,12 +6840,6 @@ source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, sourc
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
-source-map@^0.1.41:
- version "0.1.43"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
- dependencies:
- amdefine ">=0.0.4"
-
source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
@@ -5931,6 +6856,10 @@ source-map@~0.2.0:
dependencies:
amdefine ">=0.0.4"
+source-map@~0.5.6:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+
spdx-correct@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@@ -5982,6 +6911,13 @@ sql.js@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-0.4.0.tgz#23be9635520eb0ff43a741e7e830397266e88445"
+srcset@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef"
+ dependencies:
+ array-uniq "^1.0.2"
+ number-is-nan "^1.0.0"
+
sshpk@^1.7.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
@@ -6000,19 +6936,36 @@ sshpk@^1.7.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
-stream-browserify@^2.0.1:
+stream-browserify@^2.0.0, stream-browserify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
dependencies:
inherits "~2.0.1"
readable-stream "^2.0.2"
+stream-combiner2@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe"
+ dependencies:
+ duplexer2 "~0.1.0"
+ readable-stream "^2.0.2"
+
stream-combiner@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
dependencies:
duplexer "~0.1.1"
+stream-http@^2.0.0:
+ version "2.7.2"
+ resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad"
+ dependencies:
+ builtin-status-codes "^3.0.0"
+ inherits "^2.0.1"
+ readable-stream "^2.2.6"
+ to-arraybuffer "^1.0.0"
+ xtend "^4.0.0"
+
stream-http@^2.3.1:
version "2.6.3"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3"
@@ -6027,6 +6980,22 @@ stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
+stream-splicer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83"
+ dependencies:
+ inherits "^2.0.1"
+ readable-stream "^2.0.2"
+
+streamroller@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
+ dependencies:
+ date-format "^1.2.0"
+ debug "^3.1.0"
+ mkdirp "^0.5.1"
+ readable-stream "^2.3.0"
+
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@@ -6064,13 +7033,13 @@ string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
-string_decoder@~1.0.3:
+string_decoder@~1.0.0, string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies:
safe-buffer "~5.1.0"
-stringstream@~0.0.4:
+stringstream@~0.0.4, stringstream@~0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
@@ -6080,6 +7049,12 @@ strip-ansi@3.0.1, strip-ansi@^3.0.0, strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ dependencies:
+ ansi-regex "^3.0.0"
+
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -6104,6 +7079,12 @@ strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+subarg@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
+ dependencies:
+ minimist "^1.1.0"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -6126,6 +7107,12 @@ supports-color@^4.2.1:
dependencies:
has-flag "^2.0.0"
+supports-color@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5"
+ dependencies:
+ has-flag "^2.0.0"
+
svg4everybody@2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d"
@@ -6142,6 +7129,12 @@ svgo@^0.7.0:
sax "~1.2.1"
whet.extend "~0.9.9"
+syntax-error@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062bb96ca1"
+ dependencies:
+ acorn "^4.0.3"
+
table@^3.7.8:
version "3.8.3"
resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
@@ -6226,14 +7219,29 @@ three@^0.84.0:
version "0.84.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
-through@2, through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4, through@~2.3.8:
+through2@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
+ dependencies:
+ readable-stream "^2.1.5"
+ xtend "~4.0.1"
+
+through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4, through@~2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+thunkify@~2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
+
thunky@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e"
+time-stamp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357"
+
timeago.js@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-3.0.2.tgz#32a67e7c0d887ea42ca588d3aae26f77de5e76cc"
@@ -6248,7 +7256,7 @@ timed-out@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
-timers-browserify@^1.4.2:
+timers-browserify@^1.0.1, timers-browserify@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
dependencies:
@@ -6260,11 +7268,21 @@ timers-browserify@^2.0.2:
dependencies:
setimmediate "^1.0.4"
+timespan@2.3.x:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929"
+
tiny-emitter@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
-tmp@0.0.31, tmp@0.0.x:
+tmp@0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+tmp@0.0.x:
version "0.0.31"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
dependencies:
@@ -6298,6 +7316,12 @@ tough-cookie@~2.3.0:
dependencies:
punycode "^1.4.1"
+tough-cookie@~2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
+ dependencies:
+ punycode "^1.4.1"
+
traverse@0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
@@ -6314,7 +7338,11 @@ tryit@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
-tty-browserify@0.0.0:
+tsscmp@~1.0.0:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97"
+
+tty-browserify@0.0.0, tty-browserify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@@ -6324,6 +7352,10 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
+tunnel-agent@~0.4.1:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
+
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
@@ -6341,7 +7373,7 @@ type-is@~1.6.15:
media-typer "0.3.0"
mime-types "~2.1.15"
-typedarray@^0.0.6:
+typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -6370,14 +7402,14 @@ uid-number@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
-ultron@1.0.x:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
-
ultron@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864"
+umd@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e"
+
unc-path-regex@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
@@ -6390,6 +7422,10 @@ underscore@^1.8.3:
version "1.8.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
+underscore@~1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
+
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
@@ -6458,7 +7494,7 @@ url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
-url@^0.11.0:
+url@^0.11.0, url@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
dependencies:
@@ -6482,7 +7518,7 @@ util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
-util@0.10.3, util@^0.10.3:
+util@0.10.3, util@^0.10.3, util@~0.10.1:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
@@ -6496,10 +7532,14 @@ uuid@^2.0.1, uuid@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
-uuid@^3.0.0:
+uuid@^3.0.0, uuid@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
+uws@~0.14.4:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc"
+
validate-npm-package-license@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
@@ -6527,7 +7567,7 @@ 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:
+vm-browserify@0.0.4, vm-browserify@~0.0.1:
version "0.0.4"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
dependencies:
@@ -6552,9 +7592,9 @@ vue-hot-reload-api@^2.2.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.4.tgz#683bd1d026c0d3b3c937d5875679e9a87ec6cd8f"
-vue-loader@^13.5.0:
- version "13.5.0"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.5.0.tgz#52f7b3790a267eff80012b77ea187a54586dd5d4"
+vue-loader@^13.7.0:
+ version "13.7.0"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.7.0.tgz#4d6a35b169c2a0a488842fb95c85052105fa9729"
dependencies:
consolidate "^0.14.0"
hash-sum "^1.0.2"
@@ -6570,11 +7610,11 @@ vue-loader@^13.5.0:
vue-style-loader "^3.0.0"
vue-template-es2015-compiler "^1.6.0"
-vue-resource@^1.3.4:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.4.tgz#9fc0bdf6a2f5cab430129fc99d347b3deae7b099"
+vue-resource@^1.3.5:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.5.tgz#021d8713e9d86a77e83169dfdd8eab6047369a71"
dependencies:
- got "^7.0.0"
+ got "^7.1.0"
vue-router@^3.0.1:
version "3.0.1"
@@ -6587,9 +7627,9 @@ vue-style-loader@^3.0.0:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
-vue-template-compiler@^2.5.8:
- version "2.5.8"
- resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.8.tgz#826ae77e1d5faa7fa5fca554f33872dde38de674"
+vue-template-compiler@^2.5.13:
+ version "2.5.13"
+ resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.13.tgz#12a2aa0ecd6158ac5e5f14d294b0993f399c3d38"
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
@@ -6598,9 +7638,9 @@ vue-template-es2015-compiler@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
-vue@^2.5.8:
- version "2.5.8"
- resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.8.tgz#f855c1c27255184a82225f4bef225473e8faf15b"
+vue@^2.5.13:
+ version "2.5.13"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.13.tgz#95bd31e20efcf7a7f39239c9aa6787ce8cf578e1"
vuex@^3.0.1:
version "3.0.1"
@@ -6636,7 +7676,7 @@ webpack-bundle-analyzer@^2.8.2:
opener "^1.4.3"
ws "^2.3.1"
-webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.11.0:
+webpack-dev-middleware@^1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz#09691d0973a30ad1f82ac73a12e2087f0a4754f9"
dependencies:
@@ -6645,6 +7685,16 @@ webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.11.0:
path-is-absolute "^1.0.0"
range-parser "^1.0.3"
+webpack-dev-middleware@^1.12.0:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
+ dependencies:
+ memory-fs "~0.4.1"
+ mime "^1.5.0"
+ path-is-absolute "^1.0.0"
+ range-parser "^1.0.3"
+ time-stamp "^2.0.0"
+
webpack-dev-server@^2.6.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.7.1.tgz#21580f5a08cd065c71144cf6f61c345bca59a8b8"
@@ -6721,6 +7771,10 @@ websocket-extensions@>=0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7"
+when@^3.7.7:
+ version "3.7.8"
+ resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82"
+
whet.extend@~0.9.9:
version "0.9.9"
resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
@@ -6793,13 +7847,6 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
-ws@1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f"
- dependencies:
- options ">=0.0.5"
- ultron "1.0.x"
-
ws@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-2.3.1.tgz#6b94b3e447cb6a363f785eaf94af6359e8e81c80"
@@ -6807,9 +7854,13 @@ ws@^2.3.1:
safe-buffer "~5.0.1"
ultron "~1.1.0"
-wtf-8@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a"
+ws@~3.3.1:
+ version "3.3.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
+ dependencies:
+ async-limiter "~1.0.0"
+ safe-buffer "~5.1.0"
+ ultron "~1.1.0"
xdg-basedir@^2.0.0:
version "2.0.0"
@@ -6817,11 +7868,15 @@ xdg-basedir@^2.0.0:
dependencies:
os-homedir "^1.0.0"
-xmlhttprequest-ssl@1.5.3:
- version "1.5.3"
- resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d"
+xmlhttprequest-ssl@~1.5.4:
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
+
+xregexp@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
-xtend@^4.0.0:
+xtend@^4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"