summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.yml2
-rw-r--r--CHANGELOG.md22
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--Gemfile.rails5.lock4
-rw-r--r--PROCESS.md19
-rw-r--r--app/assets/javascripts/ajax_loading_spinner.js2
-rw-r--r--app/assets/javascripts/behaviors/copy_to_clipboard.js2
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js4
-rw-r--r--app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js2
-rw-r--r--app/assets/javascripts/blob/balsamiq_viewer.js2
-rw-r--r--app/assets/javascripts/blob/stl_viewer.js2
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.vue2
-rw-r--r--app/assets/javascripts/boards/index.js4
-rw-r--r--app/assets/javascripts/boards/stores/modal_store.js2
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js2
-rw-r--r--app/assets/javascripts/commit/image_file.js4
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js2
-rw-r--r--app/assets/javascripts/diff_notes/components/diff_note_avatars.js2
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js2
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js2
-rw-r--r--app/assets/javascripts/diffs/components/app.vue16
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue46
-rw-r--r--app/assets/javascripts/diffs/components/diff_gutter_avatars.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_gutter_content.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue23
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue2
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue2
-rw-r--r--app/assets/javascripts/diffs/constants.js1
-rw-r--r--app/assets/javascripts/diffs/index.js2
-rw-r--r--app/assets/javascripts/diffs/store/actions.js7
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js1
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js2
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js7
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue2
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js6
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js2
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js4
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js4
-rw-r--r--app/assets/javascripts/filtered_search/recent_searches_root.js2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js4
-rw-r--r--app/assets/javascripts/gl_dropdown.js11
-rw-r--r--app/assets/javascripts/groups/index.js4
-rw-r--r--app/assets/javascripts/ide/components/file_finder/item.vue2
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue1
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue39
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff_worker.js2
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js2
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js13
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/ide/stores/mutations/tree.js5
-rw-r--r--app/assets/javascripts/image_diff/helpers/dom_helper.js3
-rw-r--r--app/assets/javascripts/image_diff/helpers/utils_helper.js3
-rw-r--r--app/assets/javascripts/issuable/auto_width_dropdown_select.js2
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js2
-rw-r--r--app/assets/javascripts/labels_select.js9
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js26
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js2
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js4
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js4
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js4
-rw-r--r--app/assets/javascripts/network/branch_graph.js49
-rw-r--r--app/assets/javascripts/new_branch_form.js2
-rw-r--r--app/assets/javascripts/notes.js2
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue2
-rw-r--r--app/assets/javascripts/notes/services/notes_service.js2
-rw-r--r--app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue2
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js9
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js2
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js2
-rw-r--r--app/assets/javascripts/pipelines/services/pipelines_service.js2
-rw-r--r--app/assets/javascripts/preview_markdown.js2
-rw-r--r--app/assets/javascripts/profile/gl_crop.js14
-rw-r--r--app/assets/javascripts/project_find_file.js2
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue2
-rw-r--r--app/assets/javascripts/projects_dropdown/index.js2
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js34
-rw-r--r--app/assets/javascripts/registry/index.js2
-rw-r--r--app/assets/javascripts/registry/stores/actions.js2
-rw-r--r--app/assets/javascripts/search_autocomplete.js6
-rw-r--r--app/assets/javascripts/smart_interval.js9
-rw-r--r--app/assets/javascripts/test_utils/simulate_drag.js4
-rw-r--r--app/assets/javascripts/users_select.js5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/table_pagination.vue2
-rw-r--r--app/assets/stylesheets/bootstrap.scss37
-rw-r--r--app/assets/stylesheets/framework.scss3
-rw-r--r--app/assets/stylesheets/framework/header.scss4
-rw-r--r--app/assets/stylesheets/pages/diff.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss1
-rw-r--r--app/controllers/health_controller.rb1
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/models/application_setting.rb13
-rw-r--r--app/services/metrics_service.rb3
-rw-r--r--app/views/admin/application_settings/show.html.haml3
-rw-r--r--app/views/admin/labels/_form.html.haml10
-rw-r--r--app/views/admin/labels/_label.html.haml4
-rw-r--r--app/views/admin/labels/edit.html.haml8
-rw-r--r--app/views/admin/labels/index.html.haml8
-rw-r--r--app/views/admin/labels/new.html.haml4
-rw-r--r--app/views/groups/new.html.haml46
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml2
-rw-r--r--app/views/projects/clusters/_integration_form.html.haml35
-rw-r--r--app/views/projects/deployments/_commit.html.haml2
-rw-r--r--app/views/projects/deployments/_deployment.html.haml8
-rw-r--r--app/views/projects/deployments/_rollback.haml4
-rw-r--r--app/views/projects/merge_requests/show.html.haml3
-rw-r--r--app/views/shared/tokens/_scopes_form.html.haml1
-rw-r--r--app/workers/admin_email_worker.rb2
-rw-r--r--app/workers/archive_trace_worker.rb2
-rw-r--r--app/workers/authorized_projects_worker.rb2
-rw-r--r--app/workers/background_migration_worker.rb2
-rw-r--r--app/workers/build_coverage_worker.rb2
-rw-r--r--app/workers/build_finished_worker.rb2
-rw-r--r--app/workers/build_hooks_worker.rb2
-rw-r--r--app/workers/build_queue_worker.rb2
-rw-r--r--app/workers/build_success_worker.rb2
-rw-r--r--app/workers/build_trace_sections_worker.rb2
-rw-r--r--app/workers/ci/archive_traces_cron_worker.rb2
-rw-r--r--app/workers/ci/build_trace_chunk_flush_worker.rb2
-rw-r--r--app/workers/cluster_install_app_worker.rb2
-rw-r--r--app/workers/cluster_provision_worker.rb2
-rw-r--r--app/workers/cluster_wait_for_app_installation_worker.rb2
-rw-r--r--app/workers/cluster_wait_for_ingress_ip_address_worker.rb2
-rw-r--r--app/workers/concerns/application_worker.rb2
-rw-r--r--app/workers/concerns/cluster_applications.rb2
-rw-r--r--app/workers/concerns/cluster_queue.rb2
-rw-r--r--app/workers/concerns/cronjob_queue.rb2
-rw-r--r--app/workers/concerns/exception_backtrace.rb2
-rw-r--r--app/workers/concerns/gitlab/github_import/queue.rb2
-rw-r--r--app/workers/concerns/mail_scheduler_queue.rb2
-rw-r--r--app/workers/concerns/new_issuable.rb2
-rw-r--r--app/workers/concerns/object_storage_queue.rb2
-rw-r--r--app/workers/concerns/pipeline_background_queue.rb2
-rw-r--r--app/workers/concerns/pipeline_queue.rb2
-rw-r--r--app/workers/concerns/project_import_options.rb2
-rw-r--r--app/workers/concerns/project_start_import.rb2
-rw-r--r--app/workers/concerns/repository_check_queue.rb2
-rw-r--r--app/workers/concerns/waitable_worker.rb2
-rw-r--r--app/workers/create_gpg_signature_worker.rb2
-rw-r--r--app/workers/create_note_diff_file_worker.rb2
-rw-r--r--app/workers/create_pipeline_worker.rb2
-rw-r--r--app/workers/delete_merged_branches_worker.rb2
-rw-r--r--app/workers/delete_user_worker.rb2
-rw-r--r--app/workers/email_receiver_worker.rb2
-rw-r--r--app/workers/emails_on_push_worker.rb2
-rw-r--r--app/workers/expire_build_artifacts_worker.rb2
-rw-r--r--app/workers/expire_build_instance_artifacts_worker.rb2
-rw-r--r--app/workers/expire_job_cache_worker.rb2
-rw-r--r--app/workers/expire_pipeline_cache_worker.rb2
-rw-r--r--app/workers/git_garbage_collect_worker.rb2
-rw-r--r--app/workers/gitlab_shell_worker.rb2
-rw-r--r--app/workers/gitlab_usage_ping_worker.rb2
-rw-r--r--app/workers/group_destroy_worker.rb2
-rw-r--r--app/workers/import_export_project_cleanup_worker.rb2
-rw-r--r--app/workers/invalid_gpg_signature_update_worker.rb2
-rw-r--r--app/workers/irker_worker.rb17
-rw-r--r--app/workers/issue_due_scheduler_worker.rb2
-rw-r--r--app/workers/mail_scheduler/issue_due_worker.rb2
-rw-r--r--app/workers/mail_scheduler/notification_service_worker.rb2
-rw-r--r--app/workers/merge_worker.rb2
-rw-r--r--app/workers/namespaceless_project_destroy_worker.rb2
-rw-r--r--app/workers/new_issue_worker.rb2
-rw-r--r--app/workers/new_merge_request_worker.rb2
-rw-r--r--app/workers/new_note_worker.rb2
-rw-r--r--app/workers/object_storage/background_move_worker.rb2
-rw-r--r--app/workers/object_storage_upload_worker.rb2
-rw-r--r--app/workers/pages_domain_verification_cron_worker.rb2
-rw-r--r--app/workers/pages_domain_verification_worker.rb2
-rw-r--r--app/workers/pages_worker.rb2
-rw-r--r--app/workers/pipeline_hooks_worker.rb2
-rw-r--r--app/workers/pipeline_metrics_worker.rb2
-rw-r--r--app/workers/pipeline_notification_worker.rb2
-rw-r--r--app/workers/pipeline_process_worker.rb2
-rw-r--r--app/workers/pipeline_schedule_worker.rb2
-rw-r--r--app/workers/pipeline_success_worker.rb2
-rw-r--r--app/workers/pipeline_update_worker.rb2
-rw-r--r--app/workers/plugin_worker.rb2
-rw-r--r--app/workers/post_receive.rb2
-rw-r--r--app/workers/process_commit_worker.rb2
-rw-r--r--app/workers/project_cache_worker.rb2
-rw-r--r--app/workers/project_destroy_worker.rb2
-rw-r--r--app/workers/project_export_worker.rb2
-rw-r--r--app/workers/project_migrate_hashed_storage_worker.rb2
-rw-r--r--app/workers/project_service_worker.rb2
-rw-r--r--app/workers/propagate_service_template_worker.rb2
-rw-r--r--app/workers/prune_old_events_worker.rb2
-rw-r--r--app/workers/reactive_caching_worker.rb2
-rw-r--r--app/workers/rebase_worker.rb2
-rw-r--r--app/workers/remove_expired_group_links_worker.rb2
-rw-r--r--app/workers/remove_expired_members_worker.rb2
-rw-r--r--app/workers/remove_old_web_hook_logs_worker.rb2
-rw-r--r--app/workers/remove_unreferenced_lfs_objects_worker.rb2
-rw-r--r--app/workers/repository_archive_cache_worker.rb2
-rw-r--r--app/workers/repository_check/batch_worker.rb2
-rw-r--r--app/workers/repository_check/clear_worker.rb2
-rw-r--r--app/workers/repository_check/single_repository_worker.rb2
-rw-r--r--app/workers/repository_fork_worker.rb2
-rw-r--r--app/workers/repository_import_worker.rb2
-rw-r--r--app/workers/repository_remove_remote_worker.rb2
-rw-r--r--app/workers/repository_update_remote_mirror_worker.rb2
-rw-r--r--app/workers/requests_profiles_worker.rb2
-rw-r--r--app/workers/run_pipeline_schedule_worker.rb2
-rw-r--r--app/workers/schedule_update_user_activity_worker.rb2
-rw-r--r--app/workers/stage_update_worker.rb2
-rw-r--r--app/workers/storage_migrator_worker.rb2
-rw-r--r--app/workers/stuck_ci_jobs_worker.rb2
-rw-r--r--app/workers/stuck_import_jobs_worker.rb2
-rw-r--r--app/workers/stuck_merge_jobs_worker.rb2
-rw-r--r--app/workers/system_hook_push_worker.rb2
-rw-r--r--app/workers/trending_projects_worker.rb2
-rw-r--r--app/workers/update_head_pipeline_for_merge_request_worker.rb2
-rw-r--r--app/workers/update_merge_requests_worker.rb2
-rw-r--r--app/workers/update_user_activity_worker.rb2
-rw-r--r--app/workers/upload_checksum_worker.rb2
-rw-r--r--app/workers/wait_for_cluster_creation_worker.rb2
-rw-r--r--app/workers/web_hook_worker.rb2
-rw-r--r--changelogs/unreleased/40484-ordered-lists-copy-gfm.yml5
-rw-r--r--changelogs/unreleased/45703-open-web-ide-file-tree.yml5
-rw-r--r--changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml5
-rw-r--r--changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml5
-rw-r--r--changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml5
-rw-r--r--changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml5
-rw-r--r--changelogs/unreleased/47794-environment-scope-cluster-page.yml6
-rw-r--r--changelogs/unreleased/48126-fix-prometheus-installation.yml5
-rw-r--r--changelogs/unreleased/48339-sorting-by-name-on-explore-projects-page-renders-a-500-error-when-logged-in.yml5
-rw-r--r--changelogs/unreleased/48378-avatar-upload.yml5
-rw-r--r--changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml5
-rw-r--r--changelogs/unreleased/48528-fix-mr-autocompletion.yml5
-rw-r--r--changelogs/unreleased/6591-dont-load-omniauth-if-not-enabled.yml5
-rw-r--r--changelogs/unreleased/6598-notify-only-open-unmergeable-mr.yml5
-rw-r--r--changelogs/unreleased/add-missing-index-for-deployments.yml5
-rw-r--r--changelogs/unreleased/bvl-dont-generate-mo.yml5
-rw-r--r--changelogs/unreleased/enforce-variable-value-to-be-a-string.yml5
-rw-r--r--changelogs/unreleased/fix-alert-btn.yml5
-rw-r--r--changelogs/unreleased/fix-favicon-cross-origin.yml5
-rw-r--r--changelogs/unreleased/frozen-string-app-workers.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-workers-2.yml5
-rw-r--r--changelogs/unreleased/mk-rake-task-verify-remote-files.yml5
-rw-r--r--changelogs/unreleased/prefer-destructuring-fix.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-mysql-arel-from.yml5
-rw-r--r--changelogs/unreleased/revert-merge-request-widget-button-height.yml5
-rw-r--r--changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml5
-rw-r--r--changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml5
-rw-r--r--changelogs/unreleased/zj-gitaly-read-write-check.yml5
-rw-r--r--config/locales/doorkeeper.en.yml16
-rw-r--r--db/migrate/20180626125654_add_index_on_deployable_for_deployments.rb18
-rw-r--r--db/schema.rb3
-rw-r--r--doc/development/i18n/proofreader.md1
-rw-r--r--doc/development/what_requires_downtime.md33
-rw-r--r--doc/install/installation.md5
-rw-r--r--doc/update/10.8-to-11.0.md7
-rw-r--r--lib/api/branches.rb8
-rw-r--r--lib/backup/repository.rb8
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb6
-rw-r--r--lib/gitaly/server.rb18
-rw-r--r--lib/gitlab/background_migration/cleanup_concurrent_rename.rb14
-rw-r--r--lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb52
-rw-r--r--lib/gitlab/background_migration/cleanup_concurrent_type_change.rb48
-rw-r--r--lib/gitlab/database/median.rb8
-rw-r--r--lib/gitlab/database/migration_helpers.rb91
-rw-r--r--lib/gitlab/git/commit.rb41
-rw-r--r--lib/gitlab/git/repository.rb7
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb7
-rw-r--r--lib/gitlab/health_checks/fs_shards_check.rb169
-rw-r--r--lib/gitlab/health_checks/gitaly_check.rb14
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb4
-rw-r--r--spec/controllers/health_controller_spec.rb4
-rw-r--r--spec/controllers/metrics_controller_spec.rb7
-rw-r--r--spec/features/admin/admin_settings_spec.rb23
-rw-r--r--spec/features/protected_branches_spec.rb47
-rw-r--r--spec/helpers/application_helper_spec.rb2
-rw-r--r--spec/javascripts/behaviors/copy_as_gfm_spec.js55
-rw-r--r--spec/javascripts/blob/3d_viewer/mesh_object_spec.js4
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js96
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js19
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js13
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js8
-rw-r--r--spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js8
-rw-r--r--spec/javascripts/filtered_search/recent_searches_root_spec.js3
-rw-r--r--spec/javascripts/gl_field_errors_spec.js2
-rw-r--r--spec/javascripts/groups/components/app_spec.js6
-rw-r--r--spec/javascripts/groups/components/group_item_spec.js2
-rw-r--r--spec/javascripts/helpers/init_vue_mr_page_helper.js2
-rw-r--r--spec/javascripts/ide/helpers.js26
-rw-r--r--spec/javascripts/ide/lib/diff/controller_spec.js2
-rw-r--r--spec/javascripts/ide/stores/actions/tree_spec.js36
-rw-r--r--spec/javascripts/namespace_select_spec.js4
-rw-r--r--spec/javascripts/notebook/cells/markdown_spec.js1
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_table_spec.js2
-rw-r--r--spec/javascripts/smart_interval_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/icon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js2
-rw-r--r--spec/lib/gitaly/server_spec.rb34
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb55
-rw-r--r--spec/lib/gitlab/health_checks/fs_shards_check_spec.rb200
-rw-r--r--spec/lib/gitlab/health_checks/gitaly_check_spec.rb7
-rw-r--r--spec/models/application_setting_spec.rb36
-rw-r--r--spec/models/commit_spec.rb31
-rw-r--r--spec/models/repository_spec.rb36
-rw-r--r--spec/requests/api/branches_spec.rb29
-rw-r--r--spec/support/helpers/stub_object_storage.rb7
-rw-r--r--spec/support/shared_examples/features/protected_branches_access_control_ce.rb36
317 files changed, 1617 insertions, 959 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index b9c5973d7ac..77b1b72fe68 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -69,5 +69,3 @@ rules:
FunctionExpression:
parameters: 1
body: 1
- ## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
- prefer-destructuring: off
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 72725122b8f..f9f38766392 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,28 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.0.2 (2018-06-26)
+
+### Fixed (8 changes, 1 of them is from the community)
+
+- Serve favicon image always from the main GitLab domain to avoid issues with CORS. !19810 (Alexis Reigel)
+- Specify chart version when installing applications on Clusters. !20010
+- Fix invalid fuzzy translations being generated during installation. !20048
+- Fix incremental rollouts for Auto DevOps. !20061
+- Notify conflict for only open merge request. !20125
+- Only load Omniauth if enabled. !20132
+- Fix sorting by name on explore projects page. !20162
+- Fix alert button styling so that they don't show up white.
+
+### Performance (1 change)
+
+- Remove performance bottleneck preventing large wiki pages from displaying. !20174
+
+### Added (1 change)
+
+- Add support for verifying remote uploads, artifacts, and LFS objects in check rake tasks. !19501
+
+
## 11.0.1 (2018-06-21)
### Security (5 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index a9a7f3fec01..3f667dbcd89 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.107.0
+0.108.0
diff --git a/Gemfile b/Gemfile
index 93c6115eeec..283886b7187 100644
--- a/Gemfile
+++ b/Gemfile
@@ -418,7 +418,7 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.102.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.103.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
diff --git a/Gemfile.lock b/Gemfile.lock
index 8281c1eff9a..13f9212c637 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -282,7 +282,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.102.0)
+ gitaly-proto (0.103.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -1041,7 +1041,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.102.0)
+ gitaly-proto (~> 0.103.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 52388f17c7c..3161e4653ee 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -285,7 +285,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.102.0)
+ gitaly-proto (0.103.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -1051,7 +1051,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.102.0)
+ gitaly-proto (~> 0.103.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
diff --git a/PROCESS.md b/PROCESS.md
index a46fd8c25b4..1cc5b44aa67 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -197,24 +197,7 @@ to. For example:
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
you can ask for an exception to be made.
-Go to [Release tasks issue tracker](https://gitlab.com/gitlab-org/release/tasks/issues/new) and create an issue
-using the `Exception-request` issue template.
-
-**Do not** set the relevant `Pick into X.Y` label (see above) before request an
-exception; this should be done after the exception is approved.
-
-You can find who is who on the [team page](https://about.gitlab.com/team/).
-
-Whether an exception is made is determined by weighing the benefit and urgency of the change
-(how important it is to the company that this is released _right now_ instead of in a month)
-against the potential negative impact
-(things breaking without enough time to comfortably find and fix them before the release on the 22nd).
-When in doubt, we err on the side of _not_ cherry-picking.
-
-For example, it is likely that an exception will be made for a trivial 1-5 line performance improvement
-(e.g. adding a database index or adding `includes` to a query), but not for a new feature, no matter how relatively small or thoroughly tested.
-
-All MRs which have had exceptions granted must be merged by the 15th.
+Check [this guide](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md) about how to open an exception request before opening one.
### Regressions
diff --git a/app/assets/javascripts/ajax_loading_spinner.js b/app/assets/javascripts/ajax_loading_spinner.js
index bd08308904c..54e86f329e4 100644
--- a/app/assets/javascripts/ajax_loading_spinner.js
+++ b/app/assets/javascripts/ajax_loading_spinner.js
@@ -26,7 +26,7 @@ export default class AjaxLoadingSpinner {
}
static toggleLoadingIcon(iconElement) {
- const classList = iconElement.classList;
+ const { classList } = iconElement;
classList.toggle(iconElement.dataset.icon);
classList.toggle('fa-spinner');
classList.toggle('fa-spin');
diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js
index 75834ba351d..00419e80cbb 100644
--- a/app/assets/javascripts/behaviors/copy_to_clipboard.js
+++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js
@@ -52,7 +52,7 @@ export default function initCopyToClipboard() {
* data types to the intended values.
*/
$(document).on('copy', 'body > textarea[readonly]', (e) => {
- const clipboardData = e.originalEvent.clipboardData;
+ const { clipboardData } = e.originalEvent;
if (!clipboardData) return;
const text = e.target.value;
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 9745e37acce..5d7a3bed301 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -321,7 +321,7 @@ export class CopyAsGFM {
}
static copyAsGFM(e, transformer) {
- const clipboardData = e.originalEvent.clipboardData;
+ const { clipboardData } = e.originalEvent;
if (!clipboardData) return;
const documentFragment = getSelectedFragment();
@@ -338,7 +338,7 @@ export class CopyAsGFM {
}
static pasteGFM(e) {
- const clipboardData = e.originalEvent.clipboardData;
+ const { clipboardData } = e.originalEvent;
if (!clipboardData) return;
const text = clipboardData.getData('text/plain');
diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
index 766039404ce..7986287f7e7 100644
--- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
+++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
@@ -84,7 +84,7 @@ class BalsamiqViewer {
renderTemplate(preview) {
const resource = this.getResource(preview.resourceID);
const name = BalsamiqViewer.parseTitle(resource);
- const image = preview.image;
+ const { image } = preview;
const template = PREVIEW_TEMPLATE({
name,
diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js
index 06ef86ecb77..b88e69a07bf 100644
--- a/app/assets/javascripts/blob/balsamiq_viewer.js
+++ b/app/assets/javascripts/blob/balsamiq_viewer.js
@@ -12,7 +12,7 @@ export default function loadBalsamiqFile() {
if (!(viewer instanceof Element)) return;
- const endpoint = viewer.dataset.endpoint;
+ const { endpoint } = viewer.dataset;
const balsamiqViewer = new BalsamiqViewer(viewer);
balsamiqViewer.loadFile(endpoint).catch(onError);
diff --git a/app/assets/javascripts/blob/stl_viewer.js b/app/assets/javascripts/blob/stl_viewer.js
index 63236b6477f..339906adc34 100644
--- a/app/assets/javascripts/blob/stl_viewer.js
+++ b/app/assets/javascripts/blob/stl_viewer.js
@@ -5,7 +5,7 @@ export default () => {
[].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => {
el.addEventListener('click', (e) => {
- const target = e.target;
+ const { target } = e;
e.preventDefault();
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
index 806e038a95f..55278626ffc 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
@@ -23,7 +23,7 @@
},
methods: {
removeIssue() {
- const issue = this.issue;
+ const { issue } = this;
const lists = issue.getLists();
const listLabelIds = lists.map(list => list.label.id);
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index a5f636a1e45..200d1923635 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -121,7 +121,7 @@ export default () => {
this.filterManager.updateTokens();
},
updateDetailIssue(newIssue) {
- const sidebarInfoEndpoint = newIssue.sidebarInfoEndpoint;
+ const { sidebarInfoEndpoint } = newIssue;
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
newIssue.setFetchingState('subscriptions', true);
BoardService.getIssueInfo(sidebarInfoEndpoint)
@@ -144,7 +144,7 @@ export default () => {
Store.detail.issue = {};
},
toggleSubscription(id) {
- const issue = Store.detail.issue;
+ const { issue } = Store.detail;
if (issue.id === id && issue.toggleSubscriptionEndpoint) {
issue.setFetchingState('subscriptions', true);
BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint)
diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js
index a4220cd840d..0d9ac367a70 100644
--- a/app/assets/javascripts/boards/stores/modal_store.js
+++ b/app/assets/javascripts/boards/stores/modal_store.js
@@ -26,7 +26,7 @@ class ModalStore {
toggleIssue(issueObj) {
const issue = issueObj;
- const selected = issue.selected;
+ const { selected } = issue;
issue.selected = !selected;
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index e42a3632e79..8139aa69fc7 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -81,7 +81,7 @@ export default class Clusters {
}
initApplications() {
- const store = this.store;
+ const { store } = this;
const el = document.querySelector('#js-cluster-applications');
this.applications = new Vue({
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 2d180e9903a..410580b4c25 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -122,7 +122,7 @@ export default class ImageFile {
return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
- ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
+ ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref;
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
@@ -159,7 +159,7 @@ export default class ImageFile {
return $('.onion-skin.view', this.file).each((function(_this) {
return function(index, view) {
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
- ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
+ ref = _this.prepareFrames(view), [maxWidth, maxHeight] = ref;
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
$track = $('.drag-track', view);
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index f77a5730b77..02aa507ba03 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -281,7 +281,7 @@ export default class CreateMergeRequestDropdown {
if (event.target === this.branchInput) {
target = 'branch';
- value = this.branchInput.value;
+ ({ value } = this.branchInput);
} else if (event.target === this.refInput) {
target = 'ref';
value =
diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
index 40f7c2fe5f3..5528d2a542b 100644
--- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
+++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
@@ -111,7 +111,7 @@ const DiffNoteAvatars = Vue.extend({
});
},
addNoCommentClass() {
- const notesCount = this.notesCount;
+ const { notesCount } = this;
$(this.$el).closest('.js-avatar-container')
.toggleClass('no-comment-btn', notesCount > 0)
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
index 66b20cc8739..2b893e35b6d 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
@@ -73,7 +73,7 @@ const JumpToDiscussion = Vue.extend({
}).toArray();
};
- const discussions = this.discussions;
+ const { discussions } = this;
if (activeTab === 'diffs') {
discussionsSelector = '.diffs .notes[data-discussion-id]';
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
index a9800a11644..7dcf3594471 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
@@ -18,7 +18,7 @@ import './components/new_issue_for_discussion';
export default () => {
const projectPathHolder =
document.querySelector('.merge-request') || document.querySelector('.commit-box');
- const projectPath = projectPathHolder.dataset.projectPath;
+ const { projectPath } = projectPathHolder.dataset;
const COMPONENT_SELECTOR =
'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn';
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 82ca10f4163..deddb61ca31 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -26,6 +26,10 @@ export default {
type: String,
required: true,
},
+ projectPath: {
+ type: String,
+ required: true,
+ },
shouldShow: {
type: Boolean,
required: false,
@@ -94,18 +98,16 @@ export default {
},
},
mounted() {
- this.setEndpoint(this.endpoint);
- this
- .fetchDiffFiles()
- .catch(() => {
- createFlash(__('Something went wrong on our end. Please try again!'));
- });
+ this.setBaseConfig({ endpoint: this.endpoint, projectPath: this.projectPath });
+ this.fetchDiffFiles().catch(() => {
+ createFlash(__('Fetching diff files failed. Please reload the page to try again!'));
+ });
},
created() {
this.adjustView();
},
methods: {
- ...mapActions(['setEndpoint', 'fetchDiffFiles']),
+ ...mapActions(['setBaseConfig', 'fetchDiffFiles']),
setActive(filePath) {
this.activeFile = filePath;
},
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index adcd22f7876..48ba967285f 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -1,5 +1,7 @@
<script>
-import { mapGetters } from 'vuex';
+import { mapGetters, mapState } from 'vuex';
+import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
+import { diffModes } from '~/ide/constants';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
@@ -7,6 +9,7 @@ export default {
components: {
InlineDiffView,
ParallelDiffView,
+ DiffViewer,
},
props: {
diffFile: {
@@ -15,7 +18,18 @@ export default {
},
},
computed: {
+ ...mapState({
+ projectPath: state => state.diffs.projectPath,
+ endpoint: state => state.diffs.endpoint,
+ }),
...mapGetters(['isInlineView', 'isParallelView']),
+ diffMode() {
+ const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]);
+ return diffModes[diffModeKey] || diffModes.replaced;
+ },
+ isTextFile() {
+ return this.diffFile.text;
+ },
},
};
</script>
@@ -23,16 +37,26 @@ export default {
<template>
<div class="diff-content">
<div class="diff-viewer">
- <inline-diff-view
- v-if="isInlineView"
- :diff-file="diffFile"
- :diff-lines="diffFile.highlightedDiffLines || []"
- />
- <parallel-diff-view
- v-if="isParallelView"
- :diff-file="diffFile"
- :diff-lines="diffFile.parallelDiffLines || []"
- />
+ <template v-if="isTextFile">
+ <inline-diff-view
+ v-if="isInlineView"
+ :diff-file="diffFile"
+ :diff-lines="diffFile.highlightedDiffLines || []"
+ />
+ <parallel-diff-view
+ v-else-if="isParallelView"
+ :diff-file="diffFile"
+ :diff-lines="diffFile.parallelDiffLines || []"
+ />
+ </template>
+ <diff-viewer
+ v-else
+ :diff-mode="diffMode"
+ :new-path="diffFile.newPath"
+ :new-sha="diffFile.diffRefs.headSha"
+ :old-path="diffFile.oldPath"
+ :old-sha="diffFile.diffRefs.baseSha"
+ :project-path="projectPath"/>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
index 3193b18becb..7e50a0aed84 100644
--- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
+++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
@@ -47,7 +47,7 @@ export default {
methods: {
...mapActions(['toggleDiscussion']),
getTooltipText(noteData) {
- let note = noteData.note;
+ let { note } = noteData;
if (note.length > LENGTH_OF_AVATAR_TOOLTIP) {
note = truncate(note, LENGTH_OF_AVATAR_TOOLTIP);
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index 05dca0cdd9a..8999fd2ac96 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -124,7 +124,7 @@ export default {
const newLineNumber = this.metaData.newPos || 0;
const offset = newLineNumber - oldLineNumber;
const bottom = this.isBottom;
- const fileHash = this.fileHash;
+ const { fileHash } = this;
const view = this.diffViewType;
let unfold = true;
let lineNumber = newLineNumber - 1;
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index 86f5e98194d..6943b462e86 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -1,9 +1,12 @@
<script>
+import $ from 'jquery';
import { mapState, mapGetters, mapActions } from 'vuex';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import noteForm from '../../notes/components/note_form.vue';
import { getNoteFormData } from '../store/utils';
+import Autosave from '../../autosave';
+import { DIFF_NOTE_TYPE, NOTE_TYPE } from '../constants';
export default {
components: {
@@ -37,11 +40,28 @@ export default {
noteableData: state => state.notes.noteableData,
diffViewType: state => state.diffs.diffViewType,
}),
- ...mapGetters(['noteableType', 'getNotesDataByProp']),
+ ...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
+ },
+ mounted() {
+ if (this.isLoggedIn) {
+ const noteableData = this.getNoteableData;
+ const keys = [
+ NOTE_TYPE,
+ this.noteableType,
+ noteableData.id,
+ noteableData.diff_head_sha,
+ DIFF_NOTE_TYPE,
+ noteableData.source_project_id,
+ this.line.lineCode,
+ ];
+
+ this.autosave = new Autosave($(this.$refs.noteForm.$refs.textarea), keys);
+ }
},
methods: {
...mapActions(['cancelCommentForm', 'saveNote', 'fetchDiscussions']),
handleCancelCommentForm() {
+ this.autosave.reset();
this.cancelCommentForm({
lineCode: this.line.lineCode,
});
@@ -82,6 +102,7 @@ export default {
class="content discussion-form discussion-form-container discussion-notes"
>
<note-form
+ ref="noteForm"
:is-editing="true"
:line-code="line.lineCode"
save-button-title="Comment"
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 0ed3dc7f3ad..21376117bef 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -36,7 +36,7 @@ export default {
<table
:class="userColorScheme"
:data-commit-id="commitId"
- class="code diff-wrap-lines js-syntax-highlight text-file">
+ class="code diff-wrap-lines js-syntax-highlight text-file js-diff-inline-view">
<tbody>
<template
v-for="(line, index) in normalizedDiffLines"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 2ddf8e6c6ed..60edbcbbda8 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -89,7 +89,7 @@ export default {
return isLeftExpanded || isRightExpanded;
},
getLineCode(line, side) {
- const lineCode = side.lineCode;
+ const { lineCode } = side;
if (lineCode) {
return lineCode;
}
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 1a7478b307e..d314f08e60e 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -7,6 +7,7 @@ export const CONTEXT_LINE_TYPE = 'context';
export const EMPTY_CELL_TYPE = 'empty-cell';
export const COMMENT_FORM_TYPE = 'commentForm';
export const DIFF_NOTE_TYPE = 'DiffNote';
+export const NOTE_TYPE = 'Note';
export const NEW_LINE_TYPE = 'new';
export const OLD_LINE_TYPE = 'old';
export const TEXT_DIFF_POSITION_TYPE = 'text';
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index f6840f87034..aae89109c27 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -16,6 +16,7 @@ export default function initDiffsApp(store) {
return {
endpoint: dataset.endpoint,
+ projectPath: dataset.projectPath,
currentUser: convertObjectPropsToCamelCase(JSON.parse(dataset.currentUserData), {
deep: true,
}),
@@ -31,6 +32,7 @@ export default function initDiffsApp(store) {
props: {
endpoint: this.endpoint,
currentUser: this.currentUser,
+ projectPath: this.projectPath,
shouldShow: this.activeTab === 'diffs',
},
});
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index f8089b314d3..bf188a44022 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -10,8 +10,9 @@ import {
DIFF_VIEW_COOKIE_NAME,
} from '../constants';
-export const setEndpoint = ({ commit }, endpoint) => {
- commit(types.SET_ENDPOINT, endpoint);
+export const setBaseConfig = ({ commit }, options) => {
+ const { endpoint, projectPath } = options;
+ commit(types.SET_BASE_CONFIG, { endpoint, projectPath });
};
export const setLoadingState = ({ commit }, state) => {
@@ -86,7 +87,7 @@ export const expandAllFiles = ({ commit }) => {
};
export default {
- setEndpoint,
+ setBaseConfig,
setLoadingState,
fetchDiffFiles,
setInlineDiffViewType,
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 882a098c977..94caa131506 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -13,6 +13,7 @@ export default {
state: {
isLoading: true,
endpoint: '',
+ basePath: '',
commit: null,
diffFiles: [],
mergeRequestDiffs: [],
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index a65b205b8e7..63e9239dce4 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -1,4 +1,4 @@
-export const SET_ENDPOINT = 'SET_ENDPOINT';
+export const SET_BASE_CONFIG = 'SET_BASE_CONFIG';
export const SET_LOADING = 'SET_LOADING';
export const SET_DIFF_DATA = 'SET_DIFF_DATA';
export const SET_DIFF_FILES = 'SET_DIFF_FILES';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index fd9ea73e33d..339a33f8b71 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -5,8 +5,9 @@ import { findDiffFile, addLineReferences, removeMatchLine, addContextLines } fro
import * as types from './mutation_types';
export default {
- [types.SET_ENDPOINT](state, endpoint) {
- Object.assign(state, { endpoint });
+ [types.SET_BASE_CONFIG](state, options) {
+ const { endpoint, projectPath } = options;
+ Object.assign(state, { endpoint, projectPath });
},
[types.SET_LOADING](state, isLoading) {
@@ -73,7 +74,7 @@ export default {
[types.EXPAND_ALL_FILES](state) {
const diffFiles = [];
- state.diffFiles.forEach((file) => {
+ state.diffFiles.forEach(file => {
diffFiles.push({
...file,
collapsed: false,
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 866e91057ec..5ecdccf63ad 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -292,7 +292,7 @@
if (this.model &&
this.model.last_deployment &&
this.model.last_deployment.deployable) {
- const deployable = this.model.last_deployment.deployable;
+ const { deployable } = this.model.last_deployment;
return `${deployable.name} #${deployable.id}`;
}
return '';
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index 5f2989ab854..5ce9225a4bb 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -146,7 +146,7 @@ export default class EnvironmentsStore {
* @return {Array}
*/
updateEnvironmentProp(environment, prop, newValue) {
- const environments = this.state.environments;
+ const { environments } = this.state;
const updatedEnvironments = environments.map((env) => {
const updateEnv = Object.assign({}, env);
@@ -161,7 +161,7 @@ export default class EnvironmentsStore {
}
getOpenFolders() {
- const environments = this.state.environments;
+ const { environments } = this.state;
return environments.filter(env => env.isFolder && env.isOpen);
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index 9bc36c1f9b6..27fff488603 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -35,7 +35,7 @@ export default class DropdownUtils {
// Remove the symbol for filter
if (value[0] === filterSymbol) {
- symbol = value[0];
+ [symbol] = value;
value = value.slice(1);
}
@@ -162,7 +162,7 @@ export default class DropdownUtils {
// Determines the full search query (visual tokens + input)
static getSearchQuery(untilInput = false) {
- const container = FilteredSearchContainer.container;
+ const { container } = FilteredSearchContainer;
const tokens = [].slice.call(container.querySelectorAll('.tokens-container li'));
const values = [];
@@ -220,7 +220,7 @@ export default class DropdownUtils {
}
static getInputSelectionPosition(input) {
- const selectionStart = input.selectionStart;
+ const { selectionStart } = input;
let inputValue = input.value;
// Replace all spaces inside quote marks with underscores
// (will continue to match entire string until an end quote is found if any)
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index d7e1de18d09..296571606d6 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -159,7 +159,7 @@ export default class FilteredSearchDropdownManager {
load(key, firstLoad = false) {
const mappingKey = this.mapping[key];
const glClass = mappingKey.gl;
- const element = mappingKey.element;
+ const { element } = mappingKey;
let forceShowList = false;
if (!mappingKey.reference) {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index cf5ba1e1771..81286c54c4c 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -235,7 +235,7 @@ export default class FilteredSearchManager {
checkForEnter(e) {
if (e.keyCode === 38 || e.keyCode === 40) {
- const selectionStart = this.filteredSearchInput.selectionStart;
+ const { selectionStart } = this.filteredSearchInput;
e.preventDefault();
this.filteredSearchInput.setSelectionRange(selectionStart, selectionStart);
@@ -496,7 +496,7 @@ export default class FilteredSearchManager {
// Replace underscore with hyphen in the sanitizedkey.
// e.g. 'my_reaction' => 'my-reaction'
sanitizedKey = sanitizedKey.replace('_', '-');
- const symbol = match.symbol;
+ const { symbol } = match;
let quotationsToUse = '';
if (sanitizedValue.indexOf(' ') !== -1) {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index 600024c21c3..56fe1ab4e90 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -101,7 +101,7 @@ export default class FilteredSearchVisualTokens {
static updateLabelTokenColor(tokenValueContainer, tokenValue) {
const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
- const baseEndpoint = filteredSearchInput.dataset.baseEndpoint;
+ const { baseEndpoint } = filteredSearchInput.dataset;
const labelsEndpoint = FilteredSearchVisualTokens.getEndpointWithQueryParams(
`${baseEndpoint}/labels.json`,
filteredSearchInput.dataset.endpointQueryParams,
@@ -215,7 +215,7 @@ export default class FilteredSearchVisualTokens {
static addFilterVisualToken(tokenName, tokenValue, canEdit) {
const { lastVisualToken, isLastVisualTokenValid }
= FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
- const addVisualTokenElement = FilteredSearchVisualTokens.addVisualTokenElement;
+ const { addVisualTokenElement } = FilteredSearchVisualTokens;
if (isLastVisualTokenValid) {
addVisualTokenElement(tokenName, tokenValue, false, canEdit);
diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js
index f9338b82acf..c1efa9c86f4 100644
--- a/app/assets/javascripts/filtered_search/recent_searches_root.js
+++ b/app/assets/javascripts/filtered_search/recent_searches_root.js
@@ -29,7 +29,7 @@ class RecentSearchesRoot {
}
render() {
- const state = this.store.state;
+ const { state } = this.store;
this.vm = new Vue({
el: this.wrapperElement,
components: {
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index b0f674f2c05..09186a865e4 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -80,7 +80,7 @@ class GfmAutoComplete {
let tpl = '/${name} ';
let referencePrefix = null;
if (value.params.length > 0) {
- referencePrefix = value.params[0][0];
+ [[referencePrefix]] = value.params;
if (/^[@%~]/.test(referencePrefix)) {
tpl += '<%- referencePrefix %>';
}
@@ -458,7 +458,7 @@ class GfmAutoComplete {
static isLoading(data) {
let dataToInspect = data;
if (data && data.length > 0) {
- dataToInspect = data[0];
+ [dataToInspect] = data;
}
const loadingState = GfmAutoComplete.defaultLoadingData[0];
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 45889c2d604..8d231e6c405 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -613,7 +613,7 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.renderItem = function(data, group, index) {
- var field, fieldName, html, selected, text, url, value, rowHidden;
+ var field, html, selected, text, url, value, rowHidden;
if (!this.options.renderRow) {
value = this.options.id ? this.options.id(data) : data.id;
@@ -651,7 +651,7 @@ GitLabDropdown = (function() {
html = this.options.renderRow.call(this.options, data, this);
} else {
if (!selected) {
- fieldName = this.options.fieldName;
+ const { fieldName } = this.options;
if (value) {
field = this.dropdown.parent().find(`input[name='${fieldName}'][value='${value}']`);
@@ -705,7 +705,8 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.highlightTextMatches = function(text, term) {
const occurrences = fuzzaldrinPlus.match(text, term);
- const indexOf = [].indexOf;
+ const { indexOf } = [];
+
return text.split('').map(function(character, i) {
if (indexOf.call(occurrences, i) !== -1) {
return "<b>" + character + "</b>";
@@ -721,9 +722,9 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.rowClicked = function(el) {
- var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value, isMarking;
+ var field, groupName, isInput, selectedIndex, selectedObject, value, isMarking;
- fieldName = this.options.fieldName;
+ const { fieldName } = this.options;
isInput = $(this.el).is('input');
if (this.renderedData) {
groupName = el.data('group');
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index 57eaac72906..83a9008a94b 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -29,7 +29,7 @@ export default () => {
groupsApp,
},
data() {
- const dataset = this.$options.el.dataset;
+ const { dataset } = this.$options.el;
const hideProjects = dataset.hideProjects === 'true';
const store = new GroupsStore(hideProjects);
const service = new GroupsService(dataset.endpoint);
@@ -42,7 +42,7 @@ export default () => {
};
},
beforeMount() {
- const dataset = this.$options.el.dataset;
+ const { dataset } = this.$options.el;
let groupFilterList = null;
const form = document.querySelector(dataset.formSel);
const filter = document.querySelector(dataset.filterSel);
diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue
index a4cf3edb981..f5252ce7706 100644
--- a/app/assets/javascripts/ide/components/file_finder/item.vue
+++ b/app/assets/javascripts/ide/components/file_finder/item.vue
@@ -30,7 +30,7 @@ export default {
},
computed: {
pathWithEllipsis() {
- const path = this.file.path;
+ const { path } = this.file;
return path.length < MAX_PATH_LENGTH
? path
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index 1814924be39..677b282bd61 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -23,6 +23,7 @@
let { result } = target;
if (!isText) {
+ // eslint-disable-next-line prefer-destructuring
result = result.split('base64,')[1];
}
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index dedc2988618..5cd2c9ce188 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -69,7 +69,7 @@ export default {
>
<icon
:size="16"
- name="pipeline"
+ name="rocket"
/>
</button>
</li>
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index c34547fcc60..f490a3a2a39 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -95,24 +95,53 @@ export default {
return this.file.changed || this.file.tempFile || this.file.staged;
},
},
+ mounted() {
+ if (this.hasPathAtCurrentRoute()) {
+ this.scrollIntoView(true);
+ }
+ },
updated() {
if (this.file.type === 'blob' && this.file.active) {
- this.$el.scrollIntoView({
- behavior: 'smooth',
- block: 'nearest',
- });
+ this.scrollIntoView();
}
},
methods: {
...mapActions(['toggleTreeOpen']),
clickFile() {
// Manual Action if a tree is selected/opened
- if (this.isTree && this.$router.currentRoute.path === `/project${this.file.url}`) {
+ if (this.isTree && this.hasUrlAtCurrentRoute()) {
this.toggleTreeOpen(this.file.path);
}
router.push(`/project${this.file.url}`);
},
+ scrollIntoView(isInit = false) {
+ const block = isInit && this.isTree ? 'center' : 'nearest';
+
+ this.$el.scrollIntoView({
+ behavior: 'smooth',
+ block,
+ });
+ },
+ hasPathAtCurrentRoute() {
+ if (!this.$router || !this.$router.currentRoute) {
+ return false;
+ }
+
+ // - strip route up to "/-/" and ending "/"
+ const routePath = this.$router.currentRoute.path
+ .replace(/^.*?[/]-[/]/g, '')
+ .replace(/[/]$/g, '');
+
+ // - strip ending "/"
+ const filePath = this.file.path
+ .replace(/[/]$/g, '');
+
+ return filePath === routePath;
+ },
+ hasUrlAtCurrentRoute() {
+ return this.$router.currentRoute.path === `/project${this.file.url}`;
+ },
},
};
</script>
diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js
index f09930e8158..78b2eab6399 100644
--- a/app/assets/javascripts/ide/lib/diff/diff_worker.js
+++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js
@@ -2,7 +2,7 @@ import { computeDiff } from './diff';
// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', (e) => {
- const data = e.data;
+ const { data } = e;
// eslint-disable-next-line no-restricted-globals
self.postMessage({
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 74f9c112f5a..29995a29d1a 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -8,7 +8,7 @@ import { setPageTitle } from '../utils';
import { viewerTypes } from '../../constants';
export const closeFile = ({ commit, state, dispatch }, file) => {
- const path = file.path;
+ const { path } = file;
const indexOfClosedFile = state.openFiles.findIndex(f => f.key === file.key);
const fileWasActive = file.active;
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index cc5116413f7..2fbc9990fa2 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -9,6 +9,17 @@ export const toggleTreeOpen = ({ commit }, path) => {
commit(types.TOGGLE_TREE_OPEN, path);
};
+export const showTreeEntry = ({ commit, dispatch, state }, path) => {
+ const entry = state.entries[path];
+ const parentPath = entry ? entry.parentPath : '';
+
+ if (parentPath) {
+ commit(types.SET_TREE_OPEN, parentPath);
+
+ dispatch('showTreeEntry', parentPath);
+ }
+};
+
export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
if (row.type === 'tree') {
dispatch('toggleTreeOpen', row.path);
@@ -21,6 +32,8 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
} else {
dispatch('getFileData', { path: row.path });
}
+
+ dispatch('showTreeEntry', row.path);
};
export const getLastCommitData = ({ state, commit, dispatch }, tree = state) => {
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 99b315ac4db..fda606dbf01 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -28,6 +28,7 @@ export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN';
// Tree mutation types
export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA';
export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN';
+export const SET_TREE_OPEN = 'SET_TREE_OPEN';
export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL';
export const CREATE_TREE = 'CREATE_TREE';
export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES';
diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js
index 1176c040fb9..2cf34af9274 100644
--- a/app/assets/javascripts/ide/stores/mutations/tree.js
+++ b/app/assets/javascripts/ide/stores/mutations/tree.js
@@ -6,6 +6,11 @@ export default {
opened: !state.entries[path].opened,
});
},
+ [types.SET_TREE_OPEN](state, path) {
+ Object.assign(state.entries[path], {
+ opened: true,
+ });
+ },
[types.CREATE_TREE](state, { treePath }) {
Object.assign(state, {
trees: Object.assign({}, state.trees, {
diff --git a/app/assets/javascripts/image_diff/helpers/dom_helper.js b/app/assets/javascripts/image_diff/helpers/dom_helper.js
index 12d56714b34..a319bcccb8f 100644
--- a/app/assets/javascripts/image_diff/helpers/dom_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/dom_helper.js
@@ -2,7 +2,8 @@ export function setPositionDataAttribute(el, options) {
// Update position data attribute so that the
// new comment form can use this data for ajax request
const { x, y, width, height } = options;
- const position = el.dataset.position;
+ const { position } = el.dataset;
+
const positionObject = Object.assign({}, JSON.parse(position), {
x,
y,
diff --git a/app/assets/javascripts/image_diff/helpers/utils_helper.js b/app/assets/javascripts/image_diff/helpers/utils_helper.js
index 28d9a969143..beec99e6934 100644
--- a/app/assets/javascripts/image_diff/helpers/utils_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/utils_helper.js
@@ -40,8 +40,7 @@ export function getTargetSelection(event) {
const x = event.offsetX;
const y = event.offsetY;
- const width = imageEl.width;
- const height = imageEl.height;
+ const { width, height } = imageEl;
const actualWidth = imageEl.naturalWidth;
const actualHeight = imageEl.naturalHeight;
diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
index b2c2de9e5de..07cf1eff279 100644
--- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js
+++ b/app/assets/javascripts/issuable/auto_width_dropdown_select.js
@@ -10,7 +10,7 @@ class AutoWidthDropdownSelect {
}
init() {
- const dropdownClass = this.dropdownClass;
+ const { dropdownClass } = this;
this.$selectElement.select2({
dropdownCssClass: dropdownClass,
...AutoWidthDropdownSelect.selectOptions(this.dropdownClass),
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
index f2939ad4dbe..0db7b95636c 100644
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ b/app/assets/javascripts/jobs/job_details_bundle.js
@@ -4,7 +4,7 @@ import jobHeader from './components/header.vue';
import detailsBlock from './components/sidebar_details_block.vue';
export default () => {
- const dataset = document.getElementById('js-job-details-vue').dataset;
+ const { dataset } = document.getElementById('js-job-details-vue');
const mediator = new JobMediator({ endpoint: dataset.endpoint });
mediator.fetchJob();
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index dfc3f7a94c8..37a45d1d1a2 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -56,7 +56,7 @@ export default class LabelsSelect {
.map(function () {
return this.value;
}).get();
- const handleClick = options.handleClick;
+ const { handleClick } = options;
$sidebarLabelTooltip.tooltip();
@@ -215,7 +215,7 @@ export default class LabelsSelect {
}
else {
if (label.color != null) {
- color = label.color[0];
+ [color] = label.color;
}
}
if (color) {
@@ -243,7 +243,8 @@ export default class LabelsSelect {
var $dropdownParent = $dropdown.parent();
var $dropdownInputField = $dropdownParent.find('.dropdown-input-field');
var isSelected = el !== null ? el.hasClass('is-active') : false;
- var title = selected.title;
+
+ var { title } = selected;
var selectedLabels = this.selected;
if ($dropdownInputField.length && $dropdownInputField.val().length) {
@@ -382,7 +383,7 @@ export default class LabelsSelect {
}));
}
else {
- var labels = gl.issueBoards.BoardsStore.detail.issue.labels;
+ var { labels } = gl.issueBoards.BoardsStore.detail.issue;
labels = labels.filter(function (selectedLabel) {
return selectedLabel.id !== label.id;
});
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 68f92c7f08a..6b7550efff8 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -164,7 +164,7 @@ export const scrollToElement = element => {
if (!(element instanceof $)) {
$el = $(element);
}
- const top = $el.offset().top;
+ const { top } = $el.offset();
return $('body, html').animate(
{
@@ -189,12 +189,25 @@ export const getParameterByName = (name, urlToParse) => {
return decodeURIComponent(results[2].replace(/\+/g, ' '));
};
+const handleSelectedRange = (range) => {
+ const container = range.commonAncestorContainer;
+ // add context to fragment if needed
+ if (container.tagName === 'OL') {
+ const parentContainer = document.createElement(container.tagName);
+ parentContainer.appendChild(range.cloneContents());
+ return parentContainer;
+ }
+ return range.cloneContents();
+};
+
export const getSelectedFragment = () => {
const selection = window.getSelection();
if (selection.rangeCount === 0) return null;
const documentFragment = document.createDocumentFragment();
+
for (let i = 0; i < selection.rangeCount; i += 1) {
- documentFragment.appendChild(selection.getRangeAt(i).cloneContents());
+ const range = selection.getRangeAt(i);
+ documentFragment.appendChild(handleSelectedRange(range));
}
if (documentFragment.textContent.length === 0) return null;
@@ -203,9 +216,7 @@ export const getSelectedFragment = () => {
export const insertText = (target, text) => {
// Firefox doesn't support `document.execCommand('insertText', false, text)` on textareas
- const selectionStart = target.selectionStart;
- const selectionEnd = target.selectionEnd;
- const value = target.value;
+ const { selectionStart, selectionEnd, value } = target;
const textBefore = value.substring(0, selectionStart);
const textAfter = value.substring(selectionEnd, value.length);
@@ -245,7 +256,8 @@ export const nodeMatchesSelector = (node, selector) => {
// IE11 doesn't support `node.matches(selector)`
- let parentNode = node.parentNode;
+ let { parentNode } = node;
+
if (!parentNode) {
parentNode = document.createElement('div');
// eslint-disable-next-line no-param-reassign
@@ -281,6 +293,8 @@ export const normalizeCRLFHeaders = headers => {
headersArray.forEach(header => {
const keyValue = header.split(': ');
+
+ // eslint-disable-next-line prefer-destructuring
headersObject[keyValue[0]] = keyValue[1];
});
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index f086d962221..afbab59055b 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -13,7 +13,7 @@ export function formatRelevantDigits(number) {
let relevantDigits = 0;
let formattedNumber = '';
if (!Number.isNaN(Number(number))) {
- digitsLeft = number.toString().split('.')[0];
+ [digitsLeft] = number.toString().split('.');
switch (digitsLeft.length) {
case 1:
relevantDigits = 3;
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
index 70f185e3656..1501296ac4f 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
@@ -156,7 +156,7 @@ import Cookies from 'js-cookie';
return 0;
}
- const files = this.state.conflictsData.files;
+ const { files } = this.state.conflictsData;
let count = 0;
files.forEach((file) => {
@@ -313,7 +313,7 @@ import Cookies from 'js-cookie';
},
isReadyToCommit() {
- const files = this.state.conflictsData.files;
+ const { files } = this.state.conflictsData;
const hasCommitMessage = $.trim(this.state.conflictsData.commitMessage).length;
let unresolved = 0;
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index 491858c3602..7badd68089c 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -12,7 +12,7 @@ import syntaxHighlight from '../syntax_highlight';
export default function initMergeConflicts() {
const INTERACTIVE_RESOLVE_MODE = 'interactive';
const conflictsEl = document.querySelector('#conflicts');
- const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore;
+ const { mergeConflictsStore } = gl.mergeConflicts;
const mergeConflictsService = new MergeConflictsService({
conflictsPath: conflictsEl.dataset.conflictsPath,
resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath,
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 83d326ef68f..329d4303132 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -64,7 +64,7 @@ import Notes from './notes';
/* eslint-enable max-len */
// Store the `location` object, allowing for easier stubbing in tests
-let location = window.location;
+let { location } = window;
export default class MergeRequestTabs {
constructor({ action, setUrl, stubLocation } = {}) {
@@ -279,7 +279,7 @@ export default class MergeRequestTabs {
mountPipelinesView() {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
- const CommitPipelinesTable = gl.CommitPipelinesTable;
+ const { CommitPipelinesTable } = gl;
this.commitPipelinesTable = new CommitPipelinesTable({
propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index ed3a27dd68b..cee39fd0559 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -41,10 +41,10 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
} else {
const unusedColors = _.difference(defaultColorOrder, usedColors);
if (unusedColors.length > 0) {
- pick = unusedColors[0];
+ [pick] = unusedColors;
} else {
usedColors = [];
- pick = defaultColorOrder[0];
+ [pick] = defaultColorOrder;
}
}
usedColors.push(pick);
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index 6a8591692f1..94da1be4066 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -101,8 +101,8 @@ export default (function() {
};
BranchGraph.prototype.buildGraph = function() {
- var cuday, cumonth, day, j, len, mm, r, ref;
- r = this.r;
+ var cuday, cumonth, day, j, len, mm, ref;
+ const { r } = this;
cuday = 0;
cumonth = "";
r.rect(0, 0, 40, this.barHeight).attr({
@@ -121,7 +121,7 @@ export default (function() {
font: "12px Monaco, monospace",
fill: "#BBB"
});
- cuday = day[0];
+ [cuday] = day;
}
if (cumonth !== day[1]) {
// Months
@@ -129,6 +129,8 @@ export default (function() {
font: "12px Monaco, monospace",
fill: "#EEE"
});
+
+ // eslint-disable-next-line prefer-destructuring
cumonth = day[1];
}
}
@@ -169,8 +171,8 @@ export default (function() {
};
BranchGraph.prototype.bindEvents = function() {
- var element;
- element = this.element;
+ const { element } = this;
+
return $(element).scroll((function(_this) {
return function(event) {
return _this.renderPartialGraph();
@@ -207,11 +209,13 @@ export default (function() {
};
BranchGraph.prototype.appendLabel = function(x, y, commit) {
- var label, r, rect, shortrefs, text, textbox, triangle;
+ var label, rect, shortrefs, text, textbox, triangle;
+
if (!commit.refs) {
return;
}
- r = this.r;
+
+ const { r } = this;
shortrefs = commit.refs;
// Truncate if longer than 15 chars
if (shortrefs.length > 17) {
@@ -242,11 +246,8 @@ export default (function() {
};
BranchGraph.prototype.appendAnchor = function(x, y, commit) {
- var anchor, options, r, top;
- r = this.r;
- top = this.top;
- options = this.options;
- anchor = r.circle(x, y, 10).attr({
+ const { r, top, options } = this;
+ const anchor = r.circle(x, y, 10).attr({
fill: "#000",
opacity: 0,
cursor: "pointer"
@@ -262,14 +263,15 @@ export default (function() {
};
BranchGraph.prototype.drawDot = function(x, y, commit) {
- var avatar_box_x, avatar_box_y, r;
- r = this.r;
+ const { r } = this;
r.circle(x, y, 3).attr({
fill: this.colors[commit.space],
stroke: "none"
});
- avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10;
- avatar_box_y = y - 10;
+
+ const avatar_box_x = this.offsetX + this.unitSpace * this.mspace + 10;
+ const avatar_box_y = y - 10;
+
r.rect(avatar_box_x, avatar_box_y, 20, 20).attr({
stroke: this.colors[commit.space],
"stroke-width": 2
@@ -282,10 +284,10 @@ export default (function() {
};
BranchGraph.prototype.drawLines = function(x, y, commit) {
- var arrow, color, i, j, len, offset, parent, parentCommit, parentX1, parentX2, parentY, r, ref, results, route;
- r = this.r;
- ref = commit.parents;
- results = [];
+ var arrow, color, i, len, offset, parent, parentCommit, parentX1, parentX2, parentY, route;
+ const { r } = this;
+ const ref = commit.parents;
+ const results = [];
for (i = 0, len = ref.length; i < len; i += 1) {
parent = ref[i];
@@ -331,11 +333,10 @@ export default (function() {
};
BranchGraph.prototype.markCommit = function(commit) {
- var r, x, y;
if (commit.id === this.options.commit_id) {
- r = this.r;
- x = this.offsetX + this.unitSpace * (this.mspace - commit.space);
- y = this.offsetY + this.unitTime * commit.time;
+ const { r } = this;
+ const x = this.offsetX + this.unitSpace * (this.mspace - commit.space);
+ const y = this.offsetY + this.unitTime * commit.time;
r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr({
fill: "#000",
"fill-opacity": .5,
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 41ba5b28a1b..205d9766656 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -52,7 +52,7 @@ export default class NewBranchForm {
validate() {
var errorMessage, errors, formatter, unique, validator;
- const indexOf = [].indexOf;
+ const { indexOf } = [];
this.branchNameError.empty();
unique = function(values, value) {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index da1a52155d8..48cda28a1ae 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -311,7 +311,7 @@ export default class Notes {
},
})
.then(({ data }) => {
- const notes = data.notes;
+ const { notes } = data;
this.last_fetched_at = data.last_fetched_at;
this.setPollingInterval(data.notes.length);
$.each(notes, (i, note) => this.renderNote(note));
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 17b5e8d1ae8..98f8b9af168 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -72,7 +72,7 @@ export default {
},
mounted() {
this.fetchNotes();
- const parentElement = this.$el.parentElement;
+ const { parentElement } = this.$el;
if (parentElement && parentElement.classList.contains('js-vue-notes-event')) {
parentElement.addEventListener('toggleAward', event => {
diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js
index ee7628840cf..f5dce94caad 100644
--- a/app/assets/javascripts/notes/services/notes_service.js
+++ b/app/assets/javascripts/notes/services/notes_service.js
@@ -28,7 +28,7 @@ export default {
},
poll(data = {}) {
const endpoint = data.notesData.notesPath;
- const lastFetchedAt = data.lastFetchedAt;
+ const { lastFetchedAt } = data;
const options = {
headers: {
'X-Last-Fetched-At': lastFetchedAt ? `${lastFetchedAt}` : undefined,
diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
index cc2805a1901..d6aa4bb95d2 100644
--- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
+++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
@@ -96,7 +96,7 @@
this.enteredUsername = '';
},
onSecondaryAction() {
- const form = this.$refs.form;
+ const { form } = this.$refs;
form.action = this.blockUserUrl;
this.$refs.method.value = 'put';
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 6fc43af2623..ff19b9a9c30 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -61,7 +61,7 @@ export default class Todos {
e.stopPropagation();
e.preventDefault();
- const target = e.target;
+ const { target } = e;
target.setAttribute('disabled', true);
target.classList.add('disabled');
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
index ae72c8cb4d5..6c1788dc160 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
@@ -80,10 +80,11 @@ export default (function() {
};
ContributorsStatGraph.prototype.redraw_authors = function() {
- var author_commits, x_domain;
$("ol").html("");
- x_domain = ContributorsGraph.prototype.x_domain;
- author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain);
+
+ const { x_domain } = ContributorsGraph.prototype;
+ const author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain);
+
return _.each(author_commits, (function(_this) {
return function(d) {
_this.redraw_author_commit_info(d);
@@ -102,7 +103,7 @@ export default (function() {
};
ContributorsStatGraph.prototype.change_date_header = function() {
- const x_domain = ContributorsGraph.prototype.x_domain;
+ const { x_domain } = ContributorsGraph.prototype;
const formattedDateRange = sprintf(
s__('ContributorsPage|%{startDate} – %{endDate}'),
{
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index 37ef77c8e43..1faa59fb45b 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', () => {
const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');
autoDevOpsSettings.addEventListener('click', event => {
- const target = event.target;
+ const { target } = event;
if (target.classList.contains('js-toggle-extra-settings')) {
autoDevOpsExtraSettings.classList.toggle(
'hidden',
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index b49a16a87e6..cf3ff48e608 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -10,7 +10,7 @@ import eventHub from './event_hub';
Vue.use(Translate);
export default () => {
- const dataset = document.querySelector('.js-pipeline-details-vue').dataset;
+ const { dataset } = document.querySelector('.js-pipeline-details-vue');
const mediator = new PipelinesMediator({ endpoint: dataset.endpoint });
diff --git a/app/assets/javascripts/pipelines/services/pipelines_service.js b/app/assets/javascripts/pipelines/services/pipelines_service.js
index 59c8b9c58e5..8317d3f4510 100644
--- a/app/assets/javascripts/pipelines/services/pipelines_service.js
+++ b/app/assets/javascripts/pipelines/services/pipelines_service.js
@@ -19,7 +19,7 @@ export default class PipelinesService {
getPipelines(data = {}) {
const { scope, page } = data;
- const CancelToken = axios.CancelToken;
+ const { CancelToken } = axios;
this.cancelationSource = CancelToken.source();
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 45670584679..0e973cab4d2 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -43,7 +43,7 @@ MarkdownPreview.prototype.showPreview = function ($form) {
this.fetchMarkdownPreview(mdText, url, (function (response) {
var body;
if (response.body.length > 0) {
- body = response.body;
+ ({ body } = response);
} else {
body = this.emptyMessage;
}
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index c6d809d84a6..f641b23e519 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -47,7 +47,8 @@ import _ from 'underscore';
var _this;
_this = this;
this.fileInput.on('change', function(e) {
- return _this.onFileInputChange(e, this);
+ _this.onFileInputChange(e, this);
+ this.value = null;
});
this.pickImageEl.on('click', this.onPickImageClick);
this.modalCrop.on('shown.bs.modal', this.onModalShow);
@@ -85,11 +86,10 @@ import _ from 'underscore';
cropBoxResizable: false,
toggleDragModeOnDblclick: false,
built: function() {
- var $image, container, cropBoxHeight, cropBoxWidth;
- $image = $(this);
- container = $image.cropper('getContainerData');
- cropBoxWidth = _this.cropBoxWidth;
- cropBoxHeight = _this.cropBoxHeight;
+ const $image = $(this);
+ const container = $image.cropper('getContainerData');
+ const { cropBoxWidth, cropBoxHeight } = _this;
+
return $image.cropper('setCropBoxData', {
width: cropBoxWidth,
height: cropBoxHeight,
@@ -136,7 +136,7 @@ import _ from 'underscore';
}
dataURLtoBlob(dataURL) {
- var array, binary, i, k, len, v;
+ var array, binary, i, len, v;
binary = atob(dataURL.split(',')[1]);
array = [];
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index bcdb3f739fe..05485e352dc 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -88,7 +88,7 @@ export default class ProjectFindFile {
// render result
renderList(filePaths, searchText) {
- var blobItemUrl, filePath, html, i, j, len, matches, results;
+ var blobItemUrl, filePath, html, i, len, matches, results;
this.element.find(".tree-table > tbody").empty();
results = [];
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index c772fca14bb..a4c7c143e56 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -47,7 +47,7 @@
},
methods: {
successCallback(res) {
- const pipelines = res.data.pipelines;
+ const { pipelines } = res.data;
if (pipelines.length > 0) {
// The pipeline entity always keeps the latest pipeline info on the `details.status`
this.ciStatus = pipelines[0].details.status;
diff --git a/app/assets/javascripts/projects_dropdown/index.js b/app/assets/javascripts/projects_dropdown/index.js
index e1ca70c51a6..6056f12aa4f 100644
--- a/app/assets/javascripts/projects_dropdown/index.js
+++ b/app/assets/javascripts/projects_dropdown/index.js
@@ -31,7 +31,7 @@ document.addEventListener('DOMContentLoaded', () => {
projectsDropdownApp,
},
data() {
- const dataset = this.$options.el.dataset;
+ const { dataset } = this.$options.el;
const store = new ProjectsStore();
const service = new ProjectsService(dataset.userName);
diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js
index 7c61c070a35..b601b19e7be 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_create.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js
@@ -1,11 +1,8 @@
import $ from 'jquery';
-import _ from 'underscore';
import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
import CreateItemDropdown from '../create_item_dropdown';
import AccessorUtilities from '../lib/utils/accessor';
-const PB_LOCAL_STORAGE_KEY = 'protected-branches-defaults';
-
export default class ProtectedBranchCreate {
constructor() {
this.$form = $('.js-new-protected-branch');
@@ -43,8 +40,6 @@ export default class ProtectedBranchCreate {
onSelect: this.onSelectCallback,
getData: ProtectedBranchCreate.getProtectedBranches,
});
-
- this.loadPreviousSelection($allowedToMergeDropdown.data('glDropdown'), $allowedToPushDropdown.data('glDropdown'));
}
// This will run after clicked callback
@@ -59,39 +54,10 @@ export default class ProtectedBranchCreate {
$allowedToPushInput.length
);
- this.savePreviousSelection($allowedToMergeInput.val(), $allowedToPushInput.val());
this.$form.find('input[type="submit"]').prop('disabled', completedForm);
}
static getProtectedBranches(term, callback) {
callback(gon.open_branches);
}
-
- loadPreviousSelection(mergeDropdown, pushDropdown) {
- let mergeIndex = 0;
- let pushIndex = 0;
- if (this.isLocalStorageAvailable) {
- const savedDefaults = JSON.parse(window.localStorage.getItem(PB_LOCAL_STORAGE_KEY));
- if (savedDefaults != null) {
- mergeIndex = _.findLastIndex(mergeDropdown.fullData.roles, {
- id: parseInt(savedDefaults.mergeSelection, 0),
- });
- pushIndex = _.findLastIndex(pushDropdown.fullData.roles, {
- id: parseInt(savedDefaults.pushSelection, 0),
- });
- }
- }
- mergeDropdown.selectRowAtIndex(mergeIndex);
- pushDropdown.selectRowAtIndex(pushIndex);
- }
-
- savePreviousSelection(mergeSelection, pushSelection) {
- if (this.isLocalStorageAvailable) {
- const branchDefaults = {
- mergeSelection,
- pushSelection,
- };
- window.localStorage.setItem(PB_LOCAL_STORAGE_KEY, JSON.stringify(branchDefaults));
- }
- }
}
diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js
index 6fb125192b2..e15cd94a915 100644
--- a/app/assets/javascripts/registry/index.js
+++ b/app/assets/javascripts/registry/index.js
@@ -10,7 +10,7 @@ export default () => new Vue({
registryApp,
},
data() {
- const dataset = document.querySelector(this.$options.el).dataset;
+ const { dataset } = document.querySelector(this.$options.el);
return {
endpoint: dataset.endpoint,
};
diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js
index c0de03373d8..a78aa90b7b5 100644
--- a/app/assets/javascripts/registry/stores/actions.js
+++ b/app/assets/javascripts/registry/stores/actions.js
@@ -20,7 +20,7 @@ export const fetchList = ({ commit }, { repo, page }) => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
return Vue.http.get(repo.tagsPath, { params: { page } }).then(response => {
- const headers = response.headers;
+ const { headers } = response;
return response.json().then(resp => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 2f4e4881f24..5b2e0468784 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -289,7 +289,7 @@ export default class SearchAutocomplete {
}
// If the dropdown is closed, we'll open it
- if (!this.dropdown.hasClass('open')) {
+ if (!this.dropdown.hasClass('show')) {
this.loadingSuggestions = false;
this.dropdownToggle.dropdown('toggle');
return this.searchInput.removeClass('disabled');
@@ -424,9 +424,9 @@ export default class SearchAutocomplete {
}
disableAutocomplete() {
- if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
+ if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('disabled');
- this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
+ this.dropdown.removeClass('show').trigger('hidden.bs.dropdown');
this.restoreMenu();
}
}
diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js
index 77ab7c964e6..5e385400747 100644
--- a/app/assets/javascripts/smart_interval.js
+++ b/app/assets/javascripts/smart_interval.js
@@ -42,8 +42,7 @@ export default class SmartInterval {
/* public */
start() {
- const cfg = this.cfg;
- const state = this.state;
+ const { cfg, state } = this;
if (cfg.immediateExecution && !this.isLoading) {
cfg.immediateExecution = false;
@@ -100,7 +99,7 @@ export default class SmartInterval {
/* private */
initInterval() {
- const cfg = this.cfg;
+ const { cfg } = this;
if (!cfg.lazyStart) {
this.start();
@@ -151,7 +150,7 @@ export default class SmartInterval {
}
incrementInterval() {
- const cfg = this.cfg;
+ const { cfg } = this;
const currentInterval = this.getCurrentInterval();
if (cfg.hiddenInterval && !this.isPageVisible()) return;
let nextInterval = currentInterval * cfg.incrementByFactorOf;
@@ -166,7 +165,7 @@ export default class SmartInterval {
isPageVisible() { return this.state.pageVisibility === 'visible'; }
stopTimer() {
- const state = this.state;
+ const { state } = this;
state.intervalId = window.clearInterval(state.intervalId);
}
diff --git a/app/assets/javascripts/test_utils/simulate_drag.js b/app/assets/javascripts/test_utils/simulate_drag.js
index e39213cb098..a5c18042ce7 100644
--- a/app/assets/javascripts/test_utils/simulate_drag.js
+++ b/app/assets/javascripts/test_utils/simulate_drag.js
@@ -38,14 +38,14 @@ function simulateEvent(el, type, options = {}) {
function isLast(target) {
const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
- const children = el.children;
+ const { children } = el;
return children.length - 1 === target.index;
}
function getTarget(target) {
const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
- const children = el.children;
+ const { children } = el;
return (
children[target.index] ||
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 7abe7a6be5f..e3d7645040d 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -250,7 +250,6 @@ function UsersSelect(currentUser, els, options = {}) {
let anyUser;
let index;
- let j;
let len;
let name;
let obj;
@@ -501,7 +500,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (this.multiSelect) {
selected = getSelected().find(u => user.id === u);
- const fieldName = this.fieldName;
+ const { fieldName } = this;
const field = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "'][value='" + user.id + "']");
if (field.length) {
@@ -553,7 +552,7 @@ function UsersSelect(currentUser, els, options = {}) {
minimumInputLength: 0,
query: function(query) {
return _this.users(query.term, options, function(users) {
- var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref;
+ var anyUser, data, emailUser, index, len, name, nullUser, obj, ref;
data = {
results: users
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index e455c4d2cb5..09477da40b5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -191,7 +191,7 @@ export default {
if (data.ci_status === this.mr.ciStatus) return;
if (!data.pipeline) return;
- const label = data.pipeline.details.status.label;
+ const { label } = data.pipeline.details.status;
const title = `Pipeline ${label}`;
const message = `Pipeline ${label} for "${data.title}"`;
@@ -211,7 +211,7 @@ export default {
// `params` should be an Array contains a Boolean, like `[true]`
// Passing parameter as Boolean didn't work.
eventHub.$on('SetBranchRemoveFlag', (params) => {
- this.mr.isRemovingSourceBranch = params[0];
+ [this.mr.isRemovingSourceBranch] = params;
});
eventHub.$on('FailedToMerge', (mergeError) => {
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index 6851029018a..133bdbb54f7 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -42,7 +42,7 @@ export default {
},
methods: {
onImgLoad() {
- const contentImg = this.$refs.contentImg;
+ const { contentImg } = this.$refs;
if (contentImg) {
this.isZoomable =
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index 09e0094054d..a10deb93f0f 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -4,7 +4,7 @@ import { __ } from '~/locale';
import $ from 'jquery';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-const CancelToken = axios.CancelToken;
+const { CancelToken } = axios;
let axiosSource;
export default {
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
index 2c47f5b9b35..d3cbe3c7e74 100644
--- a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -45,11 +45,15 @@ export default {
return DownloadDiffViewer;
}
},
+ basePath() {
+ // We might get the project path from rails with the relative url already setup
+ return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`;
+ },
fullOldPath() {
- return `${gon.relative_url_root}/${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`;
+ return `${this.basePath}${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`;
},
fullNewPath() {
- return `${gon.relative_url_root}/${this.projectPath}/raw/${this.newSha}/${this.newPath}`;
+ return `${this.basePath}${this.projectPath}/raw/${this.newSha}/${this.newPath}`;
},
},
};
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue
index 2370e59d017..8e9621c956f 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue
@@ -55,7 +55,7 @@
},
getItems() {
const total = this.pageInfo.totalPages;
- const page = this.pageInfo.page;
+ const { page } = this.pageInfo;
const items = [];
if (page > 1) {
diff --git a/app/assets/stylesheets/bootstrap.scss b/app/assets/stylesheets/bootstrap.scss
new file mode 100644
index 00000000000..a040c2f8c20
--- /dev/null
+++ b/app/assets/stylesheets/bootstrap.scss
@@ -0,0 +1,37 @@
+/*
+ * Includes specific styles from the bootstrap4 foler in node_modules
+ */
+
+@import "../../../node_modules/bootstrap/scss/functions";
+@import "../../../node_modules/bootstrap/scss/variables";
+@import "../../../node_modules/bootstrap/scss/mixins";
+@import "../../../node_modules/bootstrap/scss/root";
+@import "../../../node_modules/bootstrap/scss/reboot";
+@import "../../../node_modules/bootstrap/scss/type";
+@import "../../../node_modules/bootstrap/scss/images";
+@import "../../../node_modules/bootstrap/scss/code";
+@import "../../../node_modules/bootstrap/scss/grid";
+@import "../../../node_modules/bootstrap/scss/tables";
+@import "../../../node_modules/bootstrap/scss/forms";
+@import "../../../node_modules/bootstrap/scss/buttons";
+@import "../../../node_modules/bootstrap/scss/transitions";
+@import "../../../node_modules/bootstrap/scss/dropdown";
+@import "../../../node_modules/bootstrap/scss/button-group";
+@import "../../../node_modules/bootstrap/scss/input-group";
+@import "../../../node_modules/bootstrap/scss/custom-forms";
+@import "../../../node_modules/bootstrap/scss/nav";
+@import "../../../node_modules/bootstrap/scss/navbar";
+@import "../../../node_modules/bootstrap/scss/card";
+@import "../../../node_modules/bootstrap/scss/breadcrumb";
+@import "../../../node_modules/bootstrap/scss/pagination";
+@import "../../../node_modules/bootstrap/scss/badge";
+@import "../../../node_modules/bootstrap/scss/alert";
+@import "../../../node_modules/bootstrap/scss/progress";
+@import "../../../node_modules/bootstrap/scss/media";
+@import "../../../node_modules/bootstrap/scss/list-group";
+@import "../../../node_modules/bootstrap/scss/close";
+@import "../../../node_modules/bootstrap/scss/modal";
+@import "../../../node_modules/bootstrap/scss/tooltip";
+@import "../../../node_modules/bootstrap/scss/popover";
+@import "../../../node_modules/bootstrap/scss/utilities";
+@import "../../../node_modules/bootstrap/scss/print";
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 7c28024001f..c46b0b5db09 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -1,6 +1,7 @@
@import 'framework/variables';
@import 'framework/mixins';
-@import '../../../node_modules/bootstrap/scss/bootstrap';
+
+@import 'bootstrap';
@import 'bootstrap_migration';
@import 'framework/layout';
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 91a9b956d9d..5789c3fa1b1 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -539,6 +539,10 @@
display: block;
}
}
+
+ svg {
+ vertical-align: text-top;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 8e8a879be88..7b8e66b6f12 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -502,6 +502,10 @@
border-bottom: 0;
}
+.merge-request-details .file-content.image_file img {
+ max-height: 50vh;
+}
+
.diff-stats-summary-toggler {
padding: 0;
background-color: transparent;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index d96ba2107d1..ccf5d632614 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -46,7 +46,6 @@
.btn {
font-size: $gl-font-size;
- max-height: 26px;
&[disabled] {
opacity: 0.3;
diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb
index e54f372344d..3fedd5bfb29 100644
--- a/app/controllers/health_controller.rb
+++ b/app/controllers/health_controller.rb
@@ -8,7 +8,6 @@ class HealthController < ActionController::Base
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::FsShardsCheck,
Gitlab::HealthChecks::GitalyCheck
].freeze
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f5d94ad96a1..0190aa90763 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -270,7 +270,7 @@ module ApplicationHelper
{
members: members_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
issues: issues_project_autocomplete_sources_path(object),
- merge_requests: merge_requests_project_autocomplete_sources_path(object),
+ mergeRequests: merge_requests_project_autocomplete_sources_path(object),
labels: labels_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id]),
milestones: milestones_project_autocomplete_sources_path(object),
commands: commands_project_autocomplete_sources_path(object, type: noteable_type, type_id: params[:id])
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 3d58a14882f..bddeb8b0352 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -212,14 +212,6 @@ class ApplicationSetting < ActiveRecord::Base
end
end
- validates_each :disabled_oauth_sign_in_sources do |record, attr, value|
- value&.each do |source|
- unless Devise.omniauth_providers.include?(source.to_sym)
- record.errors.add(attr, "'#{source}' is not an OAuth sign-in source")
- end
- end
- end
-
validate :terms_exist, if: :enforce_terms?
before_validation :ensure_uuid!
@@ -330,6 +322,11 @@ class ApplicationSetting < ActiveRecord::Base
::Gitlab::Database.cached_column_exists?(:application_settings, :sidekiq_throttling_enabled)
end
+ def disabled_oauth_sign_in_sources=(sources)
+ sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s)
+ super(sources)
+ end
+
def domain_whitelist_raw
self.domain_whitelist&.join("\n")
end
diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb
index 236e9fe8c44..51ff9eff5e4 100644
--- a/app/services/metrics_service.rb
+++ b/app/services/metrics_service.rb
@@ -6,7 +6,8 @@ class MetricsService
Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck
+ Gitlab::HealthChecks::Redis::SharedStateCheck,
+ Gitlab::HealthChecks::GitalyCheck
].freeze
def prometheus_metrics_text
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index 38607ffca1c..bd43504dd37 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -324,3 +324,6 @@
= _('Configure push mirrors.')
.settings-content
= render partial: 'repository_mirrors_form'
+
+= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded
+
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
index 7637471f9ae..ee2d4c8430a 100644
--- a/app/views/admin/labels/_form.html.haml
+++ b/app/views/admin/labels/_form.html.haml
@@ -10,16 +10,16 @@
.col-sm-10
= f.text_field :description, class: "form-control js-quick-submit"
.form-group.row
- = f.label :color, "Background color", class: 'col-form-label col-sm-2'
+ = f.label :color, _("Background color"), class: 'col-form-label col-sm-2'
.col-sm-10
.input-group
.input-group-prepend
.input-group-text.label-color-preview &nbsp;
= f.text_field :color, class: "form-control"
.form-text.text-muted
- Choose any color.
+ = _('Choose any color.')
%br
- Or you can choose one of the suggested colors below
+ = _("Or you can choose one of the suggested colors below")
.suggest-colors
- suggested_colors.each do |color|
@@ -27,5 +27,5 @@
&nbsp;
.form-actions
- = f.submit 'Save', class: 'btn btn-save js-save-button'
- = link_to "Cancel", admin_labels_path, class: 'btn btn-cancel'
+ = f.submit _('Save'), class: 'btn btn-save js-save-button'
+ = link_to _("Cancel"), admin_labels_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index 009a47dd517..c3ea2352898 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -3,5 +3,5 @@
= render_colored_label(label, tooltip: false)
= markdown_field(label, :description)
.float-right
- = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm'
- = link_to 'Delete', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"}
+ = link_to _('Edit'), edit_admin_label_path(label), class: 'btn btn-sm'
+ = link_to _('Delete'), admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"}
diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml
index 96f0d404ac4..652ed095d00 100644
--- a/app/views/admin/labels/edit.html.haml
+++ b/app/views/admin/labels/edit.html.haml
@@ -1,7 +1,7 @@
-- add_to_breadcrumbs "Labels", admin_labels_path
-- breadcrumb_title "Edit Label"
-- page_title "Edit", @label.name, "Labels"
+- add_to_breadcrumbs _("Labels"), admin_labels_path
+- breadcrumb_title _("Edit Label")
+- page_title _("Edit"), @label.name, _("Labels")
%h3.page-title
- Edit Label
+ = _('Edit Label')
%hr
= render 'form'
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index add38fb333e..d3e5247447a 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -1,10 +1,10 @@
-- page_title "Labels"
+- page_title _("Labels")
%div
= link_to new_admin_label_path, class: "float-right btn btn-nr btn-new" do
- New label
+ = _('New label')
%h3.page-title
- Labels
+ = _('Labels')
%hr
.labels
@@ -14,5 +14,5 @@
= paginate @labels, theme: 'gitlab'
- else
.card.bg-light
- .nothing-here-block There are no labels yet
+ .nothing-here-block= _('There are no labels yet')
diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml
index 0135ad0723d..20103fb8a29 100644
--- a/app/views/admin/labels/new.html.haml
+++ b/app/views/admin/labels/new.html.haml
@@ -1,5 +1,5 @@
-- page_title "New Label"
+- page_title _("New Label")
%h3.page-title
- New Label
+ = _('New Label')
%hr
= render 'form'
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 1e72d88db1e..53f54db1ddf 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -4,27 +4,37 @@
- page_title 'New Group'
- header_title "Groups", dashboard_groups_path
-%h3.page-title
- New Group
-%hr
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ = _('New group')
+ %p
+ - group_docs_path = help_page_path('user/group/index')
+ - group_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_docs_path }
+ = s_('%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects.').html_safe % { group_docs_link_start: group_docs_link_start, group_docs_link_end: '</a>'.html_safe }
+ %p
+ - subgroup_docs_path = help_page_path('user/group/subgroups/index')
+ - subgroup_docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: subgroup_docs_path }
+ = s_('Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}.').html_safe % { subgroup_docs_link_start: subgroup_docs_link_start, subgroup_docs_link_end: '</a>'.html_safe }
-= form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f|
- = form_errors(@group)
- = render 'shared/group_form', f: f, autofocus: true
+ .col-lg-9
+ = form_for @group, html: { class: 'group-form gl-show-field-errors' } do |f|
+ = form_errors(@group)
+ = render 'shared/group_form', f: f, autofocus: true
- .form-group.row.group-description-holder
- = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2'
- .col-sm-10
- = render 'shared/choose_group_avatar_button', f: f
+ .form-group.row.group-description-holder
+ = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2'
+ .col-sm-10
+ = render 'shared/choose_group_avatar_button', f: f
- = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group
+ = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group
- = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
+ = render 'create_chat_team', f: f if Gitlab.config.mattermost.enabled
- .form-group.row
- .offset-sm-2.col-sm-10
- = render 'shared/group_tips'
+ .form-group.row
+ .offset-sm-2.col-sm-10
+ = render 'shared/group_tips'
- .form-actions
- = f.submit 'Create group', class: "btn btn-create"
- = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel'
+ .form-actions
+ = f.submit 'Create group', class: "btn btn-create"
+ = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel'
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index cdfd45fceb1..a74ea246eaf 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -20,7 +20,7 @@
%li
= link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
= _("Contribute to GitLab")
- = icon('external-link')
+ = sprite_icon('external-link', size: 16)
%li.divider
- if current_user_menu?(:sign_out)
%li
diff --git a/app/views/projects/clusters/_integration_form.html.haml b/app/views/projects/clusters/_integration_form.html.haml
index db97203a2aa..b46b45fea49 100644
--- a/app/views/projects/clusters/_integration_form.html.haml
+++ b/app/views/projects/clusters/_integration_form.html.haml
@@ -1,6 +1,6 @@
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
= form_errors(@cluster)
- .form-group.append-bottom-20
+ .form-group
%h5= s_('ClusterIntegration|Integration status')
%p
- if @cluster.enabled?
@@ -10,7 +10,7 @@
= s_('ClusterIntegration|Kubernetes cluster integration is enabled for this project.')
- else
= s_('ClusterIntegration|Kubernetes cluster integration is disabled for this project.')
- %label.append-bottom-10.js-cluster-enable-toggle-area
+ %label.append-bottom-0.js-cluster-enable-toggle-area
%button{ type: 'button',
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 Kubernetes cluster"),
@@ -20,19 +20,26 @@
= 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')
- .form-group
- %h5= s_('ClusterIntegration|Security')
- %p
- = s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.")
- = link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
-
- .form-group
- %h5= s_('ClusterIntegration|Environment scope')
- %p
- = s_("ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster.")
- = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments')
- = field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
+ - if has_multiple_clusters?(@project)
+ .form-group
+ %h5= s_('ClusterIntegration|Environment scope')
+ %p
+ = s_("ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster.")
+ = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments')
+ = field.text_field :environment_scope, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Environment scope')
- if can?(current_user, :update_cluster, @cluster)
.form-group
= field.submit _('Save changes'), class: 'btn btn-success'
+
+ - unless has_multiple_clusters?(@project)
+ %h5= s_('ClusterIntegration|Environment scope')
+ %p
+ %code *
+ is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster.
+ = link_to 'More information', ('https://docs.gitlab.com/ee/user/project/clusters/#setting-the-environment-scope')
+
+ %h5= s_('ClusterIntegration|Security')
+ %p
+ = s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.")
+ = link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml
index c7ac687e4a6..282566eeadc 100644
--- a/app/views/projects/deployments/_commit.html.haml
+++ b/app/views/projects/deployments/_commit.html.haml
@@ -14,4 +14,4 @@
= author_avatar(deployment.commit, size: 20)
= link_to_markdown commit_title, project_commit_path(@project, deployment.sha), class: "commit-row-message"
- else
- Cant find HEAD commit for this branch
+ = _("Can't find HEAD commit for this branch")
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
index 520696b01c6..85bc8ec07e3 100644
--- a/app/views/projects/deployments/_deployment.html.haml
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -1,14 +1,14 @@
.gl-responsive-table-row.deployment{ role: 'row' }
.table-section.section-10{ role: 'gridcell' }
- .table-mobile-header{ role: 'rowheader' } ID
+ .table-mobile-header{ role: 'rowheader' }= _("ID")
%strong.table-mobile-content ##{deployment.iid}
.table-section.section-30{ role: 'gridcell' }
- .table-mobile-header{ role: 'rowheader' } Commit
+ .table-mobile-header{ role: 'rowheader' }= _("Commit")
= render 'projects/deployments/commit', deployment: deployment
.table-section.section-25.build-column{ role: 'gridcell' }
- .table-mobile-header{ role: 'rowheader' } Job
+ .table-mobile-header{ role: 'rowheader' }= _("Job")
- if deployment.deployable
.table-mobile-content
.flex-truncate-parent
@@ -21,7 +21,7 @@
= user_avatar(user: deployment.user, size: 20)
.table-section.section-15{ role: 'gridcell' }
- .table-mobile-header{ role: 'rowheader' } Created
+ .table-mobile-header{ role: 'rowheader' }= _("Created")
%span.table-mobile-content= time_ago_with_tooltip(deployment.created_at)
.table-section.section-20.table-button-footer{ role: 'gridcell' }
diff --git a/app/views/projects/deployments/_rollback.haml b/app/views/projects/deployments/_rollback.haml
index 5941e01c6f1..95f950948ab 100644
--- a/app/views/projects/deployments/_rollback.haml
+++ b/app/views/projects/deployments/_rollback.haml
@@ -1,6 +1,6 @@
- if can?(current_user, :create_deployment, deployment) && deployment.deployable
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do
- if deployment.last?
- Re-deploy
+ = _("Re-deploy")
- else
- Rollback
+ = _("Rollback")
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 4fe0ae17ec5..b23baa22d8b 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -73,7 +73,8 @@
= render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request)
#js-diffs-app.diffs.tab-pane{ data: { "is-locked" => @merge_request.discussion_locked?,
endpoint: diffs_project_merge_request_path(@project, @merge_request, 'json', request.query_parameters),
- current_user_data: UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json } }
+ current_user_data: UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json,
+ project_path: project_path(@merge_request.project)} }
.mr-loading-status
= spinner
diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml
index 2d0bb722189..e5c82962f82 100644
--- a/app/views/shared/tokens/_scopes_form.html.haml
+++ b/app/views/shared/tokens/_scopes_form.html.haml
@@ -6,5 +6,4 @@
%fieldset
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}"
= label_tag ("#{prefix}_scopes_#{scope}"), scope, class: "label-light"
- %span= t(scope, scope: [:doorkeeper, :scopes])
.scope-description= t scope, scope: [:doorkeeper, :scope_desc]
diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb
index 044e470141e..06324575ffc 100644
--- a/app/workers/admin_email_worker.rb
+++ b/app/workers/admin_email_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AdminEmailWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/archive_trace_worker.rb b/app/workers/archive_trace_worker.rb
index dea7425ad88..9169f21af2a 100644
--- a/app/workers/archive_trace_worker.rb
+++ b/app/workers/archive_trace_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ArchiveTraceWorker
include ApplicationWorker
include PipelineBackgroundQueue
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index 8fe3619f6ee..dd62bb0f33d 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AuthorizedProjectsWorker
include ApplicationWorker
prepend WaitableWorker
diff --git a/app/workers/background_migration_worker.rb b/app/workers/background_migration_worker.rb
index 376703f6319..eaec7d48f35 100644
--- a/app/workers/background_migration_worker.rb
+++ b/app/workers/background_migration_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BackgroundMigrationWorker
include ApplicationWorker
diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb
index 62b212c79be..53d77dc4524 100644
--- a/app/workers/build_coverage_worker.rb
+++ b/app/workers/build_coverage_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildCoverageWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index 46f1ac09915..9dc2c7f3601 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildFinishedWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
index cbfca8c342c..f1f71dc589c 100644
--- a/app/workers/build_hooks_worker.rb
+++ b/app/workers/build_hooks_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildHooksWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/build_queue_worker.rb b/app/workers/build_queue_worker.rb
index e4f4e6c1d9e..1b3f1fd3c2a 100644
--- a/app/workers/build_queue_worker.rb
+++ b/app/workers/build_queue_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildQueueWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb
index 4b9097bc5e4..e1c1cc24a94 100644
--- a/app/workers/build_success_worker.rb
+++ b/app/workers/build_success_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildSuccessWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/build_trace_sections_worker.rb b/app/workers/build_trace_sections_worker.rb
index c0f5c144e10..f4114b3353c 100644
--- a/app/workers/build_trace_sections_worker.rb
+++ b/app/workers/build_trace_sections_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BuildTraceSectionsWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/ci/archive_traces_cron_worker.rb b/app/workers/ci/archive_traces_cron_worker.rb
index 2ac65f41f4e..7016edde698 100644
--- a/app/workers/ci/archive_traces_cron_worker.rb
+++ b/app/workers/ci/archive_traces_cron_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ArchiveTracesCronWorker
include ApplicationWorker
diff --git a/app/workers/ci/build_trace_chunk_flush_worker.rb b/app/workers/ci/build_trace_chunk_flush_worker.rb
index 218d6688bd9..6376c6d32cf 100644
--- a/app/workers/ci/build_trace_chunk_flush_worker.rb
+++ b/app/workers/ci/build_trace_chunk_flush_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildTraceChunkFlushWorker
include ApplicationWorker
diff --git a/app/workers/cluster_install_app_worker.rb b/app/workers/cluster_install_app_worker.rb
index f771cb4939f..32e2ea7996c 100644
--- a/app/workers/cluster_install_app_worker.rb
+++ b/app/workers/cluster_install_app_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterInstallAppWorker
include ApplicationWorker
include ClusterQueue
diff --git a/app/workers/cluster_provision_worker.rb b/app/workers/cluster_provision_worker.rb
index 1ab4de3b647..59de7903c1c 100644
--- a/app/workers/cluster_provision_worker.rb
+++ b/app/workers/cluster_provision_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterProvisionWorker
include ApplicationWorker
include ClusterQueue
diff --git a/app/workers/cluster_wait_for_app_installation_worker.rb b/app/workers/cluster_wait_for_app_installation_worker.rb
index d564d5e48bf..e8d7e52f70f 100644
--- a/app/workers/cluster_wait_for_app_installation_worker.rb
+++ b/app/workers/cluster_wait_for_app_installation_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterWaitForAppInstallationWorker
include ApplicationWorker
include ClusterQueue
diff --git a/app/workers/cluster_wait_for_ingress_ip_address_worker.rb b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb
index 8ba5951750c..6865384df44 100644
--- a/app/workers/cluster_wait_for_ingress_ip_address_worker.rb
+++ b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ClusterWaitForIngressIpAddressWorker
include ApplicationWorker
include ClusterQueue
diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb
index 37586e161c9..bb06e31641d 100644
--- a/app/workers/concerns/application_worker.rb
+++ b/app/workers/concerns/application_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Sidekiq::Worker.extend ActiveSupport::Concern
module ApplicationWorker
diff --git a/app/workers/concerns/cluster_applications.rb b/app/workers/concerns/cluster_applications.rb
index 24ecaa0b52f..9758a1ceb0e 100644
--- a/app/workers/concerns/cluster_applications.rb
+++ b/app/workers/concerns/cluster_applications.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ClusterApplications
extend ActiveSupport::Concern
diff --git a/app/workers/concerns/cluster_queue.rb b/app/workers/concerns/cluster_queue.rb
index 24b9f145220..e44b40c36c9 100644
--- a/app/workers/concerns/cluster_queue.rb
+++ b/app/workers/concerns/cluster_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Concern for setting Sidekiq settings for the various Gcp clusters workers.
#
diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb
index b6581779f6a..0683b229381 100644
--- a/app/workers/concerns/cronjob_queue.rb
+++ b/app/workers/concerns/cronjob_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Concern that sets various Sidekiq settings for workers executed using a
# cronjob.
module CronjobQueue
diff --git a/app/workers/concerns/exception_backtrace.rb b/app/workers/concerns/exception_backtrace.rb
index ea0f1f8d19b..37c9eaba0d7 100644
--- a/app/workers/concerns/exception_backtrace.rb
+++ b/app/workers/concerns/exception_backtrace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Concern for enabling a few lines of exception backtraces in Sidekiq
module ExceptionBacktrace
extend ActiveSupport::Concern
diff --git a/app/workers/concerns/gitlab/github_import/queue.rb b/app/workers/concerns/gitlab/github_import/queue.rb
index 22c2ce458e8..59b621f16ab 100644
--- a/app/workers/concerns/gitlab/github_import/queue.rb
+++ b/app/workers/concerns/gitlab/github_import/queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Gitlab
module GithubImport
module Queue
diff --git a/app/workers/concerns/mail_scheduler_queue.rb b/app/workers/concerns/mail_scheduler_queue.rb
index f3e9680d756..c051151e973 100644
--- a/app/workers/concerns/mail_scheduler_queue.rb
+++ b/app/workers/concerns/mail_scheduler_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MailSchedulerQueue
extend ActiveSupport::Concern
diff --git a/app/workers/concerns/new_issuable.rb b/app/workers/concerns/new_issuable.rb
index 526ed0bad07..7735dec5e6b 100644
--- a/app/workers/concerns/new_issuable.rb
+++ b/app/workers/concerns/new_issuable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NewIssuable
attr_reader :issuable, :user
diff --git a/app/workers/concerns/object_storage_queue.rb b/app/workers/concerns/object_storage_queue.rb
index a80f473a6d4..8650eed213a 100644
--- a/app/workers/concerns/object_storage_queue.rb
+++ b/app/workers/concerns/object_storage_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Concern for setting Sidekiq settings for the various GitLab ObjectStorage workers.
module ObjectStorageQueue
extend ActiveSupport::Concern
diff --git a/app/workers/concerns/pipeline_background_queue.rb b/app/workers/concerns/pipeline_background_queue.rb
index 8bf43de6b26..bbb8ad0c982 100644
--- a/app/workers/concerns/pipeline_background_queue.rb
+++ b/app/workers/concerns/pipeline_background_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Concern for setting Sidekiq settings for the low priority CI pipeline workers.
#
diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb
index e77093a6902..3aaed4669e5 100644
--- a/app/workers/concerns/pipeline_queue.rb
+++ b/app/workers/concerns/pipeline_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Concern for setting Sidekiq settings for the various CI pipeline workers.
#
diff --git a/app/workers/concerns/project_import_options.rb b/app/workers/concerns/project_import_options.rb
index ef23990ad97..22bdf441d6b 100644
--- a/app/workers/concerns/project_import_options.rb
+++ b/app/workers/concerns/project_import_options.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProjectImportOptions
extend ActiveSupport::Concern
diff --git a/app/workers/concerns/project_start_import.rb b/app/workers/concerns/project_start_import.rb
index 4e55a1ee3d6..46a133db2a1 100644
--- a/app/workers/concerns/project_start_import.rb
+++ b/app/workers/concerns/project_start_import.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Used in EE by mirroring
module ProjectStartImport
def start(project)
diff --git a/app/workers/concerns/repository_check_queue.rb b/app/workers/concerns/repository_check_queue.rb
index 43fb66c31b0..216d67e5dbc 100644
--- a/app/workers/concerns/repository_check_queue.rb
+++ b/app/workers/concerns/repository_check_queue.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Concern for setting Sidekiq settings for the various repository check workers.
module RepositoryCheckQueue
extend ActiveSupport::Concern
diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb
index 48ebe862248..d85bc7d1660 100644
--- a/app/workers/concerns/waitable_worker.rb
+++ b/app/workers/concerns/waitable_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WaitableWorker
extend ActiveSupport::Concern
diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb
index f371731f68c..a2da1bda11f 100644
--- a/app/workers/create_gpg_signature_worker.rb
+++ b/app/workers/create_gpg_signature_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateGpgSignatureWorker
include ApplicationWorker
diff --git a/app/workers/create_note_diff_file_worker.rb b/app/workers/create_note_diff_file_worker.rb
index 624b638a24e..0850250f7e3 100644
--- a/app/workers/create_note_diff_file_worker.rb
+++ b/app/workers/create_note_diff_file_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateNoteDiffFileWorker
include ApplicationWorker
diff --git a/app/workers/create_pipeline_worker.rb b/app/workers/create_pipeline_worker.rb
index c3ac35e54f5..037b4a57d4b 100644
--- a/app/workers/create_pipeline_worker.rb
+++ b/app/workers/create_pipeline_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreatePipelineWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/delete_merged_branches_worker.rb b/app/workers/delete_merged_branches_worker.rb
index 07cd1f02fb5..017d7fd1cb0 100644
--- a/app/workers/delete_merged_branches_worker.rb
+++ b/app/workers/delete_merged_branches_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteMergedBranchesWorker
include ApplicationWorker
diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb
index 6c431b02979..4d0295f8d2e 100644
--- a/app/workers/delete_user_worker.rb
+++ b/app/workers/delete_user_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteUserWorker
include ApplicationWorker
diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb
index dd8a6cbbef1..f9f0efb302a 100644
--- a/app/workers/email_receiver_worker.rb
+++ b/app/workers/email_receiver_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailReceiverWorker
include ApplicationWorker
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 2a4d65b5cb3..8d0cfc73ccd 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailsOnPushWorker
include ApplicationWorker
diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb
index 87e5dca01fd..5d3a9a39b93 100644
--- a/app/workers/expire_build_artifacts_worker.rb
+++ b/app/workers/expire_build_artifacts_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExpireBuildArtifactsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb
index 234b4357cf7..3b57ecb36e3 100644
--- a/app/workers/expire_build_instance_artifacts_worker.rb
+++ b/app/workers/expire_build_instance_artifacts_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExpireBuildInstanceArtifactsWorker
include ApplicationWorker
diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb
index 7217364a9f2..14a57b90114 100644
--- a/app/workers/expire_job_cache_worker.rb
+++ b/app/workers/expire_job_cache_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExpireJobCacheWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index db73d37868a..992fc63c451 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExpirePipelineCacheWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index ae5c5fac834..fd49bc18161 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitGarbageCollectWorker
include ApplicationWorker
diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb
index a0028e41332..0e4d40acc5c 100644
--- a/app/workers/gitlab_shell_worker.rb
+++ b/app/workers/gitlab_shell_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter
diff --git a/app/workers/gitlab_usage_ping_worker.rb b/app/workers/gitlab_usage_ping_worker.rb
index 6dd281b1147..b75e724ca98 100644
--- a/app/workers/gitlab_usage_ping_worker.rb
+++ b/app/workers/gitlab_usage_ping_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitlabUsagePingWorker
LEASE_TIMEOUT = 86400
diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb
index 509bd09dc2e..b4a3ddcae51 100644
--- a/app/workers/group_destroy_worker.rb
+++ b/app/workers/group_destroy_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GroupDestroyWorker
include ApplicationWorker
include ExceptionBacktrace
diff --git a/app/workers/import_export_project_cleanup_worker.rb b/app/workers/import_export_project_cleanup_worker.rb
index 9788c8df3a3..da3debdeede 100644
--- a/app/workers/import_export_project_cleanup_worker.rb
+++ b/app/workers/import_export_project_cleanup_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ImportExportProjectCleanupWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/invalid_gpg_signature_update_worker.rb b/app/workers/invalid_gpg_signature_update_worker.rb
index 6774ab307c6..4724ab7ad98 100644
--- a/app/workers/invalid_gpg_signature_update_worker.rb
+++ b/app/workers/invalid_gpg_signature_update_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class InvalidGpgSignatureUpdateWorker
include ApplicationWorker
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 9ae5456be4c..29631c6b7ac 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'json'
require 'socket'
@@ -69,8 +71,8 @@ class IrkerWorker
newbranch = "#{Gitlab.config.gitlab.url}/#{repo_path}/branches"
newbranch = "\x0302\x1f#{newbranch}\x0f" if @colors
- privmsg = "[#{repo_name}] #{committer} has created a new branch "
- privmsg += "#{branch}: #{newbranch}"
+ privmsg = "[#{repo_name}] #{committer} has created a new branch " \
+ "#{branch}: #{newbranch}"
sendtoirker privmsg
end
@@ -112,9 +114,7 @@ class IrkerWorker
url = compare_url data, project.full_path
commits = colorize_commits data['total_commits_count']
- new_commits = 'new commit'
- new_commits += 's' if data['total_commits_count'] > 1
-
+ new_commits = 'new commit'.pluralize(data['total_commits_count'])
sendtoirker "[#{repo}] #{committer} pushed #{commits} #{new_commits} " \
"to #{branch}: #{url}"
end
@@ -122,8 +122,8 @@ class IrkerWorker
def compare_url(data, repo_path)
sha1 = Commit.truncate_sha(data['before'])
sha2 = Commit.truncate_sha(data['after'])
- compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare"
- compare_url += "/#{sha1}...#{sha2}"
+ compare_url = "#{Gitlab.config.gitlab.url}/#{repo_path}/compare" \
+ "/#{sha1}...#{sha2}"
colorize_url compare_url
end
@@ -144,8 +144,7 @@ class IrkerWorker
def files_count(commit)
diff_size = commit.raw_deltas.size
- files = "#{diff_size} file"
- files += 's' if diff_size > 1
+ files = "#{diff_size} file".pluralize(diff_size)
files
end
diff --git a/app/workers/issue_due_scheduler_worker.rb b/app/workers/issue_due_scheduler_worker.rb
index 16ab5d069e0..c04a2d75e0b 100644
--- a/app/workers/issue_due_scheduler_worker.rb
+++ b/app/workers/issue_due_scheduler_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueDueSchedulerWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/mail_scheduler/issue_due_worker.rb b/app/workers/mail_scheduler/issue_due_worker.rb
index 54285884a52..8794ad7a82c 100644
--- a/app/workers/mail_scheduler/issue_due_worker.rb
+++ b/app/workers/mail_scheduler/issue_due_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MailScheduler
class IssueDueWorker
include ApplicationWorker
diff --git a/app/workers/mail_scheduler/notification_service_worker.rb b/app/workers/mail_scheduler/notification_service_worker.rb
index 7cfe0aa0df1..4726e416182 100644
--- a/app/workers/mail_scheduler/notification_service_worker.rb
+++ b/app/workers/mail_scheduler/notification_service_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_job/arguments'
module MailScheduler
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index ba832fe30c6..ee864b733cd 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeWorker
include ApplicationWorker
diff --git a/app/workers/namespaceless_project_destroy_worker.rb b/app/workers/namespaceless_project_destroy_worker.rb
index adb25c2a170..d9df42c9e17 100644
--- a/app/workers/namespaceless_project_destroy_worker.rb
+++ b/app/workers/namespaceless_project_destroy_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Worker to destroy projects that do not have a namespace
#
# It destroys everything it can without having the info about the namespace it
diff --git a/app/workers/new_issue_worker.rb b/app/workers/new_issue_worker.rb
index 3bc030f9c62..85b53973f56 100644
--- a/app/workers/new_issue_worker.rb
+++ b/app/workers/new_issue_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NewIssueWorker
include ApplicationWorker
include NewIssuable
diff --git a/app/workers/new_merge_request_worker.rb b/app/workers/new_merge_request_worker.rb
index bda2a0ab59d..5d8b8904502 100644
--- a/app/workers/new_merge_request_worker.rb
+++ b/app/workers/new_merge_request_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NewMergeRequestWorker
include ApplicationWorker
include NewIssuable
diff --git a/app/workers/new_note_worker.rb b/app/workers/new_note_worker.rb
index 67c54fbf10e..74f34dcf9aa 100644
--- a/app/workers/new_note_worker.rb
+++ b/app/workers/new_note_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NewNoteWorker
include ApplicationWorker
diff --git a/app/workers/object_storage/background_move_worker.rb b/app/workers/object_storage/background_move_worker.rb
index 9c4d72e0ecf..8dff65e46e3 100644
--- a/app/workers/object_storage/background_move_worker.rb
+++ b/app/workers/object_storage/background_move_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ObjectStorage
class BackgroundMoveWorker
include ApplicationWorker
diff --git a/app/workers/object_storage_upload_worker.rb b/app/workers/object_storage_upload_worker.rb
index 5c80f34069c..f17980a83d8 100644
--- a/app/workers/object_storage_upload_worker.rb
+++ b/app/workers/object_storage_upload_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# @Deprecated - remove once the `object_storage_upload` queue is empty
# The queue has been renamed `object_storage:object_storage_background_upload`
#
diff --git a/app/workers/pages_domain_verification_cron_worker.rb b/app/workers/pages_domain_verification_cron_worker.rb
index a3ff4bd2101..92d62a15aee 100644
--- a/app/workers/pages_domain_verification_cron_worker.rb
+++ b/app/workers/pages_domain_verification_cron_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PagesDomainVerificationCronWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/pages_domain_verification_worker.rb b/app/workers/pages_domain_verification_worker.rb
index 2e93489113c..4610b688189 100644
--- a/app/workers/pages_domain_verification_worker.rb
+++ b/app/workers/pages_domain_verification_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PagesDomainVerificationWorker
include ApplicationWorker
diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb
index 66a0ff83bef..13a6576a301 100644
--- a/app/workers/pages_worker.rb
+++ b/app/workers/pages_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PagesWorker
include ApplicationWorker
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index c94918ff4ee..58023e0af1b 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineHooksWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb
index d46d1f122fc..a97019b100a 100644
--- a/app/workers/pipeline_metrics_worker.rb
+++ b/app/workers/pipeline_metrics_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineMetricsWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/pipeline_notification_worker.rb b/app/workers/pipeline_notification_worker.rb
index a9a1168a6e3..3a8846b3747 100644
--- a/app/workers/pipeline_notification_worker.rb
+++ b/app/workers/pipeline_notification_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineNotificationWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb
index 24424b3f472..83744c5338a 100644
--- a/app/workers/pipeline_process_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineProcessWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/pipeline_schedule_worker.rb b/app/workers/pipeline_schedule_worker.rb
index c49758878a4..a1815757735 100644
--- a/app/workers/pipeline_schedule_worker.rb
+++ b/app/workers/pipeline_schedule_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineScheduleWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb
index 2ab0739a17f..68e9af6a619 100644
--- a/app/workers/pipeline_success_worker.rb
+++ b/app/workers/pipeline_success_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineSuccessWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb
index fc9da2d45b1..c33468c1f14 100644
--- a/app/workers/pipeline_update_worker.rb
+++ b/app/workers/pipeline_update_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelineUpdateWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/plugin_worker.rb b/app/workers/plugin_worker.rb
index bfcc683d99a..c293e28be4a 100644
--- a/app/workers/plugin_worker.rb
+++ b/app/workers/plugin_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PluginWorker
include ApplicationWorker
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index f88b3fdbfb1..09a594cdb4e 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PostReceive
include ApplicationWorker
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index 201e7f332b4..ed39b4a1ea8 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Worker for processing individiual commit messages pushed to a repository.
#
# Jobs for this worker are scheduled for every commit that is being pushed. As a
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index a993b4b2680..b0e1d8837d9 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Worker for updating any project specific caches.
class ProjectCacheWorker
include ApplicationWorker
diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb
index 1ba854ca4cb..4447e867240 100644
--- a/app/workers/project_destroy_worker.rb
+++ b/app/workers/project_destroy_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectDestroyWorker
include ApplicationWorker
include ExceptionBacktrace
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
index c3d84bb0b93..ed9da39c7c3 100644
--- a/app/workers/project_export_worker.rb
+++ b/app/workers/project_export_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectExportWorker
include ApplicationWorker
include ExceptionBacktrace
diff --git a/app/workers/project_migrate_hashed_storage_worker.rb b/app/workers/project_migrate_hashed_storage_worker.rb
index d01eb744e5d..9e4d66250a4 100644
--- a/app/workers/project_migrate_hashed_storage_worker.rb
+++ b/app/workers/project_migrate_hashed_storage_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectMigrateHashedStorageWorker
include ApplicationWorker
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
index 75c4b8b3663..a0bc9288cf0 100644
--- a/app/workers/project_service_worker.rb
+++ b/app/workers/project_service_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectServiceWorker
include ApplicationWorker
diff --git a/app/workers/propagate_service_template_worker.rb b/app/workers/propagate_service_template_worker.rb
index 635a97c99af..c9da1cae255 100644
--- a/app/workers/propagate_service_template_worker.rb
+++ b/app/workers/propagate_service_template_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Worker for updating any project specific caches.
class PropagateServiceTemplateWorker
include ApplicationWorker
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index 5ff62ab1369..c1d05ebbcfd 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PruneOldEventsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/reactive_caching_worker.rb b/app/workers/reactive_caching_worker.rb
index ef3ddb9024b..9b331f15dc5 100644
--- a/app/workers/reactive_caching_worker.rb
+++ b/app/workers/reactive_caching_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ReactiveCachingWorker
include ApplicationWorker
diff --git a/app/workers/rebase_worker.rb b/app/workers/rebase_worker.rb
index 090987778a2..a6baebc1443 100644
--- a/app/workers/rebase_worker.rb
+++ b/app/workers/rebase_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RebaseWorker
include ApplicationWorker
diff --git a/app/workers/remove_expired_group_links_worker.rb b/app/workers/remove_expired_group_links_worker.rb
index 7e64c3070a8..6b8b972a440 100644
--- a/app/workers/remove_expired_group_links_worker.rb
+++ b/app/workers/remove_expired_group_links_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RemoveExpiredGroupLinksWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/remove_expired_members_worker.rb b/app/workers/remove_expired_members_worker.rb
index 68960f72bf6..41913900571 100644
--- a/app/workers/remove_expired_members_worker.rb
+++ b/app/workers/remove_expired_members_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RemoveExpiredMembersWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/remove_old_web_hook_logs_worker.rb b/app/workers/remove_old_web_hook_logs_worker.rb
index 87fed42d7ce..17140ac4450 100644
--- a/app/workers/remove_old_web_hook_logs_worker.rb
+++ b/app/workers/remove_old_web_hook_logs_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RemoveOldWebHookLogsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/remove_unreferenced_lfs_objects_worker.rb b/app/workers/remove_unreferenced_lfs_objects_worker.rb
index 8daf079fc31..95e7a9f537f 100644
--- a/app/workers/remove_unreferenced_lfs_objects_worker.rb
+++ b/app/workers/remove_unreferenced_lfs_objects_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RemoveUnreferencedLfsObjectsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb
index 86a258cf94f..c1dff8ced90 100644
--- a/app/workers/repository_archive_cache_worker.rb
+++ b/app/workers/repository_archive_cache_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryArchiveCacheWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index 72f0a9b0619..898bca976ec 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepositoryCheck
class BatchWorker
include ApplicationWorker
diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb
index 97b89dc3db5..81e1a4b63bb 100644
--- a/app/workers/repository_check/clear_worker.rb
+++ b/app/workers/repository_check/clear_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepositoryCheck
class ClearWorker
include ApplicationWorker
diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb
index 3cffb8b14e4..f44e5693b25 100644
--- a/app/workers/repository_check/single_repository_worker.rb
+++ b/app/workers/repository_check/single_repository_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepositoryCheck
class SingleRepositoryWorker
include ApplicationWorker
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index dbb215f1964..5ef9b744db3 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryForkWorker
include ApplicationWorker
include Gitlab::ShellAdapter
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index d79b5ee5346..25fec542ac7 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryImportWorker
include ApplicationWorker
include ExceptionBacktrace
diff --git a/app/workers/repository_remove_remote_worker.rb b/app/workers/repository_remove_remote_worker.rb
index 1c19b604b77..a85e9fa9394 100644
--- a/app/workers/repository_remove_remote_worker.rb
+++ b/app/workers/repository_remove_remote_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryRemoveRemoteWorker
include ApplicationWorker
include ExclusiveLeaseGuard
diff --git a/app/workers/repository_update_remote_mirror_worker.rb b/app/workers/repository_update_remote_mirror_worker.rb
index bb963979e88..9d4e67deb9c 100644
--- a/app/workers/repository_update_remote_mirror_worker.rb
+++ b/app/workers/repository_update_remote_mirror_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryUpdateRemoteMirrorWorker
UpdateAlreadyInProgressError = Class.new(StandardError)
UpdateError = Class.new(StandardError)
diff --git a/app/workers/requests_profiles_worker.rb b/app/workers/requests_profiles_worker.rb
index 55c236e9e9d..ae022d43e29 100644
--- a/app/workers/requests_profiles_worker.rb
+++ b/app/workers/requests_profiles_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RequestsProfilesWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index 8f5138fc873..1f6cb18c812 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RunPipelineScheduleWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/schedule_update_user_activity_worker.rb b/app/workers/schedule_update_user_activity_worker.rb
index d9376577597..ff42fb8f0e5 100644
--- a/app/workers/schedule_update_user_activity_worker.rb
+++ b/app/workers/schedule_update_user_activity_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ScheduleUpdateUserActivityWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/stage_update_worker.rb b/app/workers/stage_update_worker.rb
index e4b683fca33..ec8c8e3689f 100644
--- a/app/workers/stage_update_worker.rb
+++ b/app/workers/stage_update_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StageUpdateWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/storage_migrator_worker.rb b/app/workers/storage_migrator_worker.rb
index 0aff0c4c7c6..fa76fbac55c 100644
--- a/app/workers/storage_migrator_worker.rb
+++ b/app/workers/storage_migrator_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StorageMigratorWorker
include ApplicationWorker
diff --git a/app/workers/stuck_ci_jobs_worker.rb b/app/workers/stuck_ci_jobs_worker.rb
index 7ebf69bdc39..c78b7fac589 100644
--- a/app/workers/stuck_ci_jobs_worker.rb
+++ b/app/workers/stuck_ci_jobs_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StuckCiJobsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/stuck_import_jobs_worker.rb b/app/workers/stuck_import_jobs_worker.rb
index 6fdd7592e74..79ce06dd66e 100644
--- a/app/workers/stuck_import_jobs_worker.rb
+++ b/app/workers/stuck_import_jobs_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StuckImportJobsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb
index 16394293c79..b0a62f76e94 100644
--- a/app/workers/stuck_merge_jobs_worker.rb
+++ b/app/workers/stuck_merge_jobs_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StuckMergeJobsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/system_hook_push_worker.rb b/app/workers/system_hook_push_worker.rb
index ceeaaf8d189..15e369ebcfb 100644
--- a/app/workers/system_hook_push_worker.rb
+++ b/app/workers/system_hook_push_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SystemHookPushWorker
include ApplicationWorker
diff --git a/app/workers/trending_projects_worker.rb b/app/workers/trending_projects_worker.rb
index 7eb65452a7d..3297a1fe3d0 100644
--- a/app/workers/trending_projects_worker.rb
+++ b/app/workers/trending_projects_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TrendingProjectsWorker
include ApplicationWorker
include CronjobQueue
diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb
index 76f84ff920f..0487a393566 100644
--- a/app/workers/update_head_pipeline_for_merge_request_worker.rb
+++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateHeadPipelineForMergeRequestWorker
include ApplicationWorker
include PipelineQueue
diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb
index 74bb9993275..742841219b3 100644
--- a/app/workers/update_merge_requests_worker.rb
+++ b/app/workers/update_merge_requests_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateMergeRequestsWorker
include ApplicationWorker
diff --git a/app/workers/update_user_activity_worker.rb b/app/workers/update_user_activity_worker.rb
index 27ec5cd33fb..15f01a70337 100644
--- a/app/workers/update_user_activity_worker.rb
+++ b/app/workers/update_user_activity_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateUserActivityWorker
include ApplicationWorker
diff --git a/app/workers/upload_checksum_worker.rb b/app/workers/upload_checksum_worker.rb
index 65d40336f18..2a0536106d7 100644
--- a/app/workers/upload_checksum_worker.rb
+++ b/app/workers/upload_checksum_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UploadChecksumWorker
include ApplicationWorker
diff --git a/app/workers/wait_for_cluster_creation_worker.rb b/app/workers/wait_for_cluster_creation_worker.rb
index 19cdb279aaa..8aa1d9290fd 100644
--- a/app/workers/wait_for_cluster_creation_worker.rb
+++ b/app/workers/wait_for_cluster_creation_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WaitForClusterCreationWorker
include ApplicationWorker
include ClusterQueue
diff --git a/app/workers/web_hook_worker.rb b/app/workers/web_hook_worker.rb
index dfc3f33ad9d..09219a24a16 100644
--- a/app/workers/web_hook_worker.rb
+++ b/app/workers/web_hook_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WebHookWorker
include ApplicationWorker
diff --git a/changelogs/unreleased/40484-ordered-lists-copy-gfm.yml b/changelogs/unreleased/40484-ordered-lists-copy-gfm.yml
new file mode 100644
index 00000000000..f4b34909ae9
--- /dev/null
+++ b/changelogs/unreleased/40484-ordered-lists-copy-gfm.yml
@@ -0,0 +1,5 @@
+---
+title: Keep lists ordered when copying only list items
+merge_request: 18522
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/45703-open-web-ide-file-tree.yml b/changelogs/unreleased/45703-open-web-ide-file-tree.yml
new file mode 100644
index 00000000000..abee9cad2d5
--- /dev/null
+++ b/changelogs/unreleased/45703-open-web-ide-file-tree.yml
@@ -0,0 +1,5 @@
+---
+title: Update WebIDE to show file in tree on load
+merge_request: 19887
+author:
+type: changed
diff --git a/changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml b/changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml
new file mode 100644
index 00000000000..7d42d971022
--- /dev/null
+++ b/changelogs/unreleased/46546-do-not-pre-select-previous-user-s-when-creating-protected-branches.yml
@@ -0,0 +1,5 @@
+---
+title: CE port gitlab-ee!6112
+merge_request: 19714
+author:
+type: other
diff --git a/changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml b/changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml
new file mode 100644
index 00000000000..d5ecf5163d4
--- /dev/null
+++ b/changelogs/unreleased/46783-removed-omniauth-provider-causing-invalid-application-setting.yml
@@ -0,0 +1,5 @@
+---
+title: Ignore unknown OAuth sources in ApplicationSetting
+merge_request: 20129
+author:
+type: fixed
diff --git a/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml b/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml
new file mode 100644
index 00000000000..e0e2b481b69
--- /dev/null
+++ b/changelogs/unreleased/46831-remove-unused-bootstrap-component-css.yml
@@ -0,0 +1,5 @@
+---
+title: Removes unused bootstrap 4 scss files
+merge_request: 19423
+author:
+type: deprecated
diff --git a/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml b/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml
new file mode 100644
index 00000000000..94c58a3863a
--- /dev/null
+++ b/changelogs/unreleased/47221-explain-what-groups-are-in-the-new-group-page.yml
@@ -0,0 +1,5 @@
+---
+title: Update new group page to better explain what groups are
+merge_request: 19991
+author:
+type: other
diff --git a/changelogs/unreleased/47794-environment-scope-cluster-page.yml b/changelogs/unreleased/47794-environment-scope-cluster-page.yml
new file mode 100644
index 00000000000..75eb7ec209c
--- /dev/null
+++ b/changelogs/unreleased/47794-environment-scope-cluster-page.yml
@@ -0,0 +1,6 @@
+---
+title: Change environment scope text depending on number of project clusters. Update
+ form to only include form-groups
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/48126-fix-prometheus-installation.yml b/changelogs/unreleased/48126-fix-prometheus-installation.yml
deleted file mode 100644
index e6ab9c46fbf..00000000000
--- a/changelogs/unreleased/48126-fix-prometheus-installation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Specify chart version when installing applications on Clusters
-merge_request: 20010
-author:
-type: fixed
diff --git a/changelogs/unreleased/48339-sorting-by-name-on-explore-projects-page-renders-a-500-error-when-logged-in.yml b/changelogs/unreleased/48339-sorting-by-name-on-explore-projects-page-renders-a-500-error-when-logged-in.yml
deleted file mode 100644
index 933d82b57c5..00000000000
--- a/changelogs/unreleased/48339-sorting-by-name-on-explore-projects-page-renders-a-500-error-when-logged-in.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix sorting by name on explore projects page
-merge_request: 20162
-author:
-type: fixed
diff --git a/changelogs/unreleased/48378-avatar-upload.yml b/changelogs/unreleased/48378-avatar-upload.yml
new file mode 100644
index 00000000000..1e359ee72d5
--- /dev/null
+++ b/changelogs/unreleased/48378-avatar-upload.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes issue with uploading same image to Profile Avatar twice
+merge_request: 20161
+author: Chirag Bhatia
+type: fixed
diff --git a/changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml b/changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml
new file mode 100644
index 00000000000..2ebc22dbf8f
--- /dev/null
+++ b/changelogs/unreleased/48461-search-dropdown-hides-shows-when-typing.yml
@@ -0,0 +1,5 @@
+---
+title: Fix loading screen for search autocomplete dropdown
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/48528-fix-mr-autocompletion.yml b/changelogs/unreleased/48528-fix-mr-autocompletion.yml
new file mode 100644
index 00000000000..ac44f878d1d
--- /dev/null
+++ b/changelogs/unreleased/48528-fix-mr-autocompletion.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken '!' support to autocomplete MRs in GFM fields
+merge_request: 20204
+author:
+type: fixed
diff --git a/changelogs/unreleased/6591-dont-load-omniauth-if-not-enabled.yml b/changelogs/unreleased/6591-dont-load-omniauth-if-not-enabled.yml
deleted file mode 100644
index dd1c7e6955d..00000000000
--- a/changelogs/unreleased/6591-dont-load-omniauth-if-not-enabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Only load Omniauth if enabled
-merge_request: 20132
-author:
-type: fixed
diff --git a/changelogs/unreleased/6598-notify-only-open-unmergeable-mr.yml b/changelogs/unreleased/6598-notify-only-open-unmergeable-mr.yml
deleted file mode 100644
index ae92c20fa1a..00000000000
--- a/changelogs/unreleased/6598-notify-only-open-unmergeable-mr.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Notify conflict for only open merge request
-merge_request: 20125
-author:
-type: fixed
diff --git a/changelogs/unreleased/add-missing-index-for-deployments.yml b/changelogs/unreleased/add-missing-index-for-deployments.yml
new file mode 100644
index 00000000000..7863c0ee039
--- /dev/null
+++ b/changelogs/unreleased/add-missing-index-for-deployments.yml
@@ -0,0 +1,5 @@
+---
+title: Add index on deployable_type/id for deployments
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/bvl-dont-generate-mo.yml b/changelogs/unreleased/bvl-dont-generate-mo.yml
deleted file mode 100644
index 19b8e873849..00000000000
--- a/changelogs/unreleased/bvl-dont-generate-mo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix invalid fuzzy translations being generated during installation
-merge_request: 20048
-author:
-type: fixed
diff --git a/changelogs/unreleased/enforce-variable-value-to-be-a-string.yml b/changelogs/unreleased/enforce-variable-value-to-be-a-string.yml
deleted file mode 100644
index e2a932ee5bb..00000000000
--- a/changelogs/unreleased/enforce-variable-value-to-be-a-string.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix incremental rollouts for Auto DevOps
-merge_request: 20061
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-alert-btn.yml b/changelogs/unreleased/fix-alert-btn.yml
deleted file mode 100644
index d8bf561f05a..00000000000
--- a/changelogs/unreleased/fix-alert-btn.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix alert button styling so that they don't show up white
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-favicon-cross-origin.yml b/changelogs/unreleased/fix-favicon-cross-origin.yml
deleted file mode 100644
index 3317781e222..00000000000
--- a/changelogs/unreleased/fix-favicon-cross-origin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Serve favicon image always from the main GitLab domain to avoid issues with CORS
-merge_request: 19810
-author: Alexis Reigel
-type: fixed
diff --git a/changelogs/unreleased/frozen-string-app-workers.yml b/changelogs/unreleased/frozen-string-app-workers.yml
new file mode 100644
index 00000000000..48b50cc6ca4
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-app-workers.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/workers/*.rb
+merge_request: 19944
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/frozen-string-enable-app-workers-2.yml b/changelogs/unreleased/frozen-string-enable-app-workers-2.yml
new file mode 100644
index 00000000000..81de6899d76
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-workers-2.yml
@@ -0,0 +1,5 @@
+---
+title: Finish enabling frozen string for app/workers/*.rb
+merge_request: 20197
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/mk-rake-task-verify-remote-files.yml b/changelogs/unreleased/mk-rake-task-verify-remote-files.yml
deleted file mode 100644
index 772aa11d89b..00000000000
--- a/changelogs/unreleased/mk-rake-task-verify-remote-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for verifying remote uploads, artifacts, and LFS objects in check rake tasks
-merge_request: 19501
-author:
-type: added
diff --git a/changelogs/unreleased/prefer-destructuring-fix.yml b/changelogs/unreleased/prefer-destructuring-fix.yml
new file mode 100644
index 00000000000..452e04f553e
--- /dev/null
+++ b/changelogs/unreleased/prefer-destructuring-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Enable prefer-structuring in JS files
+merge_request: 19943
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/rails5-fix-mysql-arel-from.yml b/changelogs/unreleased/rails5-fix-mysql-arel-from.yml
new file mode 100644
index 00000000000..9883ff306f1
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-mysql-arel-from.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix arel from in mysql_median_datetime_sql
+merge_request: 20167
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/revert-merge-request-widget-button-height.yml b/changelogs/unreleased/revert-merge-request-widget-button-height.yml
new file mode 100644
index 00000000000..7c400a4a2b2
--- /dev/null
+++ b/changelogs/unreleased/revert-merge-request-widget-button-height.yml
@@ -0,0 +1,5 @@
+---
+title: Revert merge request widget button max height
+merge_request: 20175
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml b/changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml
new file mode 100644
index 00000000000..ee769f06379
--- /dev/null
+++ b/changelogs/unreleased/update-external-link-icon-in-header-user-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Update external link icon in header user dropdown
+merge_request: 20150
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml b/changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml
new file mode 100644
index 00000000000..3f1f3c643e2
--- /dev/null
+++ b/changelogs/unreleased/update-pipeline-icon-in-web-ide-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Update pipeline icon in web ide sidebar
+merge_request: 20058
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/zj-gitaly-read-write-check.yml b/changelogs/unreleased/zj-gitaly-read-write-check.yml
new file mode 100644
index 00000000000..43951d20e8f
--- /dev/null
+++ b/changelogs/unreleased/zj-gitaly-read-write-check.yml
@@ -0,0 +1,5 @@
+---
+title: Gitaly metrics check for read/writeability
+merge_request: 20022
+author:
+type: other
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
index 889111282ef..9f451046462 100644
--- a/config/locales/doorkeeper.en.yml
+++ b/config/locales/doorkeeper.en.yml
@@ -60,17 +60,23 @@ en:
scopes:
api: Access the authenticated user's API
read_user: Read the authenticated user's personal information
+ read_repository: Allows read-access to the repository
+ read_registry: Grants permission to read container registry images
openid: Authenticate using OpenID Connect
- sudo: Perform API actions as any user in the system (if the authenticated user is an admin)
+ sudo: Perform API actions as any user in the system
scope_desc:
api:
- Full access to GitLab as the user, including read/write on all their groups and projects
+ Grants complete read/write access to the API, including all groups and projects.
read_user:
- Read-only access to the user's profile information, like username, public email and full name
+ Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.
+ read_repository:
+ Grants read-only access to repositories on private projects using Git-over-HTTP (not using the API).
+ read_registry:
+ Grants read-only access to container registry images on private projects.
openid:
- The ability to authenticate using GitLab, and read-only access to the user's profile information and group memberships
+ Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships.
sudo:
- Access to the Sudo feature, to perform API actions as any user in the system (only available for admins)
+ Grants permission to perform API actions as any user in the system, when authenticated as an admin user.
flash:
applications:
create:
diff --git a/db/migrate/20180626125654_add_index_on_deployable_for_deployments.rb b/db/migrate/20180626125654_add_index_on_deployable_for_deployments.rb
new file mode 100644
index 00000000000..a0e3a228f6c
--- /dev/null
+++ b/db/migrate/20180626125654_add_index_on_deployable_for_deployments.rb
@@ -0,0 +1,18 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddIndexOnDeployableForDeployments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :deployments, [:deployable_type, :deployable_id]
+ end
+
+ def down
+ remove_concurrent_index :deployments, [:deployable_type, :deployable_id]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d05c6afbb9f..0112fc726d4 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: 20180608201435) do
+ActiveRecord::Schema.define(version: 20180626125654) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -757,6 +757,7 @@ ActiveRecord::Schema.define(version: 20180608201435) do
end
add_index "deployments", ["created_at"], name: "index_deployments_on_created_at", using: :btree
+ add_index "deployments", ["deployable_type", "deployable_id"], name: "index_deployments_on_deployable_type_and_deployable_id", using: :btree
add_index "deployments", ["environment_id", "id"], name: "index_deployments_on_environment_id_and_id", using: :btree
add_index "deployments", ["environment_id", "iid", "project_id"], name: "index_deployments_on_environment_id_and_iid_and_project_id", using: :btree
add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 4091c691a3c..9d0d7348df9 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -32,6 +32,7 @@ are very appreciative of the work done by translators and proofreaders!
- Filip Mech - [GitLab](https://gitlab.com/mehenz), [Crowdin](https://crowdin.com/profile/mehenz)
- Portuguese, Brazilian
- Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [Crowdin](https://crowdin.com/profile/paulogomes.rep)
+ - André Gama - [GitLab](https://gitlab.com/andregamma), [Crowdin](https://crowdin.com/profile/ToeOficial)
- Russian
- Nikita Grylov - [GitLab](https://gitlab.com/nixel2007), [Crowdin](https://crowdin.com/profile/nixel2007)
- Alexy Lustin - [GitLab](https://gitlab.com/allustin), [Crowdin](https://crowdin.com/profile/lustin)
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index f502866333e..47396666879 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -195,22 +195,22 @@ end
And that's it, we're done!
-## Changing Column Types For Large Tables
+## Changing The Schema For Large Tables
-While `change_column_type_concurrently` can be used for changing the type of a
-column without downtime it doesn't work very well for large tables. Because all
-of the work happens in sequence the migration can take a very long time to
-complete, preventing a deployment from proceeding.
-`change_column_type_concurrently` can also produce a lot of pressure on the
-database due to it rapidly updating many rows in sequence.
+While `change_column_type_concurrently` and `rename_column_concurrently` can be
+used for changing the schema of a table without downtime it doesn't work very
+well for large tables. Because all of the work happens in sequence the migration
+can take a very long time to complete, preventing a deployment from proceeding.
+They can also produce a lot of pressure on the database due to it rapidly
+updating many rows in sequence.
To reduce database pressure you should instead use
-`change_column_type_using_background_migration` when migrating a column in a
-large table (e.g. `issues`). This method works similar to
-`change_column_type_concurrently` but uses background migration to spread the
-work / load over a longer time period, without slowing down deployments.
+`change_column_type_using_background_migration` or `rename_column_concurrently`
+when migrating a column in a large table (e.g. `issues`). These methods work
+similarly to the concurrent counterparts but uses background migration to spread
+the work / load over a longer time period, without slowing down deployments.
-Usage of this method is fairly simple:
+For example, to change the column type using a background migration:
```ruby
class ExampleMigration < ActiveRecord::Migration
@@ -296,6 +296,15 @@ class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration
end
```
+The same applies to `rename_column_using_background_migration`:
+
+1. Create a migration using the helper, which will schedule background
+ migrations to spread the writes over a longer period of time.
+2. In the next monthly release, create a clean-up migration to steal from the
+ Sidekiq queues, migrate any missing rows, and cleanup the rename. This
+ migration should skip the steps after stealing from the Sidekiq queues if the
+ column has already been renamed.
+
For more information, see [the documentation on cleaning up background
migrations](background_migrations.md#cleaning-up).
diff --git a/doc/install/installation.md b/doc/install/installation.md
index ef415246583..e4011b1a4ab 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -12,9 +12,8 @@ Since installations from source don't have Runit, Sidekiq can't be terminated an
## Select Version to Install
-Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the tag (version) of GitLab you would like to install.
-In most cases this should be the highest numbered production tag (without rc in it).
-You can select the tag in the version dropdown in the top left corner of GitLab (below the menu bar).
+Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install (e.g., `11-0-stable`).
+You can select the branch in the version dropdown in the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the [GitLab Blog](https://about.gitlab.com/blog/) for installation guide links by version.
diff --git a/doc/update/10.8-to-11.0.md b/doc/update/10.8-to-11.0.md
index f9b6044bd2f..22a0c9f950c 100644
--- a/doc/update/10.8-to-11.0.md
+++ b/doc/update/10.8-to-11.0.md
@@ -4,10 +4,9 @@ comments: false
# From 10.8 to 11.0
-Make sure you view this update guide from the tag (version) of GitLab you would
-like to install. In most cases this should be the highest numbered production
-tag (without rc in it). You can select the tag in the version dropdown at the
-top left corner of GitLab (below the menu bar).
+Make sure you view this update guide from the branch (version) of GitLab you would
+like to install (e.g., `11-0-stable`. You can select the branch in the version
+dropdown at the top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the
[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 13cfba728fa..4b223a391ae 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -45,6 +45,7 @@ module API
present(
paginate(::Kaminari.paginate_array(branches)),
with: Entities::Branch,
+ current_user: current_user,
project: user_project,
merged_branch_names: merged_branch_names
)
@@ -63,7 +64,7 @@ module API
get do
branch = find_branch!(params[:branch])
- present branch, with: Entities::Branch, project: user_project
+ present branch, with: Entities::Branch, current_user: current_user, project: user_project
end
end
@@ -101,7 +102,7 @@ module API
end
if protected_branch.valid?
- present branch, with: Entities::Branch, project: user_project
+ present branch, with: Entities::Branch, current_user: current_user, project: user_project
else
render_api_error!(protected_branch.errors.full_messages, 422)
end
@@ -121,7 +122,7 @@ module API
protected_branch = user_project.protected_branches.find_by(name: branch.name)
protected_branch&.destroy
- present branch, with: Entities::Branch, project: user_project
+ present branch, with: Entities::Branch, current_user: current_user, project: user_project
end
desc 'Create branch' do
@@ -140,6 +141,7 @@ module API
if result[:status] == :success
present result[:branch],
with: Entities::Branch,
+ current_user: current_user,
project: user_project
else
render_api_error!(result[:message], 400)
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 50a5e340191..af762db517c 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -48,7 +48,7 @@ module Backup
end
def backup_project(project)
- gitaly_migrate(:repository_backup) do |is_enabled|
+ gitaly_migrate(:repository_backup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
backup_project_gitaly(project)
else
@@ -80,7 +80,7 @@ module Backup
end
def delete_all_repositories(name, repository_storage)
- gitaly_migrate(:delete_all_repositories) do |is_enabled|
+ gitaly_migrate(:delete_all_repositories, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories
else
@@ -148,7 +148,7 @@ module Backup
end
def backup_custom_hooks(project)
- gitaly_migrate(:backup_custom_hooks) do |is_enabled|
+ gitaly_migrate(:backup_custom_hooks, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_backup_custom_hooks(project)
else
@@ -159,7 +159,7 @@ module Backup
def restore_custom_hooks(project)
in_path(path_to_tars(project)) do |dir|
- gitaly_migrate(:restore_custom_hooks) do |is_enabled|
+ gitaly_migrate(:restore_custom_hooks, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_restore_custom_hooks(project, dir)
else
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index 4bc82ecb4d6..bb9f488cd87 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -56,10 +56,12 @@ module Banzai
# Pattern to match allowed image extensions
ALLOWED_IMAGE_EXTENSIONS = /.+(jpg|png|gif|svg|bmp)\z/i.freeze
+ # Do not perform linking inside these tags.
+ IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set
+
def call
doc.search(".//text()").each do |node|
- # Do not perform linking inside <code> blocks
- next unless node.ancestors('code').empty?
+ next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
# A Gollum ToC tag is `[[_TOC_]]`, but due to MarkdownFilter running
# before this one, it will be converted into `[[<em>TOC</em>]]`, so it
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
index 605e93022e7..2760211fee8 100644
--- a/lib/gitaly/server.rb
+++ b/lib/gitaly/server.rb
@@ -22,6 +22,18 @@ module Gitaly
server_version == Gitlab::GitalyClient.expected_server_version
end
+ def read_writeable?
+ readable? && writeable?
+ end
+
+ def readable?
+ storage_status&.readable
+ end
+
+ def writeable?
+ storage_status&.writeable
+ end
+
def address
Gitlab::GitalyClient.address(@storage)
rescue RuntimeError => e
@@ -30,13 +42,17 @@ module Gitaly
private
+ def storage_status
+ @storage_status ||= info.storage_statuses.find { |s| s.storage_name == storage }
+ end
+
def info
@info ||=
begin
Gitlab::GitalyClient::ServerService.new(@storage).info
rescue GRPC::Unavailable, GRPC::GRPC::DeadlineExceeded
# This will show the server as being out of date
- Gitaly::ServerInfoResponse.new(git_version: '', server_version: '')
+ Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
end
end
end
diff --git a/lib/gitlab/background_migration/cleanup_concurrent_rename.rb b/lib/gitlab/background_migration/cleanup_concurrent_rename.rb
new file mode 100644
index 00000000000..d3f366f3480
--- /dev/null
+++ b/lib/gitlab/background_migration/cleanup_concurrent_rename.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Background migration for cleaning up a concurrent column rename.
+ class CleanupConcurrentRename < CleanupConcurrentSchemaChange
+ RESCHEDULE_DELAY = 10.minutes
+
+ def cleanup_concurrent_schema_change(table, old_column, new_column)
+ cleanup_concurrent_column_rename(table, old_column, new_column)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb b/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb
new file mode 100644
index 00000000000..54f77f184d5
--- /dev/null
+++ b/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Base class for cleaning up concurrent schema changes.
+ class CleanupConcurrentSchemaChange
+ include Database::MigrationHelpers
+
+ # table - The name of the table the migration is performed for.
+ # old_column - The name of the old (to drop) column.
+ # new_column - The name of the new column.
+ def perform(table, old_column, new_column)
+ return unless column_exists?(table, new_column)
+
+ rows_to_migrate = define_model_for(table)
+ .where(new_column => nil)
+ .where
+ .not(old_column => nil)
+
+ if rows_to_migrate.any?
+ BackgroundMigrationWorker.perform_in(
+ RESCHEDULE_DELAY,
+ self.class.name,
+ [table, old_column, new_column]
+ )
+ else
+ cleanup_concurrent_schema_change(table, old_column, new_column)
+ end
+ end
+
+ # These methods are necessary so we can re-use the migration helpers in
+ # this class.
+ def connection
+ ActiveRecord::Base.connection
+ end
+
+ def method_missing(name, *args, &block)
+ connection.__send__(name, *args, &block) # rubocop: disable GitlabSecurity/PublicSend
+ end
+
+ def respond_to_missing?(*args)
+ connection.respond_to?(*args) || super
+ end
+
+ def define_model_for(table)
+ Class.new(ActiveRecord::Base) do
+ self.table_name = table
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/cleanup_concurrent_type_change.rb b/lib/gitlab/background_migration/cleanup_concurrent_type_change.rb
index de622f657b2..48411095dbb 100644
--- a/lib/gitlab/background_migration/cleanup_concurrent_type_change.rb
+++ b/lib/gitlab/background_migration/cleanup_concurrent_type_change.rb
@@ -2,52 +2,12 @@
module Gitlab
module BackgroundMigration
- # Background migration for cleaning up a concurrent column rename.
- class CleanupConcurrentTypeChange
- include Database::MigrationHelpers
-
+ # Background migration for cleaning up a concurrent column type changeb.
+ class CleanupConcurrentTypeChange < CleanupConcurrentSchemaChange
RESCHEDULE_DELAY = 10.minutes
- # table - The name of the table the migration is performed for.
- # old_column - The name of the old (to drop) column.
- # new_column - The name of the new column.
- def perform(table, old_column, new_column)
- return unless column_exists?(:issues, new_column)
-
- rows_to_migrate = define_model_for(table)
- .where(new_column => nil)
- .where
- .not(old_column => nil)
-
- if rows_to_migrate.any?
- BackgroundMigrationWorker.perform_in(
- RESCHEDULE_DELAY,
- 'CleanupConcurrentTypeChange',
- [table, old_column, new_column]
- )
- else
- cleanup_concurrent_column_type_change(table, old_column)
- end
- end
-
- # These methods are necessary so we can re-use the migration helpers in
- # this class.
- def connection
- ActiveRecord::Base.connection
- end
-
- def method_missing(name, *args, &block)
- connection.__send__(name, *args, &block) # rubocop: disable GitlabSecurity/PublicSend
- end
-
- def respond_to_missing?(*args)
- connection.respond_to?(*args) || super
- end
-
- def define_model_for(table)
- Class.new(ActiveRecord::Base) do
- self.table_name = table
- end
+ def cleanup_concurrent_schema_change(table, old_column, new_column)
+ cleanup_concurrent_column_type_change(table, old_column)
end
end
end
diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb
index 3cac007a42c..f64e3d53138 100644
--- a/lib/gitlab/database/median.rb
+++ b/lib/gitlab/database/median.rb
@@ -33,7 +33,13 @@ module Gitlab
end
def mysql_median_datetime_sql(arel_table, query_so_far, column_sym)
- query = arel_table
+ arel_from = if Gitlab.rails5?
+ arel_table.from
+ else
+ arel_table
+ end
+
+ query = arel_from
.from(arel_table.project(Arel.sql('*')).order(arel_table[column_sym]).as(arel_table.table_name))
.project(average([arel_table[column_sym]], 'median'))
.where(
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index c21bae5e16b..4fe5b4cc835 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -596,6 +596,97 @@ module Gitlab
end
end
+ # Renames a column using a background migration.
+ #
+ # Because this method uses a background migration it's more suitable for
+ # large tables. For small tables it's better to use
+ # `rename_column_concurrently` since it can complete its work in a much
+ # shorter amount of time and doesn't rely on Sidekiq.
+ #
+ # Example usage:
+ #
+ # rename_column_using_background_migration(
+ # :users,
+ # :feed_token,
+ # :rss_token
+ # )
+ #
+ # table - The name of the database table containing the column.
+ #
+ # old - The old column name.
+ #
+ # new - The new column name.
+ #
+ # type - The type of the new column. If no type is given the old column's
+ # type is used.
+ #
+ # batch_size - The number of rows to schedule in a single background
+ # migration.
+ #
+ # interval - The time interval between every background migration.
+ def rename_column_using_background_migration(
+ table,
+ old_column,
+ new_column,
+ type: nil,
+ batch_size: 10_000,
+ interval: 10.minutes
+ )
+
+ check_trigger_permissions!(table)
+
+ old_col = column_for(table, old_column)
+ new_type = type || old_col.type
+ max_index = 0
+
+ add_column(table, new_column, new_type,
+ limit: old_col.limit,
+ precision: old_col.precision,
+ scale: old_col.scale)
+
+ # We set the default value _after_ adding the column so we don't end up
+ # updating any existing data with the default value. This isn't
+ # necessary since we copy over old values further down.
+ change_column_default(table, new_column, old_col.default) if old_col.default
+
+ install_rename_triggers(table, old_column, new_column)
+
+ model = Class.new(ActiveRecord::Base) do
+ self.table_name = table
+
+ include ::EachBatch
+ end
+
+ # Schedule the jobs that will copy the data from the old column to the
+ # new one. Rows with NULL values in our source column are skipped since
+ # the target column is already NULL at this point.
+ model.where.not(old_column => nil).each_batch(of: batch_size) do |batch, index|
+ start_id, end_id = batch.pluck('MIN(id), MAX(id)').first
+ max_index = index
+
+ BackgroundMigrationWorker.perform_in(
+ index * interval,
+ 'CopyColumn',
+ [table, old_column, new_column, start_id, end_id]
+ )
+ end
+
+ # Schedule the renaming of the column to happen (initially) 1 hour after
+ # the last batch finished.
+ BackgroundMigrationWorker.perform_in(
+ (max_index * interval) + 1.hour,
+ 'CleanupConcurrentRename',
+ [table, old_column, new_column]
+ )
+
+ if perform_background_migration_inline?
+ # To ensure the schema is up to date immediately we perform the
+ # migration inline in dev / test environments.
+ Gitlab::BackgroundMigration.steal('CopyColumn')
+ Gitlab::BackgroundMigration.steal('CleanupConcurrentRename')
+ end
+ end
+
def perform_background_migration_inline?
Rails.env.test? || Rails.env.development?
end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 341768752dc..74240cedc9d 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -493,13 +493,18 @@ module Gitlab
def tree_entry(path)
return unless path.present?
- @repository.gitaly_migrate(:commit_tree_entry) do |is_migrated|
- if is_migrated
- gitaly_tree_entry(path)
- else
- rugged_tree_entry(path)
- end
- end
+ # We're only interested in metadata, so limit actual data to 1 byte
+ # since Gitaly doesn't support "send no data" option.
+ entry = @repository.gitaly_commit_client.tree_entry(id, path, 1)
+ return unless entry
+
+ # To be compatible with the rugged format
+ entry = entry.to_h
+ entry.delete(:data)
+ entry[:name] = File.basename(path)
+ entry[:type] = entry[:type].downcase
+
+ entry
end
def to_gitaly_commit
@@ -562,28 +567,6 @@ module Gitlab
SERIALIZE_KEYS
end
- def gitaly_tree_entry(path)
- # We're only interested in metadata, so limit actual data to 1 byte
- # since Gitaly doesn't support "send no data" option.
- entry = @repository.gitaly_commit_client.tree_entry(id, path, 1)
- return unless entry
-
- # To be compatible with the rugged format
- entry = entry.to_h
- entry.delete(:data)
- entry[:name] = File.basename(path)
- entry[:type] = entry[:type].downcase
-
- entry
- end
-
- # Is this the same as Blob.find_entry_by_path ?
- def rugged_tree_entry(path)
- rugged_commit.tree.path(path)
- rescue Rugged::TreeError
- nil
- end
-
def gitaly_commit_author_from_rugged(author_or_committer)
Gitaly::CommitAuthor.new(
name: author_or_committer[:name].b,
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index b3016c1a637..88944cd62ea 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -684,6 +684,10 @@ module Gitlab
end
end
+ def update_branch(branch_name, user:, newrev:, oldrev:)
+ OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
+ end
+
def rm_branch(branch_name, user:)
gitaly_migrate(:operation_user_delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
@@ -1953,8 +1957,7 @@ module Gitlab
rebase_sha = run_git!(%w(rev-parse HEAD), chdir: rebase_path, env: env).strip
- Gitlab::Git::OperationService.new(user, self)
- .update_branch(branch, rebase_sha, branch_sha)
+ update_branch(branch, user: user, newrev: rebase_sha, oldrev: branch_sha)
rebase_sha
end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 7f2e6441f16..077297b9d6d 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -76,6 +76,13 @@ module Gitlab
end
def tree_entry(ref, path, limit = nil)
+ if Pathname.new(path).cleanpath.to_s.start_with?('../')
+ # The TreeEntry RPC should return an empty reponse in this case but in
+ # Gitaly 0.107.0 and earlier we get an exception instead. This early return
+ # saves us a Gitaly roundtrip while also avoiding the exception.
+ return
+ end
+
request = Gitaly::TreeEntryRequest.new(
repository: @gitaly_repo,
revision: encode_binary(ref),
diff --git a/lib/gitlab/health_checks/fs_shards_check.rb b/lib/gitlab/health_checks/fs_shards_check.rb
deleted file mode 100644
index 050fe7a5173..00000000000
--- a/lib/gitlab/health_checks/fs_shards_check.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-module Gitlab
- module HealthChecks
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1218
- class FsShardsCheck
- extend BaseAbstractCheck
- RANDOM_STRING = SecureRandom.hex(1000).freeze
- COMMAND_TIMEOUT = '1'.freeze
- TIMEOUT_EXECUTABLE = 'timeout'.freeze
-
- class << self
- def readiness
- repository_storages.map do |storage_name|
- begin
- if !storage_circuitbreaker_test(storage_name)
- HealthChecks::Result.new(false, 'circuitbreaker tripped', shard: storage_name)
- elsif !storage_stat_test(storage_name)
- HealthChecks::Result.new(false, 'cannot stat storage', shard: storage_name)
- else
- with_temp_file(storage_name) do |tmp_file_path|
- if !storage_write_test(tmp_file_path)
- HealthChecks::Result.new(false, 'cannot write to storage', shard: storage_name)
- elsif !storage_read_test(tmp_file_path)
- HealthChecks::Result.new(false, 'cannot read from storage', shard: storage_name)
- else
- HealthChecks::Result.new(true, nil, shard: storage_name)
- end
- end
- end
- rescue RuntimeError => ex
- message = "unexpected error #{ex} when checking storage #{storage_name}"
- Rails.logger.error(message)
- HealthChecks::Result.new(false, message, shard: storage_name)
- end
- end
- end
-
- def metrics
- repository_storages.flat_map do |storage_name|
- [
- storage_stat_metrics(storage_name),
- storage_write_metrics(storage_name),
- storage_read_metrics(storage_name),
- storage_circuitbreaker_metrics(storage_name)
- ].flatten
- end
- end
-
- private
-
- def operation_metrics(ok_metric, latency_metric, **labels)
- result, elapsed = yield
- [
- metric(latency_metric, elapsed, **labels),
- metric(ok_metric, result ? 1 : 0, **labels)
- ]
- rescue RuntimeError => ex
- Rails.logger.error("unexpected error #{ex} when checking #{ok_metric}")
- [metric(ok_metric, 0, **labels)]
- end
-
- def repository_storages
- storages_paths.keys
- end
-
- def storages_paths
- Gitlab.config.repositories.storages
- end
-
- def exec_with_timeout(cmd_args, *args, &block)
- Gitlab::Popen.popen([TIMEOUT_EXECUTABLE, COMMAND_TIMEOUT].concat(cmd_args), *args, &block)
- end
-
- def with_temp_file(storage_name)
- temp_file_path = Dir::Tmpname.create(%w(fs_shards_check +deleted), storage_path(storage_name)) { |path| path }
- yield temp_file_path
- ensure
- delete_test_file(temp_file_path)
- end
-
- def storage_path(storage_name)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- storages_paths[storage_name]&.legacy_disk_path
- end
- end
-
- # All below test methods use shell commands to perform actions on storage volumes.
- # In case a storage volume have connectivity problems causing pure Ruby IO operation to wait indefinitely,
- # we can rely on shell commands to be terminated once `timeout` kills them.
- #
- # However we also fallback to pure Ruby file operations in case a specific shell command is missing
- # so we are still able to perform healthchecks and gather metrics from such system.
-
- def delete_test_file(tmp_path)
- _, status = exec_with_timeout(%W{ rm -f #{tmp_path} })
- status.zero?
- rescue Errno::ENOENT
- File.delete(tmp_path) rescue Errno::ENOENT
- end
-
- def storage_stat_test(storage_name)
- stat_path = File.join(storage_path(storage_name), '.')
- begin
- _, status = exec_with_timeout(%W{ stat #{stat_path} })
- status.zero?
- rescue Errno::ENOENT
- File.exist?(stat_path) && File::Stat.new(stat_path).readable?
- end
- end
-
- def storage_write_test(tmp_path)
- _, status = exec_with_timeout(%W{ tee #{tmp_path} }) do |stdin|
- stdin.write(RANDOM_STRING)
- end
- status.zero?
- rescue Errno::ENOENT
- written_bytes = File.write(tmp_path, RANDOM_STRING) rescue Errno::ENOENT
- written_bytes == RANDOM_STRING.length
- end
-
- def storage_read_test(tmp_path)
- _, status = exec_with_timeout(%W{ diff #{tmp_path} - }) do |stdin|
- stdin.write(RANDOM_STRING)
- end
- status.zero?
- rescue Errno::ENOENT
- file_contents = File.read(tmp_path) rescue Errno::ENOENT
- file_contents == RANDOM_STRING
- end
-
- def storage_circuitbreaker_test(storage_name)
- Gitlab::Git::Storage::CircuitBreaker.build(storage_name).perform { "OK" }
- rescue Gitlab::Git::Storage::Inaccessible
- nil
- end
-
- def storage_stat_metrics(storage_name)
- operation_metrics(:filesystem_accessible, :filesystem_access_latency_seconds, shard: storage_name) do
- with_timing { storage_stat_test(storage_name) }
- end
- end
-
- def storage_write_metrics(storage_name)
- operation_metrics(:filesystem_writable, :filesystem_write_latency_seconds, shard: storage_name) do
- with_temp_file(storage_name) do |tmp_file_path|
- with_timing { storage_write_test(tmp_file_path) }
- end
- end
- end
-
- def storage_read_metrics(storage_name)
- operation_metrics(:filesystem_readable, :filesystem_read_latency_seconds, shard: storage_name) do
- with_temp_file(storage_name) do |tmp_file_path|
- storage_write_test(tmp_file_path) # writes data used by read test
- with_timing { storage_read_test(tmp_file_path) }
- end
- end
- end
-
- def storage_circuitbreaker_metrics(storage_name)
- operation_metrics(:filesystem_circuitbreaker,
- :filesystem_circuitbreaker_latency_seconds,
- shard: storage_name) do
- with_timing { storage_circuitbreaker_test(storage_name) }
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/gitaly_check.rb b/lib/gitlab/health_checks/gitaly_check.rb
index 11416c002e3..1f623e0b6ec 100644
--- a/lib/gitlab/health_checks/gitaly_check.rb
+++ b/lib/gitlab/health_checks/gitaly_check.rb
@@ -13,14 +13,14 @@ module Gitlab
end
def metrics
- repository_storages.flat_map do |storage_name|
- result, elapsed = with_timing { check(storage_name) }
- labels = { shard: storage_name }
+ Gitaly::Server.all.flat_map do |server|
+ result, elapsed = with_timing { server.read_writeable? }
+ labels = { shard: server.storage }
[
- metric("#{metric_prefix}_success", successful?(result) ? 1 : 0, **labels),
+ metric("#{metric_prefix}_success", result ? 1 : 0, **labels),
metric("#{metric_prefix}_latency_seconds", elapsed, **labels)
- ].flatten
+ ]
end
end
@@ -36,10 +36,6 @@ module Gitlab
METRIC_PREFIX
end
- def successful?(result)
- result[:success]
- end
-
def repository_storages
storages.keys
end
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index 4428e263bbb..a8558d7c50a 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -57,6 +57,10 @@ module QA
private
def within_project_deploy_keys
+ wait(reload: false) do
+ find_element(:project_deploy_keys)
+ end
+
within_element(:project_deploy_keys) do
yield
end
diff --git a/spec/controllers/health_controller_spec.rb b/spec/controllers/health_controller_spec.rb
index 542eddc2d16..d800ad7c187 100644
--- a/spec/controllers/health_controller_spec.rb
+++ b/spec/controllers/health_controller_spec.rb
@@ -69,8 +69,7 @@ describe HealthController do
expect(json_response['cache_check']['status']).to eq('ok')
expect(json_response['queues_check']['status']).to eq('ok')
expect(json_response['shared_state_check']['status']).to eq('ok')
- expect(json_response['fs_shards_check']['status']).to eq('ok')
- expect(json_response['fs_shards_check']['labels']['shard']).to eq('default')
+ expect(json_response['gitaly_check']['status']).to eq('ok')
end
end
@@ -122,7 +121,6 @@ describe HealthController do
expect(json_response['cache_check']['status']).to eq('ok')
expect(json_response['queues_check']['status']).to eq('ok')
expect(json_response['shared_state_check']['status']).to eq('ok')
- expect(json_response['fs_shards_check']['status']).to eq('ok')
end
end
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 9e8a37171ec..7376841fac8 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -59,6 +59,13 @@ describe MetricsController do
expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/)
end
+ it 'returns Gitaly metrics' do
+ get :index
+
+ expect(response.body).to match(/^gitaly_health_check_success{shard="default"} 1$/)
+ expect(response.body).to match(/^gitaly_health_check_latency_seconds{shard="default"} [0-9\.]+$/)
+ end
+
context 'prometheus metrics are disabled' do
before do
allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(false)
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index e7aca94db66..f3ab4ff771a 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -124,6 +124,29 @@ feature 'Admin updates settings' do
expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).not_to include('google_oauth2')
end
+ scenario 'Oauth providers do not raise validation errors when saving unrelated changes' do
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty
+
+ page.within('.as-signin') do
+ uncheck 'Google'
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
+
+ # Remove google_oauth2 from the Omniauth strategies
+ allow(Devise).to receive(:omniauth_providers).and_return([])
+
+ # Save an unrelated setting
+ page.within('.as-ci-cd') do
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2')
+ end
+
scenario 'Change Help page' do
page.within('.as-help-page') do
fill_in 'Help page text', with: 'Example text'
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 4c0f9971425..39bd4af6cd0 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -60,33 +60,6 @@ feature 'Protected Branches', :js do
expect(page).to have_content('No branches to show')
end
end
-
- describe "Saved defaults" do
- it "keeps the allowed to merge and push dropdowns defaults based on the previous selection" do
- visit project_protected_branches_path(project)
- form = '.js-new-protected-branch'
-
- within form do
- find(".js-allowed-to-merge").click
- wait_for_requests
- click_link 'No one'
- find(".js-allowed-to-push").click
- wait_for_requests
- click_link 'Developers + Maintainers'
- end
-
- visit project_protected_branches_path(project)
-
- within form do
- page.within(".js-allowed-to-merge") do
- expect(page.find(".dropdown-toggle-text")).to have_content("No one")
- end
- page.within(".js-allowed-to-push") do
- expect(page.find(".dropdown-toggle-text")).to have_content("Developers + Maintainers")
- end
- end
- end
- end
end
context 'logged in as admin' do
@@ -97,6 +70,7 @@ feature 'Protected Branches', :js do
describe "explicit protected branches" do
it "allows creating explicit protected branches" do
visit project_protected_branches_path(project)
+ set_defaults
set_protected_branch_name('some-branch')
click_on "Protect"
@@ -110,6 +84,7 @@ feature 'Protected Branches', :js do
project.repository.add_branch(admin, 'some-branch', commit.id)
visit project_protected_branches_path(project)
+ set_defaults
set_protected_branch_name('some-branch')
click_on "Protect"
@@ -118,6 +93,7 @@ feature 'Protected Branches', :js do
it "displays an error message if the named branch does not exist" do
visit project_protected_branches_path(project)
+ set_defaults
set_protected_branch_name('some-branch')
click_on "Protect"
@@ -128,6 +104,7 @@ feature 'Protected Branches', :js do
describe "wildcard protected branches" do
it "allows creating protected branches with a wildcard" do
visit project_protected_branches_path(project)
+ set_defaults
set_protected_branch_name('*-stable')
click_on "Protect"
@@ -141,6 +118,7 @@ feature 'Protected Branches', :js do
project.repository.add_branch(admin, 'staging-stable', 'master')
visit project_protected_branches_path(project)
+ set_defaults
set_protected_branch_name('*-stable')
click_on "Protect"
@@ -157,6 +135,7 @@ feature 'Protected Branches', :js do
visit project_protected_branches_path(project)
set_protected_branch_name('*-stable')
+ set_defaults
click_on "Protect"
visit project_protected_branches_path(project)
@@ -180,4 +159,18 @@ feature 'Protected Branches', :js do
find(".dropdown-input-field").set(branch_name)
click_on("Create wildcard #{branch_name}")
end
+
+ def set_defaults
+ find(".js-allowed-to-merge").click
+ within('.qa-allowed-to-merge-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
+ find(".js-allowed-to-push").click
+ within('.qa-allowed-to-push-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+ end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 593b2ca1825..14297a1a544 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -157,7 +157,7 @@ describe ApplicationHelper do
let(:noteable_type) { Issue }
it 'returns paths for autocomplete_sources_controller' do
sources = helper.autocomplete_data_sources(project, noteable_type)
- expect(sources.keys).to match_array([:members, :issues, :merge_requests, :labels, :milestones, :commands])
+ expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands])
sources.keys.each do |key|
expect(sources[key]).not_to be_nil
end
diff --git a/spec/javascripts/behaviors/copy_as_gfm_spec.js b/spec/javascripts/behaviors/copy_as_gfm_spec.js
index efbe09a10a2..c2db81c6ce4 100644
--- a/spec/javascripts/behaviors/copy_as_gfm_spec.js
+++ b/spec/javascripts/behaviors/copy_as_gfm_spec.js
@@ -44,4 +44,59 @@ describe('CopyAsGFM', () => {
callPasteGFM();
});
});
+
+ describe('CopyAsGFM.copyGFM', () => {
+ // Stub getSelection to return a purpose-built object.
+ const stubSelection = (html, parentNode) => ({
+ getRangeAt: () => ({
+ commonAncestorContainer: { tagName: parentNode },
+ cloneContents: () => {
+ const fragment = document.createDocumentFragment();
+ const node = document.createElement('div');
+ node.innerHTML = html;
+ Array.from(node.childNodes).forEach((item) => fragment.appendChild(item));
+ return fragment;
+ },
+ }),
+ rangeCount: 1,
+ });
+
+ const clipboardData = {
+ setData() {},
+ };
+
+ const simulateCopy = () => {
+ const e = {
+ originalEvent: {
+ clipboardData,
+ },
+ preventDefault() {},
+ stopPropagation() {},
+ };
+ CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection);
+ return clipboardData;
+ };
+
+ beforeEach(() => spyOn(clipboardData, 'setData'));
+
+ describe('list handling', () => {
+ it('uses correct gfm for unordered lists', () => {
+ const selection = stubSelection('<li>List Item1</li><li>List Item2</li>\n', 'UL');
+ spyOn(window, 'getSelection').and.returnValue(selection);
+ simulateCopy();
+
+ const expectedGFM = '- List Item1\n- List Item2';
+ expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM);
+ });
+
+ it('uses correct gfm for ordered lists', () => {
+ const selection = stubSelection('<li>List Item1</li><li>List Item2</li>\n', 'OL');
+ spyOn(window, 'getSelection').and.returnValue(selection);
+ simulateCopy();
+
+ const expectedGFM = '1. List Item1\n1. List Item2';
+ expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM);
+ });
+ });
+ });
});
diff --git a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js
index d1ebae33dab..7651792be2e 100644
--- a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js
+++ b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js
@@ -26,7 +26,7 @@ describe('Mesh object', () => {
const object = new MeshObject(
new BoxGeometry(10, 10, 10),
);
- const radius = object.geometry.boundingSphere.radius;
+ const { radius } = object.geometry.boundingSphere;
expect(radius).not.toBeGreaterThan(4);
});
@@ -35,7 +35,7 @@ describe('Mesh object', () => {
const object = new MeshObject(
new BoxGeometry(1, 1, 1),
);
- const radius = object.geometry.boundingSphere.radius;
+ const { radius } = object.geometry.boundingSphere;
expect(radius).toBeLessThan(1);
});
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 819ed7896ca..a18e09da50a 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -16,7 +16,7 @@ describe('Pipelines table in Commits and Merge requests', function () {
beforeEach(() => {
mock = new MockAdapter(axios);
- const pipelines = getJSONFixture(jsonFixtureName).pipelines;
+ const { pipelines } = getJSONFixture(jsonFixtureName);
PipelinesTable = Vue.extend(pipelinesTable);
pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index 4279add21d1..d1de9d132b8 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -88,7 +88,7 @@ describe('Deploy keys key', () => {
});
it('expands all project labels after click', done => {
- const length = vm.deployKey.deploy_keys_projects.length;
+ const { length } = vm.deployKey.deploy_keys_projects;
vm.$el.querySelectorAll('.deploy-project-label')[1].click();
Vue.nextTick(() => {
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
index 7237274eb43..dea600a783a 100644
--- a/spec/javascripts/diffs/components/diff_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -1 +1,95 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
+import Vue from 'vue';
+import DiffContentComponent from '~/diffs/components/diff_content.vue';
+import store from '~/mr_notes/stores';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
+import diffFileMockData from '../mock_data/diff_file';
+
+describe('DiffContent', () => {
+ const Component = Vue.extend(DiffContentComponent);
+ let vm;
+ const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: {
+ diffFile: getDiffFileMock(),
+ },
+ });
+ });
+
+ describe('text based files', () => {
+ it('should render diff inline view', done => {
+ vm.$store.state.diffs.diffViewType = 'inline';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(1);
+
+ done();
+ });
+ });
+
+ it('should render diff parallel view', done => {
+ vm.$store.state.diffs.diffViewType = 'parallel';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.parallel').length).toEqual(18);
+
+ done();
+ });
+ });
+ });
+
+ describe('Non-Text diffs', () => {
+ beforeEach(() => {
+ vm.diffFile.text = false;
+ });
+
+ describe('image diff', () => {
+ beforeEach(() => {
+ vm.diffFile.newPath = GREEN_BOX_IMAGE_URL;
+ vm.diffFile.newSha = 'DEF';
+ vm.diffFile.oldPath = RED_BOX_IMAGE_URL;
+ vm.diffFile.oldSha = 'ABC';
+ vm.diffFile.viewPath = '';
+ });
+
+ it('should have image diff view in place', done => {
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
+
+ expect(vm.$el.querySelectorAll('.diff-viewer .image').length).toEqual(1);
+
+ done();
+ });
+ });
+ });
+
+ describe('file diff', () => {
+ it('should have download buttons in place', done => {
+ const el = vm.$el;
+ vm.diffFile.newPath = 'test.abc';
+ vm.diffFile.newSha = 'DEF';
+ vm.diffFile.oldPath = 'test.abc';
+ vm.diffFile.oldSha = 'ABC';
+
+ vm.$nextTick(() => {
+ expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
+
+ expect(el.querySelector('.deleted .file-info').textContent.trim()).toContain('test.abc');
+ expect(el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain(
+ 'Download',
+ );
+
+ expect(el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc');
+ expect(el.querySelector('.added .btn.btn-default').textContent.trim()).toContain(
+ 'Download',
+ );
+
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index 312a684f4d2..cce10c4083c 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -92,7 +92,7 @@ describe('DiffLineGutterContent', () => {
});
it('should return discussions for the given lineCode', () => {
- const lineCode = getDiffFileMock().highlightedDiffLines[1].lineCode;
+ const { lineCode } = getDiffFileMock().highlightedDiffLines[1];
const component = createComponent({ lineCode, showCommentButton: true });
setDiscussions(component);
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
index 724d1948214..81cd4f9769a 100644
--- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -19,7 +19,15 @@ describe('DiffLineNoteForm', () => {
diffLines,
line: diffLines[0],
noteTargetLine: diffLines[0],
- }).$mount();
+ });
+
+ Object.defineProperty(component, 'isLoggedIn', {
+ get() {
+ return true;
+ },
+ });
+
+ component.$mount();
});
describe('methods', () => {
@@ -56,6 +64,15 @@ describe('DiffLineNoteForm', () => {
});
});
+ describe('mounted', () => {
+ it('should init autosave', () => {
+ const key = 'autosave/Note/issue///DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1';
+
+ expect(component.autosave).toBeDefined();
+ expect(component.autosave.key).toEqual(key);
+ });
+ });
+
describe('template', () => {
it('should have note form', () => {
const { $el } = component;
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index e61780c9928..f0bd098f698 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -12,15 +12,16 @@ import axios from '~/lib/utils/axios_utils';
import testAction from '../../helpers/vuex_action_helper';
describe('DiffsStoreActions', () => {
- describe('setEndpoint', () => {
- it('should set given endpoint', done => {
+ describe('setBaseConfig', () => {
+ it('should set given endpoint and project path', done => {
const endpoint = '/diffs/set/endpoint';
+ const projectPath = '/root/project';
testAction(
- actions.setEndpoint,
- endpoint,
- { endpoint: '' },
- [{ type: types.SET_ENDPOINT, payload: endpoint }],
+ actions.setBaseConfig,
+ { endpoint, projectPath },
+ { endpoint: '', projectPath: '' },
+ [{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }],
[],
done,
);
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 5f1a6e9def7..02836fcaeea 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -3,13 +3,15 @@ import * as types from '~/diffs/store/mutation_types';
import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
describe('DiffsStoreMutations', () => {
- describe('SET_ENDPOINT', () => {
- it('should set endpoint', () => {
+ describe('SET_BASE_CONFIG', () => {
+ it('should set endpoint and project path', () => {
const state = {};
const endpoint = '/diffs/endpoint';
+ const projectPath = '/root/project';
- mutations[types.SET_ENDPOINT](state, endpoint);
+ mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath });
expect(state.endpoint).toEqual(endpoint);
+ expect(state.projectPath).toEqual(projectPath);
});
});
diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
index 59bd2650081..d926663fac0 100644
--- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -103,7 +103,7 @@ describe('RecentSearchesDropdownContent', () => {
describe('processedItems', () => {
it('with items', () => {
vm = createComponent(propsDataWithItems);
- const processedItems = vm.processedItems;
+ const { processedItems } = vm;
expect(processedItems.length).toEqual(2);
@@ -122,7 +122,7 @@ describe('RecentSearchesDropdownContent', () => {
it('with no items', () => {
vm = createComponent(propsDataWithoutItems);
- const processedItems = vm.processedItems;
+ const { processedItems } = vm;
expect(processedItems.length).toEqual(0);
});
@@ -131,13 +131,13 @@ describe('RecentSearchesDropdownContent', () => {
describe('hasItems', () => {
it('with items', () => {
vm = createComponent(propsDataWithItems);
- const hasItems = vm.hasItems;
+ const { hasItems } = vm;
expect(hasItems).toEqual(true);
});
it('with no items', () => {
vm = createComponent(propsDataWithoutItems);
- const hasItems = vm.hasItems;
+ const { hasItems } = vm;
expect(hasItems).toEqual(false);
});
});
diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js
index 1e6272bad0b..d063fcf4f2d 100644
--- a/spec/javascripts/filtered_search/recent_searches_root_spec.js
+++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js
@@ -15,8 +15,7 @@ describe('RecentSearchesRoot', () => {
};
VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => {
- data = options.data;
- template = options.template;
+ ({ data, template } = options);
});
RecentSearchesRoot.prototype.render.call(recentSearchesRoot);
diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js
index 2839020b2ca..21c462cd040 100644
--- a/spec/javascripts/gl_field_errors_spec.js
+++ b/spec/javascripts/gl_field_errors_spec.js
@@ -18,7 +18,7 @@ describe('GL Style Field Errors', function() {
expect(this.$form).toBeDefined();
expect(this.$form.length).toBe(1);
expect(this.fieldErrors).toBeDefined();
- const inputs = this.fieldErrors.state.inputs;
+ const { inputs } = this.fieldErrors.state;
expect(inputs.length).toBe(4);
});
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index 2b92c485f41..03d4b472b87 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -67,7 +67,7 @@ describe('AppComponent', () => {
it('should return list of groups from store', () => {
spyOn(vm.store, 'getGroups');
- const groups = vm.groups;
+ const { groups } = vm;
expect(vm.store.getGroups).toHaveBeenCalled();
expect(groups).not.toBeDefined();
});
@@ -77,7 +77,7 @@ describe('AppComponent', () => {
it('should return pagination info from store', () => {
spyOn(vm.store, 'getPaginationInfo');
- const pageInfo = vm.pageInfo;
+ const { pageInfo } = vm;
expect(vm.store.getPaginationInfo).toHaveBeenCalled();
expect(pageInfo).not.toBeDefined();
});
@@ -293,7 +293,7 @@ describe('AppComponent', () => {
beforeEach(() => {
groupItem = Object.assign({}, mockParentGroupItem);
groupItem.children = mockChildren;
- childGroupItem = groupItem.children[0];
+ [childGroupItem] = groupItem.children;
groupItem.isChildrenLoading = false;
vm.targetGroup = childGroupItem;
vm.targetParentGroup = groupItem;
diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js
index 49a139855c8..d0cac5efc40 100644
--- a/spec/javascripts/groups/components/group_item_spec.js
+++ b/spec/javascripts/groups/components/group_item_spec.js
@@ -41,7 +41,7 @@ describe('GroupItemComponent', () => {
describe('rowClass', () => {
it('should return map of classes based on group details', () => {
const classes = ['is-open', 'has-children', 'has-description', 'being-removed'];
- const rowClass = vm.rowClass;
+ const { rowClass } = vm;
expect(Object.keys(rowClass).length).toBe(classes.length);
Object.keys(rowClass).forEach((className) => {
diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js
index 921d42a0871..05c6d587e9c 100644
--- a/spec/javascripts/helpers/init_vue_mr_page_helper.js
+++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js
@@ -6,6 +6,7 @@ import diffFileMockData from '../diffs/mock_data/diff_file';
export default function initVueMRPage() {
const diffsAppEndpoint = '/diffs/app/endpoint';
+ const diffsAppProjectPath = 'testproject';
const mrEl = document.createElement('div');
mrEl.className = 'merge-request fixture-mr';
mrEl.setAttribute('data-mr-action', 'diffs');
@@ -26,6 +27,7 @@ export default function initVueMRPage() {
const diffsAppEl = document.createElement('div');
diffsAppEl.id = 'js-diffs-app';
diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint);
+ diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath);
diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock));
document.body.appendChild(diffsAppEl);
diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js
index 9312e17704e..569fa5c7aae 100644
--- a/spec/javascripts/ide/helpers.js
+++ b/spec/javascripts/ide/helpers.js
@@ -1,3 +1,4 @@
+import * as pathUtils from 'path';
import { decorateData } from '~/ide/stores/utils';
import state from '~/ide/stores/state';
import commitState from '~/ide/stores/modules/commit/state';
@@ -14,13 +15,34 @@ export const resetStore = store => {
store.replaceState(newState);
};
-export const file = (name = 'name', id = name, type = '') =>
+export const file = (name = 'name', id = name, type = '', parent = null) =>
decorateData({
id,
type,
icon: 'icon',
url: 'url',
name,
- path: name,
+ path: parent ? `${parent.path}/${name}` : name,
+ parentPath: parent ? parent.path : '',
lastCommit: {},
});
+
+export const createEntriesFromPaths = paths =>
+ paths
+ .map(path => ({
+ name: pathUtils.basename(path),
+ dir: pathUtils.dirname(path),
+ ext: pathUtils.extname(path),
+ }))
+ .reduce((entries, path, idx) => {
+ const { name } = path;
+ const parent = path.dir ? entries[path.dir] : null;
+ const type = path.ext ? 'blob' : 'tree';
+
+ const entry = file(name, (idx + 1).toString(), type, parent);
+
+ return {
+ [entry.path]: entry,
+ ...entries,
+ };
+ }, {});
diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js
index 96abd1dcd9e..90ebb95b687 100644
--- a/spec/javascripts/ide/lib/diff/controller_spec.js
+++ b/spec/javascripts/ide/lib/diff/controller_spec.js
@@ -63,7 +63,7 @@ describe('Multi-file editor library dirty diff controller', () => {
[type]: true,
};
- const range = getDecorator(change).range;
+ const { range } = getDecorator(change);
expect(range.startLineNumber).toBe(1);
expect(range.endLineNumber).toBe(2);
diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js
index e0ef57a3966..cefed9ddb43 100644
--- a/spec/javascripts/ide/stores/actions/tree_spec.js
+++ b/spec/javascripts/ide/stores/actions/tree_spec.js
@@ -1,8 +1,11 @@
import Vue from 'vue';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { showTreeEntry } from '~/ide/stores/actions/tree';
+import * as types from '~/ide/stores/mutation_types';
import store from '~/ide/stores';
import service from '~/ide/services';
import router from '~/ide/ide_router';
-import { file, resetStore } from '../../helpers';
+import { file, resetStore, createEntriesFromPaths } from '../../helpers';
describe('Multi-file store tree actions', () => {
let projectTree;
@@ -96,6 +99,37 @@ describe('Multi-file store tree actions', () => {
});
});
+ describe('showTreeEntry', () => {
+ beforeEach(() => {
+ const paths = [
+ 'grandparent',
+ 'ancestor',
+ 'grandparent/parent',
+ 'grandparent/aunt',
+ 'grandparent/parent/child.txt',
+ 'grandparent/aunt/cousing.txt',
+ ];
+
+ Object.assign(store.state.entries, createEntriesFromPaths(paths));
+ });
+
+ it('opens the parents', done => {
+ testAction(
+ showTreeEntry,
+ 'grandparent/parent/child.txt',
+ store.state,
+ [
+ { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' },
+ { type: types.SET_TREE_OPEN, payload: 'grandparent' },
+ ],
+ [
+ { type: 'showTreeEntry' },
+ ],
+ done,
+ );
+ });
+ });
+
describe('getLastCommitData', () => {
beforeEach(() => {
spyOn(service, 'getTreeLastCommit').and.returnValue(
diff --git a/spec/javascripts/namespace_select_spec.js b/spec/javascripts/namespace_select_spec.js
index 3b2641f7646..07b82ce721e 100644
--- a/spec/javascripts/namespace_select_spec.js
+++ b/spec/javascripts/namespace_select_spec.js
@@ -22,7 +22,7 @@ describe('NamespaceSelect', () => {
const dropdown = document.createElement('div');
// eslint-disable-next-line no-new
new NamespaceSelect({ dropdown });
- glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0];
+ [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0);
});
it('prevents click events', () => {
@@ -43,7 +43,7 @@ describe('NamespaceSelect', () => {
dropdown.dataset.isFilter = 'true';
// eslint-disable-next-line no-new
new NamespaceSelect({ dropdown });
- glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0];
+ [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0);
});
it('does not prevent click events', () => {
diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js
index 8f8ba231ae8..0b1b11de1fd 100644
--- a/spec/javascripts/notebook/cells/markdown_spec.js
+++ b/spec/javascripts/notebook/cells/markdown_spec.js
@@ -14,6 +14,7 @@ describe('Markdown component', () => {
beforeEach((done) => {
json = getJSONFixture('blob/notebook/basic.json');
+ // eslint-disable-next-line prefer-destructuring
cell = json.cells[1];
vm = new Component({
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index 78d8e9e572e..03ffc122795 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -24,7 +24,7 @@ describe('Pipelines Table Row', () => {
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- const pipelines = getJSONFixture(jsonFixtureName).pipelines;
+ const { pipelines } = getJSONFixture(jsonFixtureName);
pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null);
diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js
index 4fc3c08145e..d21ba35e96d 100644
--- a/spec/javascripts/pipelines/pipelines_table_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_spec.js
@@ -11,7 +11,7 @@ describe('Pipelines Table', () => {
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- const pipelines = getJSONFixture(jsonFixtureName).pipelines;
+ const { pipelines } = getJSONFixture(jsonFixtureName);
PipelinesTableComponent = Vue.extend(pipelinesTableComp);
pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js
index a54219d58c2..60153672214 100644
--- a/spec/javascripts/smart_interval_spec.js
+++ b/spec/javascripts/smart_interval_spec.js
@@ -87,7 +87,7 @@ describe('SmartInterval', function () {
setTimeout(() => {
interval.cancel();
- const intervalId = interval.state.intervalId;
+ const { intervalId } = interval.state;
const currentInterval = interval.getCurrentInterval();
const intervalLowerLimit = interval.cfg.startingInterval;
@@ -106,7 +106,7 @@ describe('SmartInterval', function () {
interval.resume();
- const intervalId = interval.state.intervalId;
+ const { intervalId } = interval.state;
expect(intervalId).toBeTruthy();
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
index f7581251bf0..1c666fc6c55 100644
--- a/spec/javascripts/vue_shared/components/file_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/file_icon_spec.js
@@ -74,7 +74,7 @@ describe('File Icon component', () => {
size: 120,
});
- const classList = vm.$el.firstChild.classList;
+ const { classList } = vm.$el.firstChild;
const containsSizeClass = classList.contains('s120');
const containsCustomClass = classList.contains('extraclasses');
expect(containsSizeClass).toBe(true);
diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js
index 68d57ebc8f0..cc030e29d61 100644
--- a/spec/javascripts/vue_shared/components/icon_spec.js
+++ b/spec/javascripts/vue_shared/components/icon_spec.js
@@ -44,7 +44,7 @@ describe('Sprite Icon Component', function () {
});
it('should properly render img css', function () {
- const classList = icon.$el.classList;
+ const { classList } = icon.$el;
const containsSizeClass = classList.contains('s32');
const containsCustomClass = classList.contains('extraclasses');
expect(containsSizeClass).toBe(true);
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
index 446f025c127..656b57d764e 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
@@ -51,7 +51,7 @@ describe('User Avatar Image Component', function () {
});
it('should properly render img css', function () {
- const classList = vm.$el.classList;
+ const { classList } = vm.$el;
const containsAvatar = classList.contains('avatar');
const containsSizeClass = classList.contains('s99');
const containsCustomClass = classList.contains(DEFAULT_PROPS.cssClasses);
@@ -73,7 +73,7 @@ describe('User Avatar Image Component', function () {
});
it('should add lazy attributes', function () {
- const classList = vm.$el.classList;
+ const { classList } = vm.$el;
const lazyClass = classList.contains('lazy');
expect(lazyClass).toBe(true);
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
index adf80d0c2bb..4c5c242cbb3 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
@@ -21,7 +21,7 @@ describe('User Avatar Link Component', function () {
propsData: this.propsData,
}).$mount();
- this.userAvatarImage = this.userAvatarLink.$children[0];
+ [this.userAvatarImage] = this.userAvatarLink.$children;
});
it('should return a defined Vue component', function () {
diff --git a/spec/lib/gitaly/server_spec.rb b/spec/lib/gitaly/server_spec.rb
index ed5d56e91d4..09bf21b5946 100644
--- a/spec/lib/gitaly/server_spec.rb
+++ b/spec/lib/gitaly/server_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Gitaly::Server do
+ let(:server) { described_class.new('default') }
+
describe '.all' do
let(:storages) { Gitlab.config.repositories.storages }
@@ -17,6 +19,38 @@ describe Gitaly::Server do
it { is_expected.to respond_to(:up_to_date?) }
it { is_expected.to respond_to(:address) }
+ describe 'readable?' do
+ context 'when the storage is readable' do
+ it 'returns true' do
+ expect(server).to be_readable
+ end
+ end
+
+ context 'when the storage is not readable' do
+ let(:server) { described_class.new('broken') }
+
+ it 'returns false' do
+ expect(server).not_to be_readable
+ end
+ end
+ end
+
+ describe 'writeable?' do
+ context 'when the storage is writeable' do
+ it 'returns true' do
+ expect(server).to be_writeable
+ end
+ end
+
+ context 'when the storage is not writeable' do
+ let(:server) { described_class.new('broken') }
+
+ it 'returns false' do
+ expect(server).not_to be_writeable
+ end
+ end
+ end
+
describe 'request memoization' do
context 'when requesting multiple properties', :request_store do
it 'uses memoization for the info request' do
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 280f799f2ab..eb7148ff108 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -1178,6 +1178,61 @@ describe Gitlab::Database::MigrationHelpers do
end
end
+ describe '#rename_column_using_background_migration' do
+ let!(:issue) { create(:issue, :closed, closed_at: Time.zone.now) }
+
+ it 'renames a column using a background migration' do
+ expect(model)
+ .to receive(:add_column)
+ .with(
+ 'issues',
+ :closed_at_timestamp,
+ :datetime_with_timezone,
+ limit: anything,
+ precision: anything,
+ scale: anything
+ )
+
+ expect(model)
+ .to receive(:install_rename_triggers)
+ .with('issues', :closed_at, :closed_at_timestamp)
+
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_in)
+ .ordered
+ .with(
+ 10.minutes,
+ 'CopyColumn',
+ ['issues', :closed_at, :closed_at_timestamp, issue.id, issue.id]
+ )
+
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_in)
+ .ordered
+ .with(
+ 1.hour + 10.minutes,
+ 'CleanupConcurrentRename',
+ ['issues', :closed_at, :closed_at_timestamp]
+ )
+
+ expect(Gitlab::BackgroundMigration)
+ .to receive(:steal)
+ .ordered
+ .with('CopyColumn')
+
+ expect(Gitlab::BackgroundMigration)
+ .to receive(:steal)
+ .ordered
+ .with('CleanupConcurrentRename')
+
+ model.rename_column_using_background_migration(
+ 'issues',
+ :closed_at,
+ :closed_at_timestamp
+ )
+ end
+ end
+
describe '#perform_background_migration_inline?' do
it 'returns true in a test environment' do
allow(Rails.env)
diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
deleted file mode 100644
index 9dcf272d25e..00000000000
--- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::HealthChecks::FsShardsCheck do
- def command_exists?(command)
- _, status = Gitlab::Popen.popen(%W{ #{command} 1 echo })
- status.zero?
- rescue Errno::ENOENT
- false
- end
-
- def timeout_command
- @timeout_command ||=
- if command_exists?('timeout')
- 'timeout'
- elsif command_exists?('gtimeout')
- 'gtimeout'
- else
- ''
- end
- end
-
- let(:metric_class) { Gitlab::HealthChecks::Metric }
- let(:result_class) { Gitlab::HealthChecks::Result }
- let(:repository_storages) { ['default'] }
- let(:tmp_dir) { Dir.mktmpdir }
-
- let(:storages_paths) do
- {
- default: Gitlab::GitalyClient::StorageSettings.new('path' => tmp_dir)
- }.with_indifferent_access
- end
-
- before do
- allow(described_class).to receive(:repository_storages) { repository_storages }
- allow(described_class).to receive(:storages_paths) { storages_paths }
- stub_const('Gitlab::HealthChecks::FsShardsCheck::TIMEOUT_EXECUTABLE', timeout_command)
- end
-
- after do
- FileUtils.remove_entry_secure(tmp_dir) if Dir.exist?(tmp_dir)
- end
-
- shared_examples 'filesystem checks' do
- describe '#readiness' do
- subject { described_class.readiness }
-
- context 'storage has a tripped circuitbreaker', :broken_storage do
- let(:repository_storages) { ['broken'] }
- let(:storages_paths) do
- Gitlab.config.repositories.storages
- end
-
- it { is_expected.to include(result_class.new(false, 'circuitbreaker tripped', shard: 'broken')) }
- end
-
- context 'storage points to not existing folder' do
- let(:storages_paths) do
- {
- default: Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/this/path/doesnt/exist')
- }.with_indifferent_access
- end
-
- before do
- allow(described_class).to receive(:storage_circuitbreaker_test) { true }
- end
-
- it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) }
- end
-
- context 'storage points to directory that has both read and write rights' do
- before do
- FileUtils.chmod_R(0755, tmp_dir)
- end
-
- it { is_expected.to include(result_class.new(true, nil, shard: 'default')) }
-
- it 'cleans up files used for testing' do
- expect(described_class).to receive(:storage_write_test).with(any_args).and_call_original
-
- expect { subject }.not_to change(Dir.entries(tmp_dir), :count)
- end
-
- context 'read test fails' do
- before do
- allow(described_class).to receive(:storage_read_test).with(any_args).and_return(false)
- end
-
- it { is_expected.to include(result_class.new(false, 'cannot read from storage', shard: 'default')) }
- end
-
- context 'write test fails' do
- before do
- allow(described_class).to receive(:storage_write_test).with(any_args).and_return(false)
- end
-
- it { is_expected.to include(result_class.new(false, 'cannot write to storage', shard: 'default')) }
- end
- end
- end
-
- describe '#metrics' do
- context 'storage points to not existing folder' do
- let(:storages_paths) do
- {
- default: Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/this/path/doesnt/exist')
- }.with_indifferent_access
- end
-
- it 'provides metrics' do
- metrics = described_class.metrics
-
- expect(metrics).to all(have_attributes(labels: { shard: 'default' }))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_circuitbreaker_latency_seconds, value: be >= 0))
- end
- end
-
- context 'storage points to directory that has both read and write rights' do
- before do
- FileUtils.chmod_R(0755, tmp_dir)
- end
-
- it 'provides metrics' do
- metrics = described_class.metrics
-
- expect(metrics).to all(have_attributes(labels: { shard: 'default' }))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 1))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 1))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 1))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_circuitbreaker_latency_seconds, value: be >= 0))
- end
-
- it 'cleans up files used for metrics' do
- expect { described_class.metrics }.not_to change(Dir.entries(tmp_dir), :count)
- end
- end
- end
- end
-
- context 'when timeout kills fs checks' do
- before do
- stub_const('Gitlab::HealthChecks::FsShardsCheck::COMMAND_TIMEOUT', '1')
-
- allow(described_class).to receive(:exec_with_timeout).and_wrap_original { |m| m.call(%w(sleep 60)) }
- FileUtils.chmod_R(0755, tmp_dir)
- end
-
- describe '#readiness' do
- subject { described_class.readiness }
-
- it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) }
- end
-
- describe '#metrics' do
- it 'provides metrics' do
- metrics = described_class.metrics
-
- expect(metrics).to all(have_attributes(labels: { shard: 'default' }))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
- expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
- end
- end
- end
-
- context 'when popen always finds required binaries' do
- before do
- allow(described_class).to receive(:exec_with_timeout).and_wrap_original do |method, *args, &block|
- begin
- method.call(*args, &block)
- rescue RuntimeError, Errno::ENOENT
- raise 'expected not to happen'
- end
- end
-
- stub_const('Gitlab::HealthChecks::FsShardsCheck::COMMAND_TIMEOUT', '10')
- end
-
- it_behaves_like 'filesystem checks'
- end
-
- context 'when popen never finds required binaries' do
- before do
- allow(Gitlab::Popen).to receive(:popen).and_raise(Errno::ENOENT)
- end
-
- it_behaves_like 'filesystem checks'
- end
-end
diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
index 724beefff69..4912cd48761 100644
--- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
@@ -30,13 +30,14 @@ describe Gitlab::HealthChecks::GitalyCheck do
describe '#metrics' do
subject { described_class.metrics }
+ let(:server) { double(storage: 'default', read_writeable?: up) }
before do
- expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check)
+ allow(Gitaly::Server).to receive(:new).and_return(server)
end
context 'Gitaly server is up' do
- let(:gitaly_check) { double(check: { success: true }) }
+ let(:up) { true }
it 'provides metrics' do
expect(subject).to all(have_attributes(labels: { shard: 'default' }))
@@ -46,7 +47,7 @@ describe Gitlab::HealthChecks::GitalyCheck do
end
context 'Gitaly server is down' do
- let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
+ let(:up) { false }
it 'provides metrics' do
expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_success', value: 0))
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 3e6656e0f12..02f74e2ea54 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -25,15 +25,6 @@ describe ApplicationSetting do
it { is_expected.to allow_value(https).for(:after_sign_out_path) }
it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
- describe 'disabled_oauth_sign_in_sources validations' do
- before do
- allow(Devise).to receive(:omniauth_providers).and_return([:github])
- end
-
- it { is_expected.to allow_value(['github']).for(:disabled_oauth_sign_in_sources) }
- it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) }
- end
-
describe 'default_artifacts_expire_in' do
it 'sets an error if it cannot parse' do
setting.update(default_artifacts_expire_in: 'a')
@@ -314,6 +305,33 @@ describe ApplicationSetting do
end
end
+ describe '#disabled_oauth_sign_in_sources=' do
+ before do
+ allow(Devise).to receive(:omniauth_providers).and_return([:github])
+ end
+
+ it 'removes unknown sources (as strings) from the array' do
+ subject.disabled_oauth_sign_in_sources = %w[github test]
+
+ expect(subject).to be_valid
+ expect(subject.disabled_oauth_sign_in_sources).to eq ['github']
+ end
+
+ it 'removes unknown sources (as symbols) from the array' do
+ subject.disabled_oauth_sign_in_sources = %i[github test]
+
+ expect(subject).to be_valid
+ expect(subject.disabled_oauth_sign_in_sources).to eq ['github']
+ end
+
+ it 'ignores nil' do
+ subject.disabled_oauth_sign_in_sources = nil
+
+ expect(subject).to be_valid
+ expect(subject.disabled_oauth_sign_in_sources).to be_empty
+ end
+ end
+
context 'restricted signup domains' do
it 'sets single domain' do
setting.domain_whitelist_raw = 'example.com'
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 090f91168ad..5157d8fc645 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -514,30 +514,21 @@ eos
end
describe '#uri_type' do
- shared_examples 'URI type' do
- it 'returns the URI type at the given path' do
- expect(commit.uri_type('files/html')).to be(:tree)
- expect(commit.uri_type('files/images/logo-black.png')).to be(:raw)
- expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw)
- expect(commit.uri_type('files/js/application.js')).to be(:blob)
- end
-
- it "returns nil if the path doesn't exists" do
- expect(commit.uri_type('this/path/doesnt/exist')).to be_nil
- end
-
- it 'is nil if the path is nil or empty' do
- expect(commit.uri_type(nil)).to be_nil
- expect(commit.uri_type("")).to be_nil
- end
+ it 'returns the URI type at the given path' do
+ expect(commit.uri_type('files/html')).to be(:tree)
+ expect(commit.uri_type('files/images/logo-black.png')).to be(:raw)
+ expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw)
+ expect(commit.uri_type('files/js/application.js')).to be(:blob)
end
- context 'when Gitaly commit_tree_entry feature is enabled' do
- it_behaves_like 'URI type'
+ it "returns nil if the path doesn't exists" do
+ expect(commit.uri_type('this/path/doesnt/exist')).to be_nil
+ expect(commit.uri_type('../path/doesnt/exist')).to be_nil
end
- context 'when Gitaly commit_tree_entry feature is disabled', :disable_gitaly do
- it_behaves_like 'URI type'
+ it 'is nil if the path is nil or empty' do
+ expect(commit.uri_type(nil)).to be_nil
+ expect(commit.uri_type("")).to be_nil
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index d817a8376f4..c7e751130d8 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -46,7 +46,7 @@ describe Repository do
it { is_expected.not_to include('feature') }
it { is_expected.not_to include('fix') }
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error do
broken_repository.branch_names_contains(sample_commit.id)
@@ -192,7 +192,7 @@ describe Repository do
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error do
broken_repository.last_commit_id_for_path(sample_commit.id, '.gitignore')
@@ -226,7 +226,7 @@ describe Repository do
is_expected.to eq('c1acaa5')
end
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error do
broken_repository.last_commit_for_path(sample_commit.id, '.gitignore').id
@@ -391,7 +391,7 @@ describe Repository do
it_behaves_like 'finding commits by message'
end
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error { broken_repository.find_commits_by_message('s') }
end
@@ -695,7 +695,7 @@ describe Repository do
expect(results).to match_array([])
end
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error do
broken_repository.search_files_by_content('feature', 'master')
@@ -744,7 +744,7 @@ describe Repository do
expect(results).to match_array([])
end
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error { broken_repository.search_files_by_name('files', 'master') }
end
@@ -796,7 +796,7 @@ describe Repository do
describe '#fetch_ref' do
let(:broken_repository) { create(:project, :broken_storage).repository }
- describe 'when storage is broken', :broken_storage do
+ describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do
expect_to_raise_storage_error do
broken_repository.fetch_ref(broken_repository, source_ref: '1', target_ref: '2')
@@ -2294,6 +2294,28 @@ describe Repository do
end
end
+ describe '#local_branches' do
+ it 'returns the local branches' do
+ masterrev = repository.find_branch('master').dereferenced_target
+ create_remote_branch('joe', 'remote_branch', masterrev)
+ repository.add_branch(user, 'local_branch', masterrev.id)
+
+ expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
+ expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
+ end
+ end
+
+ describe '#remote_branches' do
+ it 'returns the remote branches' do
+ masterrev = repository.find_branch('master').dereferenced_target
+ create_remote_branch('joe', 'remote_branch', masterrev)
+ repository.add_branch(user, 'local_branch', masterrev.id)
+
+ expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false)
+ expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true)
+ end
+ end
+
describe '#commit_count' do
context 'with a non-existing repository' do
it 'returns 0' do
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 64f51d9843d..9bb6ed62393 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -155,6 +155,12 @@ describe API::Branches do
end
it_behaves_like 'repository branch'
+
+ it 'returns that the current user cannot push' do
+ get api(route, current_user)
+
+ expect(json_response['can_push']).to eq(false)
+ end
end
context 'when unauthenticated', 'and project is private' do
@@ -169,6 +175,12 @@ describe API::Branches do
it_behaves_like 'repository branch'
+ it 'returns that the current user can push' do
+ get api(route, current_user)
+
+ expect(json_response['can_push']).to eq(true)
+ end
+
context 'when branch contains a dot' do
let(:branch_name) { branch_with_dot.name }
@@ -202,6 +214,23 @@ describe API::Branches do
end
end
+ context 'when authenticated', 'as a developer and branch is protected' do
+ let(:current_user) { create(:user) }
+ let!(:protected_branch) { create(:protected_branch, project: project, name: branch_name) }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'repository branch'
+
+ it 'returns that the current user cannot push' do
+ get api(route, current_user)
+
+ expect(json_response['can_push']).to eq(false)
+ end
+ end
+
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index bceaf8277ee..471b0a74a19 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -15,9 +15,14 @@ module StubObjectStorage
return unless enabled
+ stub_object_storage(connection_params: uploader.object_store_credentials,
+ remote_directory: remote_directory)
+ end
+
+ def stub_object_storage(connection_params:, remote_directory:)
Fog.mock!
- ::Fog::Storage.new(uploader.object_store_credentials).tap do |connection|
+ ::Fog::Storage.new(connection_params).tap do |connection|
begin
connection.directories.create(key: remote_directory)
rescue Excon::Error::Conflict
diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
index 5241c0fa6f1..a8f2c2e7a5a 100644
--- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
+++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
@@ -5,6 +5,12 @@ shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
+ find(".js-allowed-to-merge").click
+ within('.qa-allowed-to-merge-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
within('.js-new-protected-branch') do
allowed_to_push_button = find(".js-allowed-to-push")
@@ -25,6 +31,18 @@ shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
+ find(".js-allowed-to-merge").click
+ within('.qa-allowed-to-merge-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
+ find(".js-allowed-to-push").click
+ within('.qa-allowed-to-push-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
click_on "Protect"
expect(ProtectedBranch.count).to eq(1)
@@ -59,6 +77,12 @@ shared_examples "protected branches > access control > CE" do
end
end
+ find(".js-allowed-to-push").click
+ within('.qa-allowed-to-push-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
click_on "Protect"
expect(ProtectedBranch.count).to eq(1)
@@ -70,6 +94,18 @@ shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
+ find(".js-allowed-to-merge").click
+ within('.qa-allowed-to-merge-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
+ find(".js-allowed-to-push").click
+ within('.qa-allowed-to-push-dropdown') do
+ expect(first("li")).to have_content("Roles")
+ find(:link, 'No one').click
+ end
+
click_on "Protect"
expect(ProtectedBranch.count).to eq(1)