summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStan Hu <stanhu@gmail.com>2018-07-27 16:58:14 -0700
committerStan Hu <stanhu@gmail.com>2018-07-27 16:58:14 -0700
commita91ba2287ce7adebb5e0eb33f4335d4ce5f054ed (patch)
tree46343419e74ea762869225b982850ac97ed67ea4
parent57d1b60f61a790c034b8d43ce2358371464d2d67 (diff)
parent35ce06add8ee414e2694017126f12d47e9c9bf27 (diff)
downloadgitlab-ce-a91ba2287ce7adebb5e0eb33f4335d4ce5f054ed.tar.gz
Merge branch 'master' into sh-support-bitbucket-server-import
-rw-r--r--.gitlab-ci.yml7
-rw-r--r--.rubocop.yml6
-rw-r--r--CHANGELOG.md50
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock9
-rw-r--r--Gemfile.rails5.lock19
-rw-r--r--app/assets/javascripts/badges/components/badge.vue2
-rw-r--r--app/assets/javascripts/boards/index.js149
-rw-r--r--app/assets/javascripts/commons/gitlab_ui.js4
-rw-r--r--app/assets/javascripts/commons/index.js1
-rw-r--r--app/assets/javascripts/ide/components/changed_file_icon.vue20
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/actions.vue3
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue10
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_review.vue13
-rw-r--r--app/assets/javascripts/ide/components/ide_tree.vue8
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue6
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue52
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue17
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue8
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue6
-rw-r--r--app/assets/javascripts/ide/constants.js15
-rw-r--r--app/assets/javascripts/ide/lib/common/model.js2
-rw-r--r--app/assets/javascripts/ide/services/index.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js8
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js8
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js6
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js12
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/getters.js26
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js11
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js20
-rw-r--r--app/assets/javascripts/ide/stores/utils.js29
-rw-r--r--app/assets/javascripts/ide/utils.js12
-rw-r--r--app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue3
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_component.vue5
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue19
-rw-r--r--app/assets/javascripts/vue_shared/components/bar_chart.vue391
-rw-r--r--app/assets/javascripts/vue_shared/components/bar_chart_constants.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/panel_resizer.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/constants.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/issue_body.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue58
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/issues_list.vue64
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/report_issues.vue55
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/report_section.vue31
-rw-r--r--app/assets/javascripts/vue_shared/components/svg_gradient.vue37
-rw-r--r--app/assets/stylesheets/framework/common.scss7
-rw-r--r--app/assets/stylesheets/framework/header.scss3
-rw-r--r--app/assets/stylesheets/framework/typography.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss (renamed from app/assets/stylesheets/pages/repo.scss)79
-rw-r--r--app/assets/stylesheets/pages/builds.scss1
-rw-r--r--app/assets/stylesheets/pages/diff.scss1
-rw-r--r--app/assets/stylesheets/pages/graph.scss58
-rw-r--r--app/assets/stylesheets/pages/issuable.scss22
-rw-r--r--app/assets/stylesheets/pages/login.scss2
-rw-r--r--app/assets/stylesheets/snippets.scss4
-rw-r--r--app/controllers/projects/commits_controller.rb10
-rw-r--r--app/controllers/projects/labels_controller.rb11
-rw-r--r--app/controllers/projects/milestones_controller.rb11
-rw-r--r--app/finders/admin/projects_finder.rb4
-rw-r--r--app/graphql/gitlab_schema.rb2
-rw-r--r--app/graphql/mutations/base_mutation.rb13
-rw-r--r--app/graphql/mutations/concerns/mutations/resolves_project.rb13
-rw-r--r--app/graphql/mutations/merge_requests/base.rb32
-rw-r--r--app/graphql/mutations/merge_requests/set_wip.rb35
-rw-r--r--app/graphql/types/mutation_type.rb6
-rw-r--r--app/helpers/environments_helper.rb19
-rw-r--r--app/helpers/hooks_helper.rb2
-rw-r--r--app/helpers/snippets_helper.rb2
-rw-r--r--app/mailers/notify.rb4
-rw-r--r--app/models/clusters/applications/prometheus.rb8
-rw-r--r--app/models/concerns/prometheus_adapter.rb15
-rw-r--r--app/models/concerns/reactive_caching.rb10
-rw-r--r--app/models/concerns/routable.rb44
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb13
-rw-r--r--app/models/deploy_token.rb5
-rw-r--r--app/models/email.rb4
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/project.rb8
-rw-r--r--app/models/project_feature.rb26
-rw-r--r--app/models/remote_mirror.rb6
-rw-r--r--app/models/repository.rb2
-rw-r--r--app/models/site_statistic.rb74
-rw-r--r--app/policies/concerns/policy_actor.rb36
-rw-r--r--app/serializers/pipeline_serializer.rb9
-rw-r--r--app/services/clusters/applications/check_installation_progress_service.rb6
-rw-r--r--app/services/projects/transfer_service.rb1
-rw-r--r--app/services/prometheus/adapter_service.rb2
-rw-r--r--app/views/admin/projects/_projects.html.haml2
-rw-r--r--app/views/ide/index.html.haml3
-rw-r--r--app/views/layouts/_head.html.haml5
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml4
-rw-r--r--app/views/projects/environments/metrics.html.haml22
-rw-r--r--app/views/projects/jobs/_sidebar.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml2
-rw-r--r--app/views/shared/snippets/_embed.html.haml2
-rw-r--r--app/workers/concerns/gitlab/github_import/object_importer.rb2
-rw-r--r--app/workers/repository_fork_worker.rb4
-rw-r--r--app/workers/repository_import_worker.rb4
-rw-r--r--changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml5
-rw-r--r--changelogs/unreleased/32783-api-all-members-with-ancestors.yml6
-rw-r--r--changelogs/unreleased/4525-fix-project-indexes.yml5
-rw-r--r--changelogs/unreleased/48055-web-ide-resize-handles.yml5
-rw-r--r--changelogs/unreleased/48542-code-link.yml5
-rw-r--r--changelogs/unreleased/48617-promoting-milestone.yml5
-rw-r--r--changelogs/unreleased/48636-new-mr-card-styles.yml5
-rw-r--r--changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml5
-rw-r--r--changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml5
-rw-r--r--changelogs/unreleased/49364-fix-broadcast-margin.yml5
-rw-r--r--changelogs/unreleased/_acet-fix-expanding-context-lines.yml5
-rw-r--r--changelogs/unreleased/_acet-fix-mr-autosave.yml5
-rw-r--r--changelogs/unreleased/_acet-fix-outdated-discussions.yml5
-rw-r--r--changelogs/unreleased/bvl-graphql-wip-mutation.yml5
-rw-r--r--changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml5
-rw-r--r--changelogs/unreleased/custom_wiki_sidebar.yml (renamed from changelogs/custom_wiki_sidebar.yml)0
-rw-r--r--changelogs/unreleased/fix-email-confirmation-addtional-email.yml5
-rw-r--r--changelogs/unreleased/full-list-of-vulnerabilities-5239.yml5
-rw-r--r--changelogs/unreleased/ide-delete-entries.yml5
-rw-r--r--changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml5
-rw-r--r--changelogs/unreleased/project-dropdown-list-overflow.yml5
-rw-r--r--changelogs/unreleased/rails5-update-gemfile-lock-2.yml5
-rw-r--r--changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml5
-rw-r--r--changelogs/unreleased/security-ide-branch-name-xss.yml5
-rw-r--r--changelogs/unreleased/sh-bump-haml-5-0-4.yml5
-rw-r--r--changelogs/unreleased/sh-include-rbtrace.yml5
-rw-r--r--changelogs/unreleased/sh-revert-markdown-changes.yml5
-rw-r--r--changelogs/unreleased/stop-dynamic-routable-creation.yml5
-rw-r--r--changelogs/unreleased/tc-reorder-mail-notify-references.yml5
-rw-r--r--changelogs/unreleased/tz-mr-refactor-memory-reduction.yml5
-rw-r--r--changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml5
-rw-r--r--changelogs/unreleased/zj-backup-timeout.yml5
-rw-r--r--config/application.rb5
-rw-r--r--config/dependency_decisions.yml6
-rw-r--r--config/initializers/rbtrace.rb3
-rw-r--r--config/routes/admin.rb2
-rw-r--r--config/routes/project.rb2
-rw-r--r--config/routes/repository.rb1
-rw-r--r--danger/database/Dangerfile4
-rw-r--r--danger/frozen_string/Dangerfile11
-rw-r--r--db/migrate/20140313092127_init_schema.rb9
-rw-r--r--db/migrate/20140407135544_fix_namespaces.rb1
-rw-r--r--db/migrate/20140415124820_limits_to_mysql.rb1
-rw-r--r--db/migrate/20140729145339_migrate_project_tags.rb1
-rw-r--r--db/migrate/20141121133009_add_timestamps_to_members.rb1
-rw-r--r--db/migrate/20141223135007_add_import_data_to_project_table.rb1
-rw-r--r--db/migrate/20150116234544_add_home_page_url_for_application_settings.rb1
-rw-r--r--db/migrate/20150116234545_add_gitlab_access_token_to_user.rb1
-rw-r--r--db/migrate/20150206222854_add_notification_email_to_user.rb1
-rw-r--r--db/migrate/20150211174341_allow_null_in_services_project_id.rb1
-rw-r--r--db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb1
-rw-r--r--db/migrate/20150223022001_set_missing_last_activity_at.rb1
-rw-r--r--db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb1
-rw-r--r--db/migrate/20150320234437_add_location_to_user.rb1
-rw-r--r--db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb1
-rw-r--r--db/migrate/20150327150017_add_import_data_to_project.rb1
-rw-r--r--db/migrate/20150327223628_add_devise_two_factor_to_users.rb1
-rw-r--r--db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb1
-rw-r--r--db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb1
-rw-r--r--db/migrate/20150411000035_fix_identities.rb1
-rw-r--r--db/migrate/20150411180045_rename_buildbox_service.rb1
-rw-r--r--db/migrate/20150417121913_create_project_import_data.rb1
-rw-r--r--db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb1
-rw-r--r--db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb1
-rw-r--r--db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb1
-rw-r--r--db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb1
-rw-r--r--db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb1
-rw-r--r--db/migrate/20150429002313_remove_abandoned_group_members_records.rb1
-rw-r--r--db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb1
-rw-r--r--db/migrate/20150509180749_convert_legacy_reference_notes.rb1
-rw-r--r--db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb1
-rw-r--r--db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb1
-rw-r--r--db/migrate/20150620233230_add_default_otp_required_for_login_value.rb1
-rw-r--r--db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb1
-rw-r--r--db/migrate/20150818213832_add_sent_notifications.rb1
-rw-r--r--db/migrate/20150915001905_enable_ssl_verification_by_default.rb1
-rw-r--r--db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb1
-rw-r--r--db/migrate/20150916114643_add_help_page_text_to_application_settings.rb1
-rw-r--r--db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb1
-rw-r--r--db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb1
-rw-r--r--db/migrate/20150920010715_add_consumed_timestep_to_users.rb1
-rw-r--r--db/migrate/20150920161119_add_line_code_to_sent_notification.rb1
-rw-r--r--db/migrate/20150924125150_add_project_id_to_ci_commit.rb1
-rw-r--r--db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb1
-rw-r--r--db/migrate/20150930001110_merge_request_error_field.rb1
-rw-r--r--db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb1
-rw-r--r--db/migrate/20151002112914_add_stage_idx_to_builds.rb1
-rw-r--r--db/migrate/20151002122929_add_ref_and_tag_to_builds.rb1
-rw-r--r--db/migrate/20151005075649_add_user_id_to_build.rb1
-rw-r--r--db/migrate/20151008143519_add_admin_notification_email_setting.rb1
-rw-r--r--db/migrate/20151013092124_add_artifacts_file_to_builds.rb1
-rw-r--r--db/migrate/20151019111551_fix_build_tags.rb1
-rw-r--r--db/migrate/20151019111703_fail_build_without_names.rb1
-rw-r--r--db/migrate/20151020173516_ci_limits_to_mysql.rb1
-rw-r--r--db/migrate/20151023112551_fail_build_with_empty_name.rb1
-rw-r--r--db/migrate/20151023144219_remove_satellites.rb1
-rw-r--r--db/migrate/20151103133339_add_shared_runners_setting.rb1
-rw-r--r--db/migrate/20151104105513_add_file_to_lfs_objects.rb1
-rw-r--r--db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb1
-rw-r--r--db/migrate/20151110125604_add_import_error_to_project.rb1
-rw-r--r--db/migrate/20151201203948_raise_hook_url_limit.rb1
-rw-r--r--db/migrate/20151209144329_migrate_ci_web_hooks.rb1
-rw-r--r--db/migrate/20151209145909_migrate_ci_emails.rb1
-rw-r--r--db/migrate/20151210030143_add_unlock_token_to_user.rb1
-rw-r--r--db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb1
-rw-r--r--db/migrate/20151210125232_migrate_ci_slack_service.rb1
-rw-r--r--db/migrate/20151210125927_migrate_ci_hip_chat_service.rb1
-rw-r--r--db/migrate/20151210125929_add_project_id_to_ci.rb1
-rw-r--r--db/migrate/20151210125930_migrate_ci_to_project.rb1
-rw-r--r--db/migrate/20151218154042_add_tfa_to_application_settings.rb1
-rw-r--r--db/migrate/20151221234414_add_tfa_additional_fields.rb1
-rw-r--r--db/migrate/20151224123230_rename_emojis.rb1
-rw-r--r--db/migrate/20151228175719_add_recaptcha_to_application_settings.rb1
-rw-r--r--db/migrate/20151229102248_influxdb_udp_port_setting.rb1
-rw-r--r--db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb1
-rw-r--r--db/migrate/20151231152326_add_akismet_to_application_settings.rb1
-rw-r--r--db/migrate/20160113111034_add_metrics_sample_interval.rb1
-rw-r--r--db/migrate/20160118155830_add_sentry_to_application_settings.rb1
-rw-r--r--db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb1
-rw-r--r--db/migrate/20160128233227_change_lfs_objects_size_column.rb1
-rw-r--r--db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb1
-rw-r--r--db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb1
-rw-r--r--db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb1
-rw-r--r--db/migrate/20160217100506_add_description_to_label.rb1
-rw-r--r--db/migrate/20160217174422_add_note_to_tasks.rb1
-rw-r--r--db/migrate/20160220123949_rename_tasks_to_todos.rb1
-rw-r--r--db/migrate/20160229193553_add_main_language_to_repository.rb1
-rw-r--r--db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb1
-rw-r--r--db/migrate/20160307221555_disallow_blank_line_code_on_note.rb1
-rw-r--r--db/migrate/20160316192622_change_target_id_to_null_on_todos.rb1
-rw-r--r--db/migrate/20160317092222_add_moved_to_to_issue.rb1
-rw-r--r--db/migrate/20160324020319_remove_todos_for_deleted_issues.rb1
-rw-r--r--db/migrate/20160328115649_migrate_new_notification_setting.rb1
-rw-r--r--db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb1
-rw-r--r--db/migrate/20160407120251_add_images_enabled_for_project.rb1
-rw-r--r--db/migrate/20160413115152_add_token_to_web_hooks.rb1
-rw-r--r--db/migrate/20160415062917_create_personal_access_tokens.rb1
-rw-r--r--db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb1
-rw-r--r--db/migrate/20160416182152_convert_award_note_to_emoji_award.rb1
-rw-r--r--db/migrate/20160419120017_add_metrics_packet_size.rb1
-rw-r--r--db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb1
-rw-r--r--db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb1
-rw-r--r--db/migrate/20160508215820_add_type_to_notes.rb1
-rw-r--r--db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb1
-rw-r--r--db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb1
-rw-r--r--db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb1
-rw-r--r--db/migrate/20160603180330_remove_duplicated_notification_settings.rb1
-rw-r--r--db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb1
-rw-r--r--db/migrate/20160610204157_add_deployments.rb2
-rw-r--r--db/migrate/20160610204158_add_environments.rb2
-rw-r--r--db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb1
-rw-r--r--db/migrate/20160616102642_remove_duplicated_keys.rb1
-rw-r--r--db/migrate/20160824124900_add_table_issue_metrics.rb2
-rw-r--r--db/migrate/20160825052008_add_table_merge_request_metrics.rb2
-rw-r--r--db/migrate/20161113184239_create_user_chat_names_table.rb1
-rw-r--r--db/migrate/20170130221926_create_uploads.rb1
-rw-r--r--db/migrate/20170222143317_drop_ci_projects.rb1
-rw-r--r--db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb1
-rw-r--r--db/migrate/20170329095907_create_ci_trigger_schedules.rb1
-rw-r--r--db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb2
-rw-r--r--db/migrate/20170425112128_create_pipeline_schedules_table.rb1
-rw-r--r--db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb1
-rw-r--r--db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb1
-rw-r--r--db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb1
-rw-r--r--db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb1
-rw-r--r--db/migrate/20180214093516_create_badges.rb1
-rw-r--r--db/migrate/20180227182112_add_group_id_to_boards_ce.rb2
-rw-r--r--db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb1
-rw-r--r--db/migrate/20180309160427_add_partial_indexes_on_todos.rb11
-rw-r--r--db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb1
-rw-r--r--db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb1
-rw-r--r--db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb1
-rw-r--r--db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb1
-rw-r--r--db/migrate/20180425075446_create_term_agreements.rb1
-rw-r--r--db/migrate/20180503131624_create_remote_mirrors.rb1
-rw-r--r--db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb1
-rw-r--r--db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb1
-rw-r--r--db/migrate/20180503193542_add_indexes_to_remote_mirror.rb1
-rw-r--r--db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb1
-rw-r--r--db/migrate/20180515005612_add_squash_to_merge_requests.rb1
-rw-r--r--db/migrate/20180515121227_create_notes_diff_files.rb1
-rw-r--r--db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb1
-rw-r--r--db/migrate/20180529093006_ensure_remote_mirror_columns.rb1
-rw-r--r--db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb4
-rw-r--r--db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb1
-rw-r--r--db/migrate/20180629153018_create_site_statistics.rb18
-rw-r--r--db/migrate/20180702124358_remove_orphaned_routes.rb49
-rw-r--r--db/migrate/20180702134423_generate_missing_routes.rb143
-rw-r--r--db/migrate/gpg_keys_limits_to_mysql.rb1
-rw-r--r--db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb2
-rw-r--r--db/migrate/limits_to_mysql.rb1
-rw-r--r--db/optional_migrations/composite_primary_keys.rb6
-rw-r--r--db/post_migrate/20161221153951_rename_reserved_project_names.rb2
-rw-r--r--db/post_migrate/20170313133418_rename_more_reserved_project_names.rb2
-rw-r--r--db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb1
-rw-r--r--db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb1
-rw-r--r--db/post_migrate/20170711145558_migrate_stages_statuses.rb1
-rw-r--r--db/post_migrate/20170830150306_drop_events_for_migration_table.rb1
-rw-r--r--db/post_migrate/20171106154015_remove_issues_branch_name.rb1
-rw-r--r--db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb1
-rw-r--r--db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb1
-rw-r--r--db/post_migrate/20180223124427_build_user_interacted_projects_table.rb3
-rw-r--r--db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb1
-rw-r--r--db/post_migrate/20180424151928_fill_file_store.rb2
-rw-r--r--db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb1
-rw-r--r--db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb1
-rw-r--r--db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb2
-rw-r--r--db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb1
-rw-r--r--db/post_migrate/20180704145007_update_project_indexes.rb2
-rw-r--r--db/post_migrate/20180706223200_populate_site_statistics.rb25
-rw-r--r--db/schema.rb5
-rw-r--r--doc/administration/troubleshooting/debug.md13
-rw-r--r--doc/api/members.md75
-rw-r--r--doc/development/api_graphql_styleguide.md174
-rw-r--r--lib/api/helpers/members_helpers.rb25
-rw-r--r--lib/api/members.rb17
-rw-r--r--lib/api/runner.rb6
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb4
-rw-r--r--lib/gitlab/email/handler/create_merge_request_handler.rb4
-rw-r--r--lib/gitlab/email/handler/create_note_handler.rb4
-rw-r--r--lib/gitlab/email/handler/unsubscribe_handler.rb4
-rw-r--r--lib/gitlab/git/conflict/resolver.rb2
-rw-r--r--lib/gitlab/git/repository.rb30
-rw-r--r--lib/gitlab/git/repository_mirroring.rb4
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_importer.rb2
-rw-r--r--lib/gitlab/graphql/authorize.rb9
-rw-r--r--lib/gitlab/graphql/authorize/authorize_resource.rb46
-rw-r--r--lib/gitlab/graphql/errors.rb1
-rw-r--r--lib/gitlab/graphql/mount_mutation.rb18
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb6
-rw-r--r--lib/gitlab/kubernetes/config_map.rb10
-rw-r--r--lib/gitlab/kubernetes/helm/api.rb20
-rw-r--r--lib/gitlab/prometheus/metric.rb2
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb1
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb1
-rw-r--r--lib/gitlab/prometheus/queries/query_additional_metrics.rb8
-rw-r--r--locale/gitlab.pot17
-rw-r--r--package.json4
-rw-r--r--qa/qa/runtime/browser.rb1
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb12
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb8
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb8
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb14
-rw-r--r--spec/factories/projects.rb2
-rw-r--r--spec/factories/site_statistics.rb7
-rw-r--r--spec/features/projects/jobs_spec.rb14
-rw-r--r--spec/features/users/add_email_to_existing_account.rb15
-rw-r--r--spec/fixtures/emails/commands_in_reply.eml2
-rw-r--r--spec/fixtures/emails/commands_only_reply.eml2
-rw-r--r--spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml2
-rw-r--r--spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml2
-rw-r--r--spec/fixtures/emails/update_commands_only_reply.eml2
-rw-r--r--spec/fixtures/emails/valid_reply.eml2
-rw-r--r--spec/graphql/gitlab_schema_spec.rb2
-rw-r--r--spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb19
-rw-r--r--spec/graphql/mutations/merge_requests/set_wip_spec.rb51
-rw-r--r--spec/graphql/types/mutation_type_spec.rb9
-rw-r--r--spec/helpers/snippets_helper_spec.rb4
-rw-r--r--spec/javascripts/fixtures/graph.html.haml1
-rw-r--r--spec/javascripts/helpers/vue_mount_component_helper.js10
-rw-r--r--spec/javascripts/ide/components/changed_file_icon_spec.js8
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/actions_spec.js8
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_item_spec.js16
-rw-r--r--spec/javascripts/ide/components/new_dropdown/index_spec.js11
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js3
-rw-r--r--spec/javascripts/ide/components/repo_file_spec.js19
-rw-r--r--spec/javascripts/ide/components/repo_tab_spec.js4
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js17
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js16
-rw-r--r--spec/javascripts/ide/stores/modules/commit/getters_spec.js16
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js64
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js57
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js73
-rw-r--r--spec/javascripts/pipelines/graph/dropdown_job_component_spec.js93
-rw-r--r--spec/javascripts/pipelines/graph/graph_component_spec.js43
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js26
-rw-r--r--spec/javascripts/pipelines/graph/mock_data.js2
-rw-r--r--spec/javascripts/pipelines/graph/stage_column_component_spec.js37
-rw-r--r--spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js17
-rw-r--r--spec/javascripts/test_bundle.js13
-rw-r--r--spec/javascripts/vue_shared/components/bar_chart_spec.js85
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/reports/report_issues_spec.js0
-rw-r--r--spec/javascripts/vue_shared/components/reports/report_section_spec.js33
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb1
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb103
-rw-r--r--spec/lib/gitlab/graphql/authorize_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/config_map_spec.rb6
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb12
-rw-r--r--spec/migrations/generate_missing_routes_spec.rb84
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb57
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb7
-rw-r--r--spec/models/concerns/routable_spec.rb33
-rw-r--r--spec/models/namespace_spec.rb38
-rw-r--r--spec/models/project_feature_spec.rb42
-rw-r--r--spec/models/project_spec.rb20
-rw-r--r--spec/models/repository_spec.rb6
-rw-r--r--spec/models/site_statistic_spec.rb83
-rw-r--r--spec/policies/concerns/policy_actor_spec.rb13
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb68
-rw-r--r--spec/requests/api/members_spec.rb113
-rw-r--r--spec/routing/admin_routing_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb2
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb4
-rw-r--r--spec/services/projects/transfer_service_spec.rb6
-rw-r--r--spec/support/helpers/graphql_helpers.rb61
-rw-r--r--spec/support/matchers/graphql_matchers.rb9
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb2
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb33
-rw-r--r--spec/workers/concerns/gitlab/github_import/object_importer_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_issue_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_note_worker_spec.rb1
-rw-r--r--spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb1
-rw-r--r--yarn.lock282
426 files changed, 4055 insertions, 982 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c7ef75d1c32..fcf47421b01 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -439,13 +439,12 @@ setup-test-env:
- vendor/gitaly-ruby
danger-review:
- image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
stage: test
allow_failure: true
- before_script:
- - source scripts/utils.sh
- - retry gem install danger --no-ri --no-rdoc
cache: {}
+ dependencies: []
+ before_script: []
only:
variables:
- $DANGER_GITLAB_API_TOKEN
diff --git a/.rubocop.yml b/.rubocop.yml
index 0582bfe8d70..c8b1ce327e2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -10,9 +10,9 @@ AllCops:
Exclude:
- 'vendor/**/*'
- 'node_modules/**/*'
- - 'db/**/*'
- 'db/fixtures/**/*'
- - 'ee/db/**/*'
+ - 'db/schema.rb'
+ - 'ee/db/geo/schema.rb'
- 'tmp/**/*'
- 'bin/**/*'
- 'generator_templates/**/*'
@@ -34,6 +34,8 @@ Style/MutableConstant:
Naming/FileName:
ExpectMatchingDefinition: true
Exclude:
+ - 'db/**/*'
+ - 'ee/db/**/*'
- 'spec/**/*'
- 'features/**/*'
- 'ee/spec/**/*'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cc047c40ed..b4b672b55c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,42 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.1.3 (2018-07-27)
+
+### Fixed (8 changes, 1 of them is from the community)
+
+- Rework some projects table indexes around repository_storage field. !20377
+- Fix navigation to First and Next discussion on MR Changes tab. !20434
+- Fix showing outdated discussions on Changes tab. !20445
+- Fix autosave and ESC confirmation issues for MR discussions. !20569
+- Fix rendering of the context lines in MR diffs page. !20642
+- Don't overflow project/group dropdown results. !20704 (gfyoung)
+- Fixed IDE not opening JSON files. !20798
+- Disable Gitaly timeouts when creating or restoring backups. !20810
+
+### Performance (1 change)
+
+- Reduces the client side memory footprint on merge requests. !20744
+
+
+## 11.1.2 (2018-07-26)
+
+### Security (4 changes)
+
+- Adding CSRF protection to Hooks test action.
+- Don't expose project names in GitHub counters.
+- Don't expose project names in various counters.
+- Fixed XSS in branch name in Web IDE.
+
+### Fixed (1 change)
+
+- Escapes milestone and label's names on flash notice when promoting them.
+
+### Performance (1 change)
+
+- Fix slow Markdown rendering. !20820
+
+
## 11.1.1 (2018-07-23)
### Fixed (2 changes)
@@ -253,6 +289,20 @@ entry.
- Use monospaced font for MR diff commit link ref on GFM.
+## 11.0.5 (2018-07-26)
+
+### Security (4 changes)
+
+- Don't expose project names in various counters.
+- Don't expose project names in GitHub counters.
+- Adding CSRF protection to Hooks test action.
+- Fixed XSS in branch name in Web IDE.
+
+### Fixed (1 change)
+
+- Escapes milestone and label's names on flash notice when promoting them.
+
+
## 11.0.4 (2018-07-17)
### Security (1 change)
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 69adf3456f8..0ee843cc604 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-7.1.5
+7.2.0
diff --git a/Gemfile b/Gemfile
index 74e9625d57b..b330127cb82 100644
--- a/Gemfile
+++ b/Gemfile
@@ -400,6 +400,7 @@ gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
gem 'ruby-prof', '~> 0.17.0'
+gem 'rbtrace', '~> 0.4', require: false
# OAuth
gem 'oauth2', '~> 1.4'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5bb7bc53962..d147f81c81e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -375,7 +375,8 @@ GEM
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
- haml (4.0.7)
+ haml (5.0.4)
+ temple (>= 0.8.0)
tilt
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
@@ -700,6 +701,10 @@ GEM
ffi (>= 0.5.0, < 2)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
+ rbtrace (0.4.10)
+ ffi (>= 1.0.6)
+ msgpack (>= 0.4.3)
+ trollop (>= 1.16.2)
rdoc (6.0.4)
re2 (1.1.1)
recaptcha (3.0.0)
@@ -911,6 +916,7 @@ GEM
parslet (~> 1.5.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
+ trollop (2.1.3)
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
@@ -1133,6 +1139,7 @@ DEPENDENCIES
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
+ rbtrace (~> 0.4)
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 8c14316a67a..b055b561b61 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -364,7 +364,7 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- grape-path-helpers (1.0.5)
+ grape-path-helpers (1.0.6)
activesupport (>= 4, < 5.1)
grape (~> 1.0)
rake (~> 12)
@@ -378,7 +378,8 @@ GEM
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
- haml (4.0.7)
+ haml (5.0.4)
+ temple (>= 0.8.0)
tilt
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
@@ -400,7 +401,7 @@ GEM
hipchat (1.5.2)
httparty
mimemagic
- html-pipeline (2.8.3)
+ html-pipeline (2.8.4)
activesupport (>= 2)
nokogiri (>= 1.4)
html2text (0.2.0)
@@ -518,7 +519,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
- nokogiri (1.8.3)
+ nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -709,6 +710,10 @@ GEM
ffi (>= 0.5.0, < 2)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
+ rbtrace (0.4.10)
+ ffi (>= 1.0.6)
+ msgpack (>= 0.4.3)
+ trollop (>= 1.16.2)
rdoc (6.0.4)
re2 (1.1.1)
recaptcha (3.0.0)
@@ -817,7 +822,7 @@ GEM
et-orbi (~> 1.0)
rugged (0.27.2)
safe_yaml (1.0.4)
- sanitize (4.6.5)
+ sanitize (4.6.6)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
@@ -911,13 +916,14 @@ GEM
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
- tilt (2.0.6)
+ tilt (2.0.8)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
parslet (~> 1.5.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
+ trollop (2.1.3)
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
@@ -1144,6 +1150,7 @@ DEPENDENCIES
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
+ rbtrace (~> 0.4)
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue
index b4bfaee1d85..155c348286c 100644
--- a/app/assets/javascripts/badges/components/badge.vue
+++ b/app/assets/javascripts/badges/components/badge.vue
@@ -93,7 +93,7 @@ export default {
<icon
:size="16"
class="prepend-left-8 append-right-8"
- name="doc_image"
+ name="doc-image"
aria-hidden="true"
/>
</div>
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 200d1923635..bc263cbbfea 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,5 +1,3 @@
-/* eslint-disable quote-props, comma-dangle */
-
import $ from 'jquery';
import _ from 'underscore';
import Vue from 'vue';
@@ -47,7 +45,7 @@ export default () => {
gl.IssueBoardsApp = new Vue({
el: $boardApp,
components: {
- 'board': gl.issueBoards.Board,
+ board: gl.issueBoards.Board,
'board-sidebar': gl.issueBoards.BoardSidebar,
BoardAddIssuesModal,
},
@@ -65,11 +63,11 @@ export default () => {
defaultAvatar: $boardApp.dataset.defaultAvatar,
},
computed: {
- detailIssueVisible () {
+ detailIssueVisible() {
return Object.keys(this.detailIssue.issue).length;
},
},
- created () {
+ created() {
gl.boardService = new BoardService({
boardsEndpoint: this.boardsEndpoint,
listsEndpoint: this.listsEndpoint,
@@ -89,15 +87,16 @@ export default () => {
eventHub.$off('clearDetailIssue', this.clearDetailIssue);
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
},
- mounted () {
+ mounted() {
this.filterManager = new FilteredSearchBoards(Store.filter, true, Store.cantEdit);
this.filterManager.setup();
Store.disabled = this.disabled;
- gl.boardService.all()
+ gl.boardService
+ .all()
.then(res => res.data)
- .then((data) => {
- data.forEach((board) => {
+ .then(data => {
+ data.forEach(board => {
const list = Store.addList(board, this.defaultAvatar);
if (list.type === 'closed') {
@@ -126,7 +125,7 @@ export default () => {
newIssue.setFetchingState('subscriptions', true);
BoardService.getIssueInfo(sidebarInfoEndpoint)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
newIssue.setFetchingState('subscriptions', false);
newIssue.updateData({
subscribed: data.subscribed,
@@ -159,7 +158,7 @@ export default () => {
Flash(__('An error occurred when toggling the notification subscription'));
});
}
- }
+ },
},
});
@@ -168,77 +167,81 @@ export default () => {
data: {
filters: Store.state.filters,
},
- mounted () {
+ mounted() {
gl.issueBoards.newListDropdownInit();
},
});
- gl.IssueBoardsModalAddBtn = new Vue({
- el: document.getElementById('js-add-issues-btn'),
- mixins: [modalMixin],
- data() {
- return {
- modal: ModalStore.store,
- store: Store.state,
- canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
- };
- },
- computed: {
- disabled() {
- if (!this.store) {
- return true;
- }
- return !this.store.lists.filter(list => !list.preset).length;
+ const issueBoardsModal = document.getElementById('js-add-issues-btn');
+
+ if (issueBoardsModal) {
+ gl.IssueBoardsModalAddBtn = new Vue({
+ el: issueBoardsModal,
+ mixins: [modalMixin],
+ data() {
+ return {
+ modal: ModalStore.store,
+ store: Store.state,
+ canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
+ };
},
- tooltipTitle() {
- if (this.disabled) {
- return 'Please add a list to your board first';
- }
+ computed: {
+ disabled() {
+ if (!this.store) {
+ return true;
+ }
+ return !this.store.lists.filter(list => !list.preset).length;
+ },
+ tooltipTitle() {
+ if (this.disabled) {
+ return 'Please add a list to your board first';
+ }
- return '';
+ return '';
+ },
},
- },
- watch: {
- disabled() {
+ watch: {
+ disabled() {
+ this.updateTooltip();
+ },
+ },
+ mounted() {
this.updateTooltip();
},
- },
- mounted() {
- this.updateTooltip();
- },
- methods: {
- updateTooltip() {
- const $tooltip = $(this.$refs.addIssuesButton);
-
- this.$nextTick(() => {
- if (this.disabled) {
- $tooltip.tooltip();
- } else {
- $tooltip.tooltip('dispose');
+ methods: {
+ updateTooltip() {
+ const $tooltip = $(this.$refs.addIssuesButton);
+
+ this.$nextTick(() => {
+ if (this.disabled) {
+ $tooltip.tooltip();
+ } else {
+ $tooltip.tooltip('dispose');
+ }
+ });
+ },
+ openModal() {
+ if (!this.disabled) {
+ this.toggleModal(true);
}
- });
- },
- openModal() {
- if (!this.disabled) {
- this.toggleModal(true);
- }
+ },
},
- },
- template: `
- <div class="board-extra-actions">
- <button
- class="btn btn-create prepend-left-10"
- type="button"
- data-placement="bottom"
- ref="addIssuesButton"
- :class="{ 'disabled': disabled }"
- :title="tooltipTitle"
- :aria-disabled="disabled"
- v-if="canAdminList"
- @click="openModal">
- Add issues
- </button>
- </div>
- `,
- });
+ template: `
+ <div class="board-extra-actions">
+ <button
+ class="btn btn-create prepend-left-10"
+ type="button"
+ data-placement="bottom"
+ ref="addIssuesButton"
+ :class="{ 'disabled': disabled }"
+ :title="tooltipTitle"
+ :aria-disabled="disabled"
+ v-if="canAdminList"
+ @click="openModal">
+ Add issues
+ </button>
+ </div>
+ `,
+ });
+ }
};
diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js
new file mode 100644
index 00000000000..923c036f5a4
--- /dev/null
+++ b/app/assets/javascripts/commons/gitlab_ui.js
@@ -0,0 +1,4 @@
+import Vue from 'vue';
+import progressBar from '@gitlab-org/gitlab-ui/dist/base/progress_bar';
+
+Vue.component('gl-progress-bar', progressBar);
diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js
index 0d2fe2925d8..ea945cd3fa5 100644
--- a/app/assets/javascripts/commons/index.js
+++ b/app/assets/javascripts/commons/index.js
@@ -3,4 +3,5 @@ import './polyfills';
import './jquery';
import './bootstrap';
import './vue';
+import './gitlab_ui';
import '../lib/utils/axios_utils';
diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue
index a4e06bbbe3c..720ae11aaa6 100644
--- a/app/assets/javascripts/ide/components/changed_file_icon.vue
+++ b/app/assets/javascripts/ide/components/changed_file_icon.vue
@@ -3,6 +3,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import { pluralize } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
+import { getCommitIconMap } from '../utils';
export default {
components: {
@@ -34,16 +35,14 @@ export default {
},
computed: {
changedIcon() {
- const suffix = this.file.staged && !this.showStagedIcon ? '-solid' : '';
- return this.file.tempFile && !this.forceModifiedIcon
- ? `file-addition${suffix}`
- : `file-modified${suffix}`;
- },
- stagedIcon() {
- return `${this.changedIcon}-solid`;
+ const suffix = !this.file.changed && this.file.staged && !this.showStagedIcon ? '-solid' : '';
+
+ if (this.forceModifiedIcon) return `file-modified${suffix}`;
+
+ return `${getCommitIconMap(this.file).icon}${suffix}`;
},
changedIconClass() {
- return `multi-${this.changedIcon} float-left`;
+ return `ide-${this.changedIcon} float-left`;
},
tooltipTitle() {
if (!this.showTooltip) return undefined;
@@ -66,6 +65,9 @@ export default {
return undefined;
},
+ showIcon() {
+ return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted;
+ },
},
};
</script>
@@ -79,7 +81,7 @@ export default {
class="ide-file-changed-icon"
>
<icon
- v-if="file.changed || file.tempFile || file.staged"
+ v-if="showIcon"
:name="changedIcon"
:size="12"
:css-classes="changedIconClass"
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
index eb7cb9745ec..a8b5c7a16d0 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex';
import { sprintf, __ } from '~/locale';
import * as consts from '../../stores/modules/commit/constants';
@@ -14,7 +15,7 @@ export default {
commitToCurrentBranchText() {
return sprintf(
__('Commit to %{branchName} branch'),
- { branchName: `<strong class="monospace">${this.currentBranchId}</strong>` },
+ { branchName: `<strong class="monospace">${_.escape(this.currentBranchId)}</strong>` },
false,
);
},
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index ee21eeda3cd..391004dcd3c 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
import { viewerTypes } from '../../constants';
+import { getCommitIconMap } from '../../utils';
export default {
components: {
@@ -42,11 +43,12 @@ export default {
},
computed: {
iconName() {
- const prefix = this.stagedList ? '-solid' : '';
- return this.file.tempFile ? `file-addition${prefix}` : `file-modified${prefix}`;
+ const suffix = this.stagedList ? '-solid' : '';
+
+ return `${getCommitIconMap(this.file).icon}${suffix}`;
},
iconClass() {
- return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
+ return `${getCommitIconMap(this.file).class} append-right-8`;
},
fullKey() {
return `${this.keyPrefix}-${this.file.key}`;
@@ -67,6 +69,8 @@ export default {
'stageChange',
]),
openFileInEditor() {
+ if (this.file.type === 'tree') return null;
+
return this.openPendingTab({
file: this.file,
keyPrefix: this.keyPrefix,
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
index 7014b9f605e..e6044401c9f 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
@@ -56,7 +56,7 @@ export default {
>
<icon
:size="12"
- name="more"
+ name="ellipsis_h"
/>
</button>
<div class="dropdown-menu dropdown-menu-right">
diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue
index f9978762c45..d09c99050fe 100644
--- a/app/assets/javascripts/ide/components/ide_review.vue
+++ b/app/assets/javascripts/ide/components/ide_review.vue
@@ -10,7 +10,7 @@ export default {
EditorModeDropdown,
},
computed: {
- ...mapGetters(['currentMergeRequest']),
+ ...mapGetters(['currentMergeRequest', 'activeFile']),
...mapState(['viewer', 'currentMergeRequestId']),
showLatestChangesText() {
return !this.currentMergeRequestId || this.viewer === viewerTypes.diff;
@@ -23,12 +23,20 @@ export default {
},
},
mounted() {
+ if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) {
+ this.$router.push(`/project${this.activeFile.url}`, () => {
+ this.updateViewer('editor');
+ });
+ } else if (this.activeFile && this.activeFile.deleted) {
+ this.resetOpenFiles();
+ }
+
this.$nextTick(() => {
this.updateViewer(this.currentMergeRequestId ? viewerTypes.mr : viewerTypes.diff);
});
},
methods: {
- ...mapActions(['updateViewer']),
+ ...mapActions(['updateViewer', 'resetOpenFiles']),
},
};
</script>
@@ -36,7 +44,6 @@ export default {
<template>
<ide-tree-list
:viewer-type="viewer"
- :disable-action-dropdown="true"
header-class="ide-review-header"
>
<template
diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue
index 0a95c0bb30d..e996dd9059e 100644
--- a/app/assets/javascripts/ide/components/ide_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_tree.vue
@@ -17,14 +17,18 @@ export default {
...mapGetters(['currentProject', 'currentTree', 'activeFile']),
},
mounted() {
- if (this.activeFile && this.activeFile.pending) {
+ if (!this.activeFile) return;
+
+ if (this.activeFile.pending && !this.activeFile.deleted) {
this.$router.push(`/project${this.activeFile.url}`, () => {
this.updateViewer('editor');
});
+ } else if (this.activeFile.deleted) {
+ this.resetOpenFiles();
}
},
methods: {
- ...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry']),
+ ...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry', 'resetOpenFiles']),
},
};
</script>
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 0df99798d21..2e7226b727c 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -22,11 +22,6 @@ export default {
required: false,
default: null,
},
- disableActionDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
},
computed: {
...mapState(['currentBranchId']),
@@ -69,7 +64,6 @@ export default {
:key="file.key"
:file="file"
:level="0"
- :disable-action-dropdown="disableActionDropdown"
/>
</template>
</div>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index c29e49ba766..440e480d596 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -13,7 +13,7 @@ export default {
ItemButton,
},
props: {
- branch: {
+ type: {
type: String,
required: true,
},
@@ -45,7 +45,7 @@ export default {
},
},
methods: {
- ...mapActions(['createTempEntry', 'openNewEntryModal']),
+ ...mapActions(['createTempEntry', 'openNewEntryModal', 'deleteEntry']),
createNewItem(type) {
this.openNewEntryModal({ type, path: this.path });
this.dropdownOpen = false;
@@ -82,28 +82,40 @@ export default {
ref="dropdownMenu"
class="dropdown-menu dropdown-menu-right"
>
+ <template v-if="type === 'tree'">
+ <li>
+ <item-button
+ :label="__('New file')"
+ class="d-flex"
+ icon="doc-new"
+ icon-classes="mr-2"
+ @click="createNewItem('blob')"
+ />
+ </li>
+ <li>
+ <upload
+ :path="path"
+ @create="createTempEntry"
+ />
+ </li>
+ <li>
+ <item-button
+ :label="__('New directory')"
+ class="d-flex"
+ icon="folder-new"
+ icon-classes="mr-2"
+ @click="createNewItem('tree')"
+ />
+ </li>
+ <li class="divider"></li>
+ </template>
<li>
<item-button
- :label="__('New file')"
+ :label="__('Delete')"
class="d-flex"
- icon="doc-new"
+ icon="remove"
icon-classes="mr-2"
- @click="createNewItem('blob')"
- />
- </li>
- <li>
- <upload
- :path="path"
- @create="createTempEntry"
- />
- </li>
- <li>
- <item-button
- :label="__('New directory')"
- class="d-flex"
- icon="folder-new"
- icon-classes="mr-2"
- @click="createNewItem('tree')"
+ @click="deleteEntry(path)"
/>
</li>
</ul>
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 50ab242ba2a..6f1a941fbc4 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -44,7 +44,7 @@ export default {
},
},
mounted() {
- if (this.lastOpenedFile) {
+ if (this.lastOpenedFile && this.lastOpenedFile.type !== 'tree') {
this.openPendingTab({
file: this.lastOpenedFile,
keyPrefix: this.lastOpenedFile.changed ? stageKeys.unstaged : stageKeys.staged,
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 08ee12fd98f..f9badb01535 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -87,7 +87,9 @@ export default {
this.editor.updateDimensions();
},
viewer() {
- this.createEditorInstance();
+ if (!this.file.pending) {
+ this.createEditorInstance();
+ }
},
panelResizing() {
if (!this.panelResizing) {
@@ -109,6 +111,7 @@ export default {
},
methods: {
...mapActions([
+ 'getFileData',
'getRawFileData',
'changeFileContent',
'setFileLanguage',
@@ -123,10 +126,16 @@ export default {
this.editor.clearEditor();
- this.getRawFileData({
+ this.getFileData({
path: this.file.path,
- baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
+ makeFileActive: false,
})
+ .then(() =>
+ this.getRawFileData({
+ path: this.file.path,
+ baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
+ }),
+ )
.then(() => {
this.createEditorInstance();
})
@@ -246,6 +255,8 @@ export default {
ref="editor"
:class="{
'is-readonly': isCommitModeActive,
+ 'is-deleted': file.deleted,
+ 'is-added': file.tempFile
}"
class="multi-file-editor-holder"
>
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index 3b4dd5ae9aa..eb4a927fe0d 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -34,11 +34,6 @@ export default {
type: Number,
required: true,
},
- disableActionDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
return {
@@ -212,8 +207,7 @@ export default {
/>
</span>
<new-dropdown
- v-if="isTree && !disableActionDropdown"
- :project-id="file.projectId"
+ :type="file.type"
:branch="file.branchId"
:path="file.path"
:mouse-over="mouseOver"
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index 03772ae4a4c..db47b75ec5c 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -37,7 +37,7 @@ export default {
return this.fileHasChanged ? !this.tabMouseOver : false;
},
fileHasChanged() {
- return this.tab.changed || this.tab.tempFile || this.tab.staged;
+ return this.tab.changed || this.tab.tempFile || this.tab.staged || this.tab.deleted;
},
},
@@ -71,7 +71,8 @@ export default {
<template>
<li
:class="{
- active: tab.active
+ active: tab.active,
+ disabled: tab.pending
}"
@click="clickFile(tab)"
@mouseover="mouseOverTab"
@@ -105,7 +106,6 @@ export default {
<changed-file-icon
v-else
:file="tab"
- :force-modified-icon="true"
/>
</button>
</li>
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 45d36f6f42c..0b514f31467 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -38,3 +38,18 @@ export const stageKeys = {
unstaged: 'unstaged',
staged: 'staged',
};
+
+export const commitItemIconMap = {
+ addition: {
+ icon: 'file-addition',
+ class: 'ide-file-addition',
+ },
+ modified: {
+ icon: 'file-modified',
+ class: 'ide-file-modified',
+ },
+ deleted: {
+ icon: 'file-deletion',
+ class: 'ide-file-deletion',
+ },
+};
diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js
index 78e6f632728..60bddb34977 100644
--- a/app/assets/javascripts/ide/lib/common/model.js
+++ b/app/assets/javascripts/ide/lib/common/model.js
@@ -7,7 +7,7 @@ export default class Model {
this.disposable = new Disposable();
this.file = file;
this.head = head;
- this.content = file.content !== '' ? file.content : file.raw;
+ this.content = file.content !== '' || file.deleted ? file.content : file.raw;
this.disposable.add(
(this.originalModel = monacoEditor.createModel(
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 49a481f25d5..cb93fba1665 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -18,7 +18,7 @@ export default {
return axios
.get(file.rawPath, {
- params: { format: 'json' },
+ transformResponse: [f => f],
})
.then(({ data }) => data);
},
@@ -33,7 +33,7 @@ export default {
return axios
.get(file.rawPath.replace(`/raw/${file.branchId}/${file.path}`, `/raw/${sha}/${file.path}`), {
- params: { format: 'json' },
+ transformResponse: [f => f],
})
.then(({ data }) => data);
},
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index b5bd6f5a6bb..2765acada48 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -185,6 +185,14 @@ export const openNewEntryModal = ({ commit }, { type, path = '' }) => {
$('#ide-new-entry').modal('show');
};
+export const deleteEntry = ({ commit, dispatch, state }, path) => {
+ dispatch('burstUnusedSeal');
+ dispatch('closeFile', state.entries[path]);
+ commit(types.DELETE_ENTRY, path);
+};
+
+export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
+
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 6c0887e11ee..b343750f789 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -61,7 +61,11 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => {
export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive = true }) => {
const file = state.entries[path];
+
+ if (file.raw || file.tempFile) return Promise.resolve();
+
commit(types.TOGGLE_LOADING, { entry: file });
+
return service
.getFileData(
`${gon.relative_url_root ? gon.relative_url_root : ''}${file.url.replace('/-/', '/')}`,
@@ -71,7 +75,7 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive
setPageTitle(decodeURI(normalizedHeaders['PAGE-TITLE']));
commit(types.SET_FILE_DATA, { data, file });
- commit(types.TOGGLE_FILE_OPEN, path);
+ if (makeFileActive) commit(types.TOGGLE_FILE_OPEN, path);
if (makeFileActive) dispatch('setFileActive', path);
commit(types.TOGGLE_LOADING, { entry: file });
})
@@ -97,7 +101,7 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) =
service
.getRawFileData(file)
.then(raw => {
- commit(types.SET_FILE_RAW_DATA, { file, raw });
+ if (!file.tempFile) commit(types.SET_FILE_RAW_DATA, { file, raw });
if (file.mrChange && file.mrChange.new_file === false) {
service
.getBaseRawFileData(file, baseSha)
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index ffaaaabff17..acb6ef5e6d4 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -21,14 +21,12 @@ export const showTreeEntry = ({ commit, dispatch, state }, path) => {
export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
if (row.type === 'tree') {
dispatch('toggleTreeOpen', row.path);
- } else if (row.type === 'blob' && (row.opened || row.changed)) {
- if (row.changed && !row.opened) {
+ } else if (row.type === 'blob') {
+ if (!row.opened) {
commit(types.TOGGLE_FILE_OPEN, row.path);
}
dispatch('setFileActive', row.path);
- } else {
- dispatch('getFileData', { path: row.path });
}
dispatch('showTreeEntry', row.path);
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 7828c31f20e..462ca45db9b 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -174,11 +174,13 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
dispatch('updateActivityBarView', activityBarViews.edit, { root: true });
dispatch('updateViewer', 'editor', { root: true });
- router.push(
- `/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${
- rootGetters.activeFile.path
- }`,
- );
+ if (rootGetters.activeFile) {
+ router.push(
+ `/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${
+ rootGetters.activeFile.path
+ }`,
+ );
+ }
}
})
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH))
diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js
index 3db4b2f903e..03777e6c10b 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/getters.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js
@@ -1,7 +1,15 @@
-import { sprintf, n__ } from '../../../../locale';
+import { sprintf, n__, __ } from '../../../../locale';
import * as consts from './constants';
const BRANCH_SUFFIX_COUNT = 5;
+const createTranslatedTextForFiles = (files, text) => {
+ if (!files.length) return null;
+
+ return sprintf(n__('%{text} %{files}', '%{text} %{files} files', files.length), {
+ files: files.reduce((acc, val) => acc.concat(val.path), []).join(', '),
+ text,
+ });
+};
export const discardDraftButtonDisabled = state =>
state.commitMessage === '' || state.submitCommitLoading;
@@ -29,14 +37,16 @@ export const branchName = (state, getters, rootState) => {
export const preBuiltCommitMessage = (state, _, rootState) => {
if (state.commitMessage) return state.commitMessage;
- const files = (rootState.stagedFiles.length
- ? rootState.stagedFiles
- : rootState.changedFiles
- ).reduce((acc, val) => acc.concat(val.path), []);
+ const files = rootState.stagedFiles.length ? rootState.stagedFiles : rootState.changedFiles;
+ const modifiedFiles = files.filter(f => !f.deleted);
+ const deletedFiles = files.filter(f => f.deleted);
- return sprintf(n__('Update %{files}', 'Update %{files} files', files.length), {
- files: files.join(', '),
- });
+ return [
+ createTranslatedTextForFiles(modifiedFiles, __('Update')),
+ createTranslatedTextForFiles(deletedFiles, __('Deleted')),
+ ]
+ .filter(t => t)
+ .join('\n');
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 8d6f9ccaf34..dae60f4d65a 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -76,3 +76,4 @@ export const RESET_OPEN_FILES = 'RESET_OPEN_FILES';
export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
export const OPEN_NEW_ENTRY_MODAL = 'OPEN_NEW_ENTRY_MODAL';
+export const DELETE_ENTRY = 'DELETE_ENTRY';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index f8091f5b5e0..799c2f51e8d 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
import projectMutations from './mutations/project';
import mergeRequestMutation from './mutations/merge_request';
@@ -171,6 +172,16 @@ export default {
newEntryModal: { type, path },
});
},
+ [types.DELETE_ENTRY](state, path) {
+ const entry = state.entries[path];
+ const parent = entry.parentPath
+ ? state.entries[entry.parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+
+ entry.deleted = true;
+ state.changedFiles = state.changedFiles.concat(entry);
+ parent.tree = parent.tree.filter(f => f.path !== entry.path);
+ },
...projectMutations,
...mergeRequestMutation,
...fileMutations,
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 46547820425..9a87d50d6d5 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
+import { sortTree } from '../utils';
import { diffModes } from '../../constants';
export default {
@@ -51,9 +52,17 @@ export default {
});
},
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
+ const openPendingFile = state.openFiles.find(
+ f => f.path === file.path && f.pending && !f.tempFile,
+ );
+
Object.assign(state.entries[file.path], {
raw,
});
+
+ if (openPendingFile) {
+ openPendingFile.raw = raw;
+ }
},
[types.SET_FILE_BASE_RAW_DATA](state, { file, baseRaw }) {
Object.assign(state.entries[file.path], {
@@ -109,11 +118,22 @@ export default {
},
[types.DISCARD_FILE_CHANGES](state, path) {
const stagedFile = state.stagedFiles.find(f => f.path === path);
+ const entry = state.entries[path];
+ const { deleted } = entry;
Object.assign(state.entries[path], {
content: stagedFile ? stagedFile.content : state.entries[path].raw,
changed: false,
+ deleted: false,
});
+
+ if (deleted) {
+ const parent = entry.parentPath
+ ? state.entries[entry.parentPath]
+ : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
+
+ parent.tree = sortTree(parent.tree.concat(entry));
+ }
},
[types.ADD_FILE_TO_CHANGED](state, path) {
Object.assign(state, {
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 9e6b86dd844..bf7ab93ff5e 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -46,6 +46,7 @@ export const dataStructure = () => ({
parentPath: null,
lastOpenedAt: 0,
mrChange: null,
+ deleted: false,
});
export const decorateData = entity => {
@@ -105,15 +106,37 @@ export const setPageTitle = title => {
document.title = title;
};
+export const commitActionForFile = file => {
+ if (file.deleted) {
+ return 'delete';
+ } else if (file.tempFile) {
+ return 'create';
+ }
+
+ return 'update';
+};
+
+export const getCommitFiles = (stagedFiles, deleteTree = false) =>
+ stagedFiles.reduce((acc, file) => {
+ if ((file.deleted || deleteTree) && file.type === 'tree') {
+ return acc.concat(getCommitFiles(file.tree, true));
+ }
+
+ return acc.concat({
+ ...file,
+ deleted: deleteTree || file.deleted,
+ });
+ }, []);
+
export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({
branch,
commit_message: state.commitMessage || getters.preBuiltCommitMessage,
- actions: rootState.stagedFiles.map(f => ({
- action: f.tempFile ? 'create' : 'update',
+ actions: getCommitFiles(rootState.stagedFiles).map(f => ({
+ action: commitActionForFile(f),
file_path: f.path,
content: f.content,
encoding: f.base64 ? 'base64' : 'text',
- last_commit_id: newBranch ? undefined : f.lastCommitSha,
+ last_commit_id: newBranch || f.deleted ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
});
diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js
new file mode 100644
index 00000000000..92b15cf232d
--- /dev/null
+++ b/app/assets/javascripts/ide/utils.js
@@ -0,0 +1,12 @@
+import { commitItemIconMap } from './constants';
+
+// eslint-disable-next-line import/prefer-default-export
+export const getCommitIconMap = file => {
+ if (file.deleted) {
+ return commitItemIconMap.deleted;
+ } else if (file.tempFile) {
+ return commitItemIconMap.addition;
+ }
+
+ return commitItemIconMap.modified;
+};
diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
index c32dc83da8e..14518f86dc7 100644
--- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import _ from 'underscore';
import JobNameComponent from './job_name_component.vue';
import JobComponent from './job_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
@@ -46,7 +47,7 @@ export default {
computed: {
tooltipText() {
- return `${this.job.name} - ${this.job.status.label}`;
+ return _.escape(`${this.job.name} - ${this.job.status.label}`);
},
},
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 4ec67f6c01b..1952dd453f4 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import StageColumnComponent from './stage_column_component.vue';
@@ -26,7 +27,8 @@ export default {
methods: {
capitalizeStageName(name) {
- return name.charAt(0).toUpperCase() + name.slice(1);
+ const escapedName = _.escape(name);
+ return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
},
isFirstColumn(index) {
diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue
index 8af984ef91a..84a3d58b770 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import ActionComponent from './action_component.vue';
import JobNameComponent from './job_name_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
@@ -61,7 +62,7 @@ export default {
const textBuilder = [];
if (this.job.name) {
- textBuilder.push(this.job.name);
+ textBuilder.push(_.escape(this.job.name));
}
if (this.job.name && this.status.tooltip) {
@@ -69,7 +70,7 @@ export default {
}
if (this.status.tooltip) {
- textBuilder.push(`${this.job.status.tooltip}`);
+ textBuilder.push(this.job.status.tooltip);
}
return textBuilder.join(' ');
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
index 2c728582b7c..e7b2de52f76 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import JobComponent from './job_component.vue';
import DropdownJobComponent from './dropdown_job_component.vue';
@@ -37,7 +38,7 @@ export default {
},
jobId(job) {
- return `ci-badge-${job.name}`;
+ return `ci-badge-${_.escape(job.name)}`;
},
buildConnnectorClass(index) {
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index d335c3f55af..dc599e1b9fc 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -42,11 +42,14 @@ export default {
return this.timeEstimate - this.timeSpent;
},
timeRemainingPercent() {
- return `${Math.floor((this.timeSpent / this.timeEstimate) * 100)}%`;
+ return Math.floor((this.timeSpent / this.timeEstimate) * 100);
},
timeRemainingStatusClass() {
return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate';
},
+ progressBarVariant() {
+ return this.timeRemainingPercent > 100 ? 'danger' : 'primary';
+ },
},
};
</script>
@@ -62,16 +65,10 @@ export default {
data-placement="top"
role="timeRemainingDisplay"
>
- <div
- :aria-valuenow="timeRemainingPercent"
- class="meter-container"
- >
- <div
- :style="{ width: timeRemainingPercent }"
- class="meter-fill"
- >
- </div>
- </div>
+ <gl-progress-bar
+ :value="timeRemainingPercent"
+ :variant="progressBarVariant"
+ />
<div class="compare-display-container">
<div class="compare-display float-left">
<span class="compare-label">
diff --git a/app/assets/javascripts/vue_shared/components/bar_chart.vue b/app/assets/javascripts/vue_shared/components/bar_chart.vue
new file mode 100644
index 00000000000..3ced4eb691a
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/bar_chart.vue
@@ -0,0 +1,391 @@
+<script>
+import * as d3 from 'd3';
+import tooltip from '../directives/tooltip';
+import Icon from './icon.vue';
+import SvgGradient from './svg_gradient.vue';
+import {
+ GRADIENT_COLORS,
+ GRADIENT_OPACITY,
+ INVERSE_GRADIENT_COLORS,
+ INVERSE_GRADIENT_OPACITY,
+} from './bar_chart_constants';
+
+/**
+ * Renders a bar chart that can be dragged(scrolled) when the number
+ * of elements to renders surpasses that of the available viewport space
+ * while keeping even padding and a width of 24px (customizable)
+ *
+ * It can render data with the following format:
+ * graphData: [{
+ * name: 'element' // x domain data
+ * value: 1 // y domain data
+ * }]
+ *
+ * Used in:
+ * - Contribution analytics - all of the rows describing pushes, merge requests and issues
+ */
+
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ SvgGradient,
+ },
+ props: {
+ graphData: {
+ type: Array,
+ required: true,
+ },
+ barWidth: {
+ type: Number,
+ required: false,
+ default: 24,
+ },
+ yAxisLabel: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ minX: -40,
+ minY: 0,
+ vbWidth: 0,
+ vbHeight: 0,
+ vpWidth: 0,
+ vpHeight: 350,
+ preserveAspectRatioType: 'xMidYMid meet',
+ containerMargin: {
+ leftRight: 30,
+ },
+ viewBoxMargin: {
+ topBottom: 150,
+ },
+ panX: 0,
+ xScale: {},
+ yScale: {},
+ zoom: {},
+ bars: {},
+ xGraphRange: 0,
+ isLoading: true,
+ paddingThreshold: 50,
+ showScrollIndicator: false,
+ showLeftScrollIndicator: false,
+ isGrabbed: false,
+ isPanAvailable: false,
+ gradientColors: GRADIENT_COLORS,
+ gradientOpacity: GRADIENT_OPACITY,
+ inverseGradientColors: INVERSE_GRADIENT_COLORS,
+ inverseGradientOpacity: INVERSE_GRADIENT_OPACITY,
+ maxTextWidth: 72,
+ rectYAxisLabelDims: {},
+ xAxisTextElements: {},
+ yAxisRectTransformPadding: 20,
+ yAxisTextTransformPadding: 10,
+ yAxisTextRotation: 90,
+ };
+ },
+ computed: {
+ svgViewBox() {
+ return `${this.minX} ${this.minY} ${this.vbWidth} ${this.vbHeight}`;
+ },
+ xAxisLocation() {
+ return `translate(${this.panX}, ${this.vbHeight})`;
+ },
+ barTranslationTransform() {
+ return `translate(${this.panX}, 0)`;
+ },
+ scrollIndicatorTransform() {
+ return `translate(${this.vbWidth - 80}, 0)`;
+ },
+ activateGrabCursor() {
+ return {
+ 'svg-graph-container-with-grab': this.isPanAvailable,
+ 'svg-graph-container-grabbed': this.isPanAvailable && this.isGrabbed,
+ };
+ },
+ yAxisLabelRectTransform() {
+ const rectWidth =
+ this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0;
+ const yCoord = this.vbHeight / 2 - rectWidth;
+
+ return `translate(${this.minX - this.yAxisRectTransformPadding}, ${yCoord})`;
+ },
+ yAxisLabelTextTransform() {
+ const rectWidth =
+ this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0;
+ const yCoord = this.vbHeight / 2 + rectWidth - 5;
+
+ return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${this.yAxisTextRotation})`;
+ },
+ },
+ mounted() {
+ if (!this.allValuesEmpty) {
+ this.draw();
+ }
+ },
+ methods: {
+ draw() {
+ // update viewport
+ this.vpWidth = this.$refs.svgContainer.clientWidth - this.containerMargin.leftRight;
+ // update viewbox
+ this.vbWidth = this.vpWidth;
+ this.vbHeight = this.vpHeight - this.viewBoxMargin.topBottom;
+ let padding = 0;
+ if (this.graphData.length * this.barWidth > this.vbWidth) {
+ this.xGraphRange = this.graphData.length * this.barWidth;
+ padding = this.calculatePadding(this.barWidth);
+ this.showScrollIndicator = true;
+ this.isPanAvailable = true;
+ } else {
+ this.xGraphRange = this.vbWidth - Math.abs(this.minX);
+ }
+
+ this.xScale = d3
+ .scaleBand()
+ .range([0, this.xGraphRange])
+ .round(true)
+ .paddingInner(padding);
+ this.yScale = d3.scaleLinear().rangeRound([this.vbHeight, 0]);
+
+ this.xScale.domain(this.graphData.map(d => d.name));
+ this.yScale.domain([0, d3.max(this.graphData.map(d => d.value))]);
+
+ // Zoom/Panning Function
+ this.zoom = d3
+ .zoom()
+ .translateExtent([[0, 0], [this.xGraphRange, this.vbHeight]])
+ .on('zoom', this.panGraph)
+ .on('end', this.removeGrabStyling);
+
+ const xAxis = d3.axisBottom().scale(this.xScale);
+
+ const yAxis = d3
+ .axisLeft()
+ .scale(this.yScale)
+ .ticks(4);
+
+ const renderedXAxis = d3
+ .select(this.$refs.baseSvg)
+ .select('.x-axis')
+ .call(xAxis);
+
+ this.xAxisTextElements = this.$refs.xAxis.querySelectorAll('text');
+
+ renderedXAxis.select('.domain').remove();
+
+ renderedXAxis
+ .selectAll('text')
+ .style('text-anchor', 'end')
+ .attr('dx', '-.3em')
+ .attr('dy', '-.95em')
+ .attr('class', 'tick-text')
+ .attr('transform', 'rotate(-90)');
+
+ renderedXAxis.selectAll('line').remove();
+
+ const { maxTextWidth } = this;
+ renderedXAxis.selectAll('text').each(function formatText() {
+ const axisText = d3.select(this);
+ let textLength = axisText.node().getComputedTextLength();
+ let textContent = axisText.text();
+ while (textLength > maxTextWidth && textContent.length > 0) {
+ textContent = textContent.slice(0, -1);
+ axisText.text(`${textContent}...`);
+ textLength = axisText.node().getComputedTextLength();
+ }
+ });
+
+ const width = this.vbWidth;
+
+ const renderedYAxis = d3
+ .select(this.$refs.baseSvg)
+ .select('.y-axis')
+ .call(yAxis);
+
+ renderedYAxis.selectAll('.tick').each(function createTickLines(d, i) {
+ if (i > 0) {
+ d3
+ .select(this)
+ .select('line')
+ .attr('x2', width)
+ .attr('class', 'axis-tick');
+ }
+ });
+
+ // Add the panning capabilities
+ if (this.isPanAvailable) {
+ d3
+ .select(this.$refs.baseSvg)
+ .call(this.zoom)
+ .on('wheel.zoom', null); // This disables the pan of the graph with the scroll of the mouse wheel
+ }
+
+ this.isLoading = false;
+ // Update the yAxisLabel coordinates
+ const labelDims = this.$refs.yAxisLabel.getBBox();
+ this.rectYAxisLabelDims = {
+ height: labelDims.width + 10,
+ };
+ },
+ panGraph() {
+ const allowedRightScroll = this.xGraphRange - this.vbWidth - this.paddingThreshold;
+ const graphMaxPan = Math.abs(d3.event.transform.x) < allowedRightScroll;
+ this.isGrabbed = true;
+ this.panX = d3.event.transform.x;
+
+ if (d3.event.transform.x === 0) {
+ this.showLeftScrollIndicator = false;
+ } else {
+ this.showLeftScrollIndicator = true;
+ this.showScrollIndicator = true;
+ }
+
+ if (!graphMaxPan) {
+ this.panX = -1 * (this.xGraphRange - this.vbWidth + this.paddingThreshold);
+ this.showScrollIndicator = false;
+ }
+ },
+ setTooltipTitle(data) {
+ return data !== null ? `${data.name}: ${data.value}` : '';
+ },
+ calculatePadding(desiredBarWidth) {
+ const widthWithMargin = this.vbWidth - Math.abs(this.minX);
+ const dividend = widthWithMargin - this.graphData.length * desiredBarWidth;
+ const divisor = widthWithMargin - desiredBarWidth;
+
+ return dividend / divisor;
+ },
+ removeGrabStyling() {
+ this.isGrabbed = false;
+ },
+ barHoveredIn(index) {
+ this.xAxisTextElements[index].classList.add('x-axis-text');
+ },
+ barHoveredOut(index) {
+ this.xAxisTextElements[index].classList.remove('x-axis-text');
+ },
+ },
+};
+</script>
+<template>
+ <div
+ ref="svgContainer"
+ :class="activateGrabCursor"
+ class="svg-graph-container"
+ >
+ <svg
+ ref="baseSvg"
+ :width="vpWidth"
+ :height="vpHeight"
+ :viewBox="svgViewBox"
+ :preserveAspectRatio="preserveAspectRatioType">
+ <g
+ ref="xAxis"
+ :transform="xAxisLocation"
+ class="x-axis"
+ />
+ <g v-if="!isLoading">
+ <template
+ v-for="(data, index) in graphData">
+ <rect
+ v-tooltip
+ :key="index"
+ :width="xScale.bandwidth()"
+ :x="xScale(data.name)"
+ :y="yScale(data.value)"
+ :height="vbHeight - yScale(data.value)"
+ :transform="barTranslationTransform"
+ :title="setTooltipTitle(data)"
+ class="bar-rect"
+ data-placement="top"
+ @mouseover="barHoveredIn(index)"
+ @mouseout="barHoveredOut(index)"
+ />
+ </template>
+ </g>
+ <rect
+ :height="vbHeight + 100"
+ transform="translate(-100, -5)"
+ width="100"
+ fill="#fff"
+ />
+ <g class="y-axis-label">
+ <line
+ :x1="0"
+ :x2="0"
+ :y1="0"
+ :y2="vbHeight"
+ transform="translate(-35, 0)"
+ stroke="black"
+ />
+ <!--Get text length and change the height of this rect accordingly-->
+ <rect
+ :height="rectYAxisLabelDims.height"
+ :transform="yAxisLabelRectTransform"
+ :width="30"
+ fill="#fff"
+ />
+ <text
+ ref="yAxisLabel"
+ :transform="yAxisLabelTextTransform"
+ >
+ {{ yAxisLabel }}
+ </text>
+ </g>
+ <g
+ class="y-axis"
+ />
+ <g v-if="showScrollIndicator">
+ <rect
+ :height="vbHeight + 100"
+ :transform="`translate(${vpWidth - 60}, -5)`"
+ width="40"
+ fill="#fff"
+ />
+ <icon
+ :x="vpWidth - 50"
+ :y="vbHeight / 2"
+ :width="14"
+ :height="14"
+ name="chevron-right"
+ class="animate-flicker"
+ />
+ </g>
+ <!--The line that shows up when the data elements surpass the available width -->
+ <g
+ v-if="showScrollIndicator"
+ :transform="scrollIndicatorTransform">
+ <rect
+ :height="vbHeight"
+ x="0"
+ y="0"
+ width="20"
+ fill="url(#shadow-gradient)"
+ />
+ </g>
+ <!--Left scroll indicator-->
+ <g
+ v-if="showLeftScrollIndicator"
+ transform="translate(0, 0)">
+ <rect
+ :height="vbHeight"
+ x="0"
+ y="0"
+ width="20"
+ fill="url(#left-shadow-gradient)"
+ />
+ </g>
+ <svg-gradient
+ :colors="gradientColors"
+ :opacity="gradientOpacity"
+ identifier-name="shadow-gradient"/>
+ <svg-gradient
+ :colors="inverseGradientColors"
+ :opacity="inverseGradientOpacity"
+ identifier-name="left-shadow-gradient"/>
+ </svg>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/bar_chart_constants.js b/app/assets/javascripts/vue_shared/components/bar_chart_constants.js
new file mode 100644
index 00000000000..6957b112da6
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/bar_chart_constants.js
@@ -0,0 +1,4 @@
+export const GRADIENT_COLORS = ['#000', '#a7a7a7'];
+export const GRADIENT_OPACITY = ['0', '0.4'];
+export const INVERSE_GRADIENT_COLORS = ['#a7a7a7', '#000'];
+export const INVERSE_GRADIENT_OPACITY = ['0.4', '0'];
diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
index 8c2dcc2d902..7947ae1e4da 100644
--- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue
+++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
@@ -32,7 +32,7 @@
},
computed: {
className() {
- return `drag${this.side}`;
+ return `drag-${this.side}`;
},
cursorStyle() {
if (this.enabled) {
@@ -44,8 +44,15 @@
methods: {
resetSize(e) {
e.preventDefault();
+ this.$emit('resize-start', this.size);
+
this.size = this.startSize;
this.$emit('update:size', this.size);
+
+ // End resizing on next tick so that listeners can react to DOM changes
+ this.$nextTick(() => {
+ this.$emit('resize-end', this.size);
+ });
},
startDrag(e) {
if (this.enabled) {
@@ -84,7 +91,7 @@
<div
:class="className"
:style="cursorStyle"
- class="dragHandle"
+ class="drag-handle"
@mousedown="startDrag"
@dblclick="resetSize"
></div>
diff --git a/app/assets/javascripts/vue_shared/components/reports/constants.js b/app/assets/javascripts/vue_shared/components/reports/constants.js
new file mode 100644
index 00000000000..dbde648bfdb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/constants.js
@@ -0,0 +1,3 @@
+export const STATUS_FAILED = 'failed';
+export const STATUS_SUCCESS = 'success';
+export const STATUS_NEUTRAL = 'neutral';
diff --git a/app/assets/javascripts/vue_shared/components/reports/issue_body.js b/app/assets/javascripts/vue_shared/components/reports/issue_body.js
new file mode 100644
index 00000000000..f2141e519da
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/issue_body.js
@@ -0,0 +1,3 @@
+export const components = {};
+
+export const componentNames = {};
diff --git a/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue b/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue
new file mode 100644
index 00000000000..f8189117ac3
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue
@@ -0,0 +1,58 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+
+import {
+ STATUS_FAILED,
+ STATUS_NEUTRAL,
+ STATUS_SUCCESS,
+} from '~/vue_shared/components/reports/constants';
+
+export default {
+ name: 'IssueStatusIcon',
+ components: {
+ Icon,
+ },
+ props: {
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ iconName() {
+ if (this.isStatusFailed) {
+ return 'status_failed_borderless';
+ } else if (this.isStatusSuccess) {
+ return 'status_success_borderless';
+ }
+
+ return 'status_created_borderless';
+ },
+ isStatusFailed() {
+ return this.status === STATUS_FAILED;
+ },
+ isStatusSuccess() {
+ return this.status === STATUS_SUCCESS;
+ },
+ isStatusNeutral() {
+ return this.status === STATUS_NEUTRAL;
+ },
+ },
+};
+</script>
+<template>
+ <div
+ :class="{
+ failed: isStatusFailed,
+ success: isStatusSuccess,
+ neutral: isStatusNeutral,
+ }"
+ class="report-block-list-icon"
+ >
+ <icon
+ :name="iconName"
+ :size="32"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue b/app/assets/javascripts/vue_shared/components/reports/issues_list.vue
index e1e03e39ee0..c01f77c2509 100644
--- a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue
+++ b/app/assets/javascripts/vue_shared/components/reports/issues_list.vue
@@ -1,5 +1,10 @@
<script>
-import IssuesBlock from './report_issues.vue';
+import IssuesBlock from '~/vue_shared/components/reports/report_issues.vue';
+import {
+ STATUS_SUCCESS,
+ STATUS_FAILED,
+ STATUS_NEUTRAL,
+} from '~/vue_shared/components/reports/constants';
/**
* Renders block of issues
@@ -9,6 +14,9 @@ export default {
components: {
IssuesBlock,
},
+ success: STATUS_SUCCESS,
+ failed: STATUS_FAILED,
+ neutral: STATUS_NEUTRAL,
props: {
unresolvedIssues: {
type: Array,
@@ -25,29 +33,10 @@ export default {
required: false,
default: () => [],
},
- allIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
- type: {
+ component: {
type: String,
- required: true,
- },
- },
- data() {
- return {
- isFullReportVisible: false,
- };
- },
- computed: {
- unresolvedIssuesStatus() {
- return this.type === 'license' ? 'neutral' : 'failed';
- },
- },
- methods: {
- openFullReport() {
- this.isFullReportVisible = true;
+ required: false,
+ default: '',
},
},
};
@@ -57,43 +46,26 @@ export default {
<issues-block
v-if="unresolvedIssues.length"
- :type="type"
- :status="unresolvedIssuesStatus"
+ :component="component"
:issues="unresolvedIssues"
+ :status="$options.failed"
class="js-mr-code-new-issues"
/>
<issues-block
- v-if="isFullReportVisible"
- :type="type"
- :issues="allIssues"
- class="js-mr-code-all-issues"
- status="failed"
- />
-
- <issues-block
v-if="neutralIssues.length"
- :type="type"
+ :component="component"
:issues="neutralIssues"
+ :status="$options.neutral"
class="js-mr-code-non-issues"
- status="neutral"
/>
<issues-block
v-if="resolvedIssues.length"
- :type="type"
+ :component="component"
:issues="resolvedIssues"
+ :status="$options.success"
class="js-mr-code-resolved-issues"
- status="success"
/>
-
- <button
- v-if="allIssues.length && !isFullReportVisible"
- type="button"
- class="btn-link btn-blank prepend-left-10 js-expand-full-list break-link"
- @click="openFullReport"
- >
- {{ s__("ciReport|Show complete code vulnerabilities report") }}
- </button>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue b/app/assets/javascripts/vue_shared/components/reports/report_issues.vue
index ecffb02a3a0..2d1f3d82234 100644
--- a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue
+++ b/app/assets/javascripts/vue_shared/components/reports/report_issues.vue
@@ -1,19 +1,23 @@
<script>
-import Icon from '~/vue_shared/components/icon.vue';
+import IssueStatusIcon from '~/vue_shared/components/reports/issue_status_icon.vue';
+import { components, componentNames } from '~/vue_shared/components/reports/issue_body';
export default {
name: 'ReportIssues',
components: {
- Icon,
+ IssueStatusIcon,
+ ...components,
},
props: {
issues: {
type: Array,
required: true,
},
- type: {
+ component: {
type: String,
- required: true,
+ required: false,
+ default: '',
+ validator: value => value === '' || Object.values(componentNames).includes(value),
},
// failed || success
status: {
@@ -21,26 +25,6 @@ export default {
required: true,
},
},
- computed: {
- iconName() {
- if (this.isStatusFailed) {
- return 'status_failed_borderless';
- } else if (this.isStatusSuccess) {
- return 'status_success_borderless';
- }
-
- return 'status_created_borderless';
- },
- isStatusFailed() {
- return this.status === 'failed';
- },
- isStatusSuccess() {
- return this.status === 'success';
- },
- isStatusNeutral() {
- return this.status === 'neutral';
- },
- },
};
</script>
<template>
@@ -52,20 +36,17 @@ export default {
:key="index"
class="report-block-list-issue"
>
- <div
- :class="{
- failed: isStatusFailed,
- success: isStatusSuccess,
- neutral: isStatusNeutral,
- }"
- class="report-block-list-icon append-right-5"
- >
- <icon
- :name="iconName"
- :size="32"
- />
- </div>
+ <issue-status-icon
+ :status="issue.status || status"
+ class="append-right-5"
+ />
+ <component
+ v-if="component"
+ :is="component"
+ :issue="issue"
+ :status="issue.status || status"
+ />
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_section.vue b/app/assets/javascripts/vue_shared/components/reports/report_section.vue
index d383ed99a0c..0124d8b5bcc 100644
--- a/app/assets/javascripts/vue_shared/components/reports/report_section.vue
+++ b/app/assets/javascripts/vue_shared/components/reports/report_section.vue
@@ -21,7 +21,7 @@ export default {
required: false,
default: false,
},
- type: {
+ component: {
type: String,
required: false,
default: '',
@@ -59,11 +59,6 @@ export default {
required: false,
default: () => [],
},
- allIssues: {
- type: Array,
- required: false,
- default: () => [],
- },
infoText: {
type: [String, Boolean],
required: false,
@@ -142,18 +137,10 @@ export default {
</script>
<template>
<section class="media-section">
- <div
- class="media"
- >
- <status-icon
- :status="statusIconName"
- />
- <div
- class="media-body space-children d-flex"
- >
- <span
- class="js-code-text code-text"
- >
+ <div class="media">
+ <status-icon :status="statusIconName" />
+ <div class="media-body space-children d-flex flex-align-self-center">
+ <span class="js-code-text code-text">
{{ headerText }}
<popover
@@ -163,10 +150,12 @@ export default {
/>
</span>
+ <slot name="actionButtons"></slot>
+
<button
v-if="isCollapsible"
type="button"
- class="js-collapse-btn btn bt-default float-right btn-sm"
+ class="js-collapse-btn btn float-right btn-sm"
@click="toggleCollapsed"
>
{{ collapseText }}
@@ -183,8 +172,8 @@ export default {
<issues-list
:unresolved-issues="unresolvedIssues"
:resolved-issues="resolvedIssues"
- :all-issues="allIssues"
- :type="type"
+ :neutral-issues="neutralIssues"
+ :component="component"
/>
</slot>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/svg_gradient.vue b/app/assets/javascripts/vue_shared/components/svg_gradient.vue
new file mode 100644
index 00000000000..b61a1befcd6
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/svg_gradient.vue
@@ -0,0 +1,37 @@
+<script>
+export default {
+ props: {
+ colors: {
+ type: Array,
+ required: true,
+ },
+ opacity: {
+ type: Array,
+ required: true,
+ },
+ identifierName: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <svg
+ height="0"
+ width="0">
+ <defs>
+ <linearGradient
+ :id="identifierName">
+ <stop
+ :stop-color="colors[0]"
+ :stop-opacity="opacity[0]"
+ offset="0%" />
+ <stop
+ :stop-color="colors[1]"
+ :stop-opacity="opacity[1]"
+ offset="100%" />
+ </linearGradient>
+ </defs>
+ </svg>
+</template>
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 637587de597..2d6dba52801 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -370,11 +370,14 @@ img.emoji {
margin-right: 10px;
}
-.alert,
-.progress {
+.alert {
margin-bottom: $gl-padding;
}
+.progress {
+ height: 4px;
+}
+
.project-item-select-holder {
display: inline-block;
position: relative;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 2097bcebf69..e7e13d35d8e 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -567,9 +567,6 @@
border-bottom: 1px solid $white-normal;
.mx-auto {
- margin: 8px 0;
- text-align: center;
-
.tanuki-logo,
img {
height: 36px;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 56307777a72..a2789021ab4 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -179,6 +179,10 @@
font-weight: inherit;
}
+ a > code {
+ color: $gl-link-color;
+ }
+
dd {
margin-left: $gl-padding;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index efc54196b75..08755b4b545 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -835,3 +835,5 @@ $font-family-monospace: $monospace-font;
$input-line-height: 20px;
$btn-line-height: 20px;
$table-accent-bg: $gray-light;
+$card-border-color: $border-color;
+$card-cap-bg: $gray-light;
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 2d76f0ce004..442aef124d3 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -1,3 +1,6 @@
+@import 'framework/variables';
+@import 'framework/mixins';
+
.project-refs-form,
.project-refs-target-form {
display: inline-block;
@@ -74,6 +77,7 @@
.ide-file-icon-holder {
display: flex;
align-items: center;
+ color: $theme-gray-700;
}
.ide-file-changed-icon {
@@ -161,12 +165,23 @@
background-color: $white-light;
border-bottom-color: $white-light;
}
+
+ &:not(.disabled) {
+ .multi-file-tab {
+ cursor: pointer;
+ }
+ }
+
+ &.disabled {
+ .multi-file-tab-close {
+ cursor: default;
+ }
+ }
}
}
.multi-file-tab {
@include str-truncated(141px);
- cursor: pointer;
svg {
vertical-align: middle;
@@ -241,6 +256,38 @@
}
}
+ .is-deleted {
+ .editor.modified {
+ .margin-view-overlays,
+ .lines-content,
+ .decorationsOverviewRuler {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .diffOverviewRuler.modified {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .is-added {
+ .editor.original {
+ .margin-view-overlays,
+ .lines-content,
+ .decorationsOverviewRuler {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
+ .diffOverviewRuler.original {
+ // !important to override monaco inline styles
+ display: none !important;
+ }
+ }
+
.monaco-diff-editor.vs {
.editor.modified {
box-shadow: none;
@@ -557,16 +604,21 @@
}
}
-.multi-file-addition,
-.multi-file-addition-solid {
+.ide-file-addition,
+.ide-file-addition-solid {
color: $green-500;
}
-.multi-file-modified,
-.multi-file-modified-solid {
+.ide-file-modified,
+.ide-file-modified-solid {
color: $orange-500;
}
+.ide-file-deletion,
+.ide-file-deletion-solid {
+ color: $red-500;
+}
+
.multi-file-commit-list-collapsed {
display: flex;
flex-direction: column;
@@ -781,18 +833,21 @@
}
}
-.dragHandle {
+.drag-handle {
position: absolute;
top: 0;
bottom: 0;
- width: 1px;
- background-color: $white-dark;
+ width: 4px;
+
+ &:hover {
+ background-color: $white-normal;
+ }
- &.dragright {
+ &.drag-right {
right: 0;
}
- &.dragleft {
+ &.drag-left {
left: 0;
}
}
@@ -1014,6 +1069,10 @@
.ide-new-btn {
margin-left: auto;
}
+
+ button {
+ color: $gl-text-color;
+ }
}
.ide-sidebar-branch-title {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index f030189af06..e5c38a20bf0 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -270,6 +270,7 @@
.block {
width: 100%;
+ word-break: break-word;
&:last-child {
border-bottom: 1px solid $border-gray-normal;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index b616357bb8d..591e21243ed 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -16,6 +16,7 @@
svg {
vertical-align: middle;
+ top: -1px;
}
}
diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss
index 84da9180f93..49d8a5d959b 100644
--- a/app/assets/stylesheets/pages/graph.scss
+++ b/app/assets/stylesheets/pages/graph.scss
@@ -31,3 +31,61 @@
color: $gl-text-red;
}
}
+
+.svg-graph-container {
+ width: 100%;
+
+ .axis-tick {
+ opacity: 0.4;
+ }
+
+ .tick-text {
+ fill: $gl-text-color-secondary;
+ }
+
+ .x-axis-text {
+ fill: $theme-gray-900;
+ }
+
+ .bar-rect {
+ fill: rgba($blue-500, 0.1);
+ stroke: $blue-500;
+ }
+
+ .bar-rect:hover {
+ fill: rgba($blue-700, 0.3);
+ }
+
+ .y-axis-label {
+ line {
+ stroke: $stat-graph-axis-fill;
+ }
+
+ text {
+ font-weight: bold;
+ font-size: 12px;
+ fill: $theme-gray-800;
+ }
+ }
+}
+
+.svg-graph-container-with-grab {
+ cursor: grab;
+ cursor: -webkit-grab;
+}
+
+.svg-graph-container-grabbed {
+ cursor: grabbing;
+ cursor: -webkit-grabbing;
+}
+
+@keyframes flickerAnimation {
+ 0% { opacity: 1; }
+ 50% { opacity: 0; }
+ 100% { opacity: 1; }
+}
+
+.animate-flicker {
+ animation: flickerAnimation 1.5s infinite;
+ fill: $theme-gray-500;
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 797b106de23..d5ae2b673d9 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -834,17 +834,7 @@
}
.compare-meter {
- &.within_estimate {
- .meter-fill {
- background: $gl-primary;
- }
- }
-
&.over_estimate {
- .meter-fill {
- background: $red-500;
- }
-
.time-remaining,
.compare-value.spent {
color: $red-500;
@@ -852,18 +842,6 @@
}
}
- .meter-container {
- background: $border-gray-light;
- border-radius: 3px;
-
- .meter-fill {
- max-width: 100%;
- height: 5px;
- border-radius: 3px;
- background: $gl-primary;
- }
- }
-
.compare-display-container {
display: flex;
justify-content: space-between;
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index c1b1d2e028d..8a4a2caa6c9 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -237,7 +237,7 @@
}
.login-page-broadcast {
- margin-top: 50px;
+ margin-top: 40px;
}
.navless-container {
diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss
index 64110f9c3a0..bd777c66b56 100644
--- a/app/assets/stylesheets/snippets.scss
+++ b/app/assets/stylesheets/snippets.scss
@@ -22,8 +22,8 @@
height: 16px;
background-size: cover;
- &.gl-snippet-icon-doc_code { background-position: 0 0; }
- &.gl-snippet-icon-doc_text { background-position: 0 -16px; }
+ &.gl-snippet-icon-doc-code { background-position: 0 0; }
+ &.gl-snippet-icon-doc-text { background-position: 0 -16px; }
&.gl-snippet-icon-download { background-position: 0 -32px; }
}
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 9e495061f4e..36faea8056e 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -4,13 +4,17 @@ class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
include RendersCommits
- before_action :whitelist_query_limiting
+ before_action :whitelist_query_limiting, except: :commits_root
before_action :require_non_empty_project
- before_action :assign_ref_vars
+ before_action :assign_ref_vars, except: :commits_root
before_action :authorize_download_code!
- before_action :set_commits
+ before_action :set_commits, except: :commits_root
before_action :set_request_format, only: :show
+ def commits_root
+ redirect_to project_commits_path(@project, @project.default_branch)
+ end
+
def show
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 21d3c918581..ce03b2d8d1d 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -112,7 +112,7 @@ class Projects::LabelsController < Projects::ApplicationController
begin
return render_404 unless promote_service.execute(@label)
- flash[:notice] = "#{@label.title} promoted to <a href=\"#{group_labels_path(@project.group)}\">group label</a>.".html_safe
+ flash[:notice] = flash_notice_for(@label, @project.group)
respond_to do |format|
format.html do
redirect_to(project_labels_path(@project), status: :see_other)
@@ -135,6 +135,15 @@ class Projects::LabelsController < Projects::ApplicationController
end
end
+ def flash_notice_for(label, group)
+ notice = ''.html_safe
+ notice << label.title
+ notice << ' promoted to '
+ notice << view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group))
+ notice << '.'
+ notice
+ end
+
protected
def label_params
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 5e86ec93f34..b9b3dcd5a85 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -76,8 +76,8 @@ class Projects::MilestonesController < Projects::ApplicationController
def promote
promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone)
+ flash[:notice] = flash_notice_for(promoted_milestone, project.group)
- flash[:notice] = "#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, promoted_milestone.iid)}\"><u>group milestone</u></a>.".html_safe
respond_to do |format|
format.html do
redirect_to project_milestones_path(project)
@@ -90,6 +90,15 @@ class Projects::MilestonesController < Projects::ApplicationController
redirect_to milestone, alert: error.message
end
+ def flash_notice_for(milestone, group)
+ notice = ''.html_safe
+ notice << milestone.title
+ notice << ' promoted to '
+ notice << view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid))
+ notice << '.'
+ notice
+ end
+
def destroy
return access_denied! unless can?(current_user, :admin_milestone, @project)
diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb
index 53b77f5fed9..543bf1a1415 100644
--- a/app/finders/admin/projects_finder.rb
+++ b/app/finders/admin/projects_finder.rb
@@ -7,7 +7,7 @@ class Admin::ProjectsFinder
end
def execute
- items = Project.without_deleted.with_statistics
+ items = Project.without_deleted.with_statistics.with_route
items = by_namespace_id(items)
items = by_visibilty_level(items)
items = by_with_push(items)
@@ -16,7 +16,7 @@ class Admin::ProjectsFinder
items = by_archived(items)
items = by_personal(items)
items = by_name(items)
- items = items.includes(namespace: [:owner])
+ items = items.includes(namespace: [:owner, :route])
sort(items).page(params[:page])
end
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index d9f9129d08a..8755a1a62e7 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -7,5 +7,5 @@ class GitlabSchema < GraphQL::Schema
query(Types::QueryType)
default_max_page_size 100
- # mutation(Types::MutationType)
+ mutation(Types::MutationType)
end
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb
new file mode 100644
index 00000000000..eb03dfe1624
--- /dev/null
+++ b/app/graphql/mutations/base_mutation.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Mutations
+ class BaseMutation < GraphQL::Schema::RelayClassicMutation
+ field :errors, [GraphQL::STRING_TYPE],
+ null: false,
+ description: "Reasons why the mutation failed."
+
+ def current_user
+ context[:current_user]
+ end
+ end
+end
diff --git a/app/graphql/mutations/concerns/mutations/resolves_project.rb b/app/graphql/mutations/concerns/mutations/resolves_project.rb
new file mode 100644
index 00000000000..0dd1f264a52
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/resolves_project.rb
@@ -0,0 +1,13 @@
+module Mutations
+ module ResolvesProject
+ extend ActiveSupport::Concern
+
+ def resolve_project(full_path:)
+ resolver.resolve(full_path: full_path)
+ end
+
+ def resolver
+ Resolvers::ProjectResolver.new(object: nil, context: context)
+ end
+ end
+end
diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb
new file mode 100644
index 00000000000..2149e72e2df
--- /dev/null
+++ b/app/graphql/mutations/merge_requests/base.rb
@@ -0,0 +1,32 @@
+module Mutations
+ module MergeRequests
+ class Base < BaseMutation
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ include Mutations::ResolvesProject
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: "The project the merge request to mutate is in"
+
+ argument :iid, GraphQL::ID_TYPE,
+ required: true,
+ description: "The iid of the merge request to mutate"
+
+ field :merge_request,
+ Types::MergeRequestType,
+ null: true,
+ description: "The merge request after mutation"
+
+ authorize :update_merge_request
+
+ private
+
+ def find_object(project_path:, iid:)
+ project = resolve_project(full_path: project_path)
+ resolver = Resolvers::MergeRequestResolver.new(object: project, context: context)
+
+ resolver.resolve(iid: iid)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/merge_requests/set_wip.rb b/app/graphql/mutations/merge_requests/set_wip.rb
new file mode 100644
index 00000000000..a2aa0c84ee4
--- /dev/null
+++ b/app/graphql/mutations/merge_requests/set_wip.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Mutations
+ module MergeRequests
+ class SetWip < Base
+ graphql_name 'MergeRequestSetWip'
+
+ argument :wip,
+ GraphQL::BOOLEAN_TYPE,
+ required: true,
+ description: <<~DESC
+ Whether or not to set the merge request as a WIP.
+ DESC
+
+ def resolve(project_path:, iid:, wip: nil)
+ merge_request = authorized_find!(project_path: project_path, iid: iid)
+ project = merge_request.project
+
+ ::MergeRequests::UpdateService.new(project, current_user, wip_event: wip_event(merge_request, wip))
+ .execute(merge_request)
+
+ {
+ merge_request: merge_request,
+ errors: merge_request.errors.full_messages
+ }
+ end
+
+ private
+
+ def wip_event(merge_request, wip)
+ wip ? 'wip' : 'unwip'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 06ed91c1658..2b4ef299296 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
module Types
class MutationType < BaseObject
+ include Gitlab::Graphql::MountMutation
+
graphql_name "Mutation"
- # TODO: Add Mutations as fields
+ mount_mutation Mutations::MergeRequests::SetWip
end
end
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 4ce89f89fa9..c005ecbb56b 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -4,4 +4,23 @@ module EnvironmentsHelper
endpoint: project_environments_path(@project, format: :json)
}
end
+
+ def metrics_data(project, environment)
+ {
+ "settings-path" => edit_project_service_path(project, 'prometheus'),
+ "clusters-path" => project_clusters_path(project),
+ "current-environment-name": environment.name,
+ "documentation-path" => help_page_path('administration/monitoring/prometheus/index.md'),
+ "empty-getting-started-svg-path" => image_path('illustrations/monitoring/getting_started.svg'),
+ "empty-loading-svg-path" => image_path('illustrations/monitoring/loading.svg'),
+ "empty-no-data-svg-path" => image_path('illustrations/monitoring/no_data.svg'),
+ "empty-unable-to-connect-svg-path" => image_path('illustrations/monitoring/unable_to_connect.svg'),
+ "metrics-endpoint" => additional_metrics_project_environment_path(project, environment, format: :json),
+ "deployment-endpoint" => project_environment_deployments_path(project, environment, format: :json),
+ "environments-endpoint": project_environments_path(project, format: :json),
+ "project-path" => project_path(project),
+ "tags-path" => project_tags_path(project),
+ "has-metrics" => "#{environment.has_metrics?}"
+ }
+ end
end
diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb
index 551b9cca6b1..0a356ba55d2 100644
--- a/app/helpers/hooks_helper.rb
+++ b/app/helpers/hooks_helper.rb
@@ -10,7 +10,7 @@ module HooksHelper
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
- link_to path, rel: 'nofollow' do
+ link_to path, rel: 'nofollow', method: :post do
content_tag(:span, trigger_human_name)
end
end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 733832c1bbb..a05640773ad 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -116,7 +116,7 @@ module SnippetsHelper
raw_project_snippet_url(@snippet.project, @snippet)
end
- link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw'
+ link_to external_snippet_icon('doc-code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw'
end
def embedded_snippet_download_button
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 1db1482d6b7..0e1e39501f5 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -124,7 +124,7 @@ class Notify < BaseMailer
fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze
headers['References'] ||= []
- headers['References'] << fallback_reply_message_id
+ headers['References'].unshift(fallback_reply_message_id)
@reply_by_email = true
end
@@ -158,7 +158,7 @@ class Notify < BaseMailer
def mail_answer_thread(model, headers = {})
headers['Message-ID'] = "<#{SecureRandom.hex}@#{Gitlab.config.gitlab.host}>"
headers['In-Reply-To'] = message_id(model)
- headers['References'] = message_id(model)
+ headers['References'] = [message_id(model)]
headers[:subject]&.prepend('Re: ')
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 48137c2ed68..ea6ec4d6b03 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -21,6 +21,14 @@ module Clusters
end
end
+ def ready_status
+ [:installed]
+ end
+
+ def ready?
+ ready_status.include?(status_name)
+ end
+
def chart
'stable/prometheus'
end
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index 18cbbd871a1..9c36f633395 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -24,11 +24,10 @@ module PrometheusAdapter
def query(query_name, *args)
return unless can_query?
- query_class = Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
+ query_class = query_klass_for(query_name)
+ query_args = build_query_args(*args)
- args.map!(&:id)
-
- with_reactive_cache(query_class.name, *args, &query_class.method(:transform_reactive_result))
+ with_reactive_cache(query_class.name, *query_args, &query_class.method(:transform_reactive_result))
end
# Cache metrics for specific environment
@@ -44,5 +43,13 @@ module PrometheusAdapter
rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err.message }
end
+
+ def query_klass_for(query_name)
+ Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
+ end
+
+ def build_query_args(*args)
+ args.map(&:id)
+ end
end
end
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index be0a5b49012..9155d82d567 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -59,6 +59,9 @@ module ReactiveCaching
raise NotImplementedError
end
+ def reactive_cache_updated(*args)
+ end
+
def with_reactive_cache(*args, &blk)
bootstrap = !within_reactive_cache_lifetime?(*args)
Rails.cache.write(alive_reactive_cache_key(*args), true, expires_in: self.class.reactive_cache_lifetime)
@@ -81,8 +84,11 @@ module ReactiveCaching
locking_reactive_cache(*args) do
if within_reactive_cache_lifetime?(*args)
enqueuing_update(*args) do
- value = calculate_reactive_cache(*args)
- Rails.cache.write(full_reactive_cache_key(*args), value)
+ key = full_reactive_cache_key(*args)
+ new_value = calculate_reactive_cache(*args)
+ old_value = Rails.cache.read(key)
+ Rails.cache.write(key, new_value)
+ reactive_cache_updated(*args) if new_value != old_value
end
end
end
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 0176a12a131..cb91f8fbac8 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -90,34 +90,17 @@ module Routable
end
def full_name
- if route && route.name.present?
- @full_name ||= route.name # rubocop:disable Gitlab/ModuleWithInstanceVariables
- else
- update_route if persisted?
-
- build_full_name
- end
+ route&.name || build_full_name
end
- # Every time `project.namespace.becomes(Namespace)` is called for polymorphic_path,
- # a new instance is instantiated, and we end up duplicating the same query to retrieve
- # the route. Caching this per request ensures that even if we have multiple instances,
- # we will not have to duplicate work, avoiding N+1 queries in some cases.
def full_path
- return uncached_full_path unless RequestStore.active? && persisted?
-
- RequestStore[full_path_key] ||= uncached_full_path
+ route&.path || build_full_path
end
def full_path_components
full_path.split('/')
end
- def expires_full_path_cache
- RequestStore.delete(full_path_key) if RequestStore.active?
- @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
- end
-
def build_full_path
if parent && path
parent.full_path + '/' + path
@@ -138,16 +121,6 @@ module Routable
self.errors[:path].concat(route_path_errors) if route_path_errors
end
- def uncached_full_path
- if route && route.path.present?
- @full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables
- else
- update_route if persisted?
-
- build_full_path
- end
- end
-
def full_name_changed?
name_changed? || parent_changed?
end
@@ -156,10 +129,6 @@ module Routable
path_changed? || parent_changed?
end
- def full_path_key
- @full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}"
- end
-
def build_full_name
if parent && name
parent.human_name + ' / ' + name
@@ -168,18 +137,9 @@ module Routable
end
end
- def update_route
- return if Gitlab::Database.read_only?
-
- prepare_route
- route.save
- end
-
def prepare_route
route || build_route(source: self)
route.path = build_full_path
route.name = build_full_name
- @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
- @full_name = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index f66bdd529f1..f5225cd81ed 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -11,8 +11,6 @@ module Storage
Namespace.find(parent_id_was) # raise NotFound early if needed
end
- expires_full_path_cache
-
move_repositories
if parent_changed?
@@ -34,13 +32,12 @@ module Storage
begin
send_update_instructions
write_projects_repository_config
-
- true
- rescue
- # Returning false does not rollback after_* transaction but gives
- # us information about failing some of tasks
- false
+ rescue => e
+ # Raise if development/test environment, else just notify Sentry
+ Gitlab::Sentry.track_exception(e, extra: { full_path_was: full_path_was, full_path: full_path, action: 'move_dir' })
end
+
+ true # false would cancel later callbacks but not rollback
end
# Hooks
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 7ab647abe93..fdbe95059e5 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -1,6 +1,7 @@
class DeployToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
+ include PolicyActor
add_authentication_token_field :token
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
@@ -58,10 +59,6 @@ class DeployToken < ActiveRecord::Base
write_attribute(:expires_at, value.presence || Forever.date)
end
- def admin?
- false
- end
-
private
def ensure_at_least_one_scope
diff --git a/app/models/email.rb b/app/models/email.rb
index d6516761f0a..15bdedeac33 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -25,6 +25,10 @@ class Email < ActiveRecord::Base
self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email)
end
+ def accept_pending_invitations!
+ user.accept_pending_invitations!
+ end
+
# once email is confirmed, update the gpg signatures
def update_invalid_gpg_signatures
user.update_invalid_gpg_signatures if confirmed?
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 7034c633268..c1dc2f55346 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -304,7 +304,6 @@ class Namespace < ActiveRecord::Base
def write_projects_repository_config
all_projects.find_each do |project|
- project.expires_full_path_cache # we need to clear cache to validate renames correctly
project.write_repository_config
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 325dbd0197f..a452ec5fcdf 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -31,6 +31,7 @@ class Project < ActiveRecord::Base
BoardLimitExceeded = Class.new(StandardError)
+ STATISTICS_ATTRIBUTE = 'repositories_count'.freeze
NUMBER_OF_PERMITTED_BOARDS = 1
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
# Hashed Storage versions handle rolling out new storage to project and dependents models:
@@ -79,6 +80,10 @@ class Project < ActiveRecord::Base
after_create :create_project_feature, unless: :project_feature
+ after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) }
+ before_destroy ->(project) { project.project_feature.untrack_statistics_for_deletion! }
+ after_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) }
+
after_create :create_ci_cd_settings,
unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? }
@@ -1235,8 +1240,6 @@ class Project < ActiveRecord::Base
return true if skip_disk_validation
return false unless repository_storage
- expires_full_path_cache # we need to clear cache to validate renames correctly
-
# Check if repository with same path already exists on disk we can
# skip this for the hashed storage because the path does not change
if legacy_storage? && repository_with_same_path_already_exists?
@@ -1615,7 +1618,6 @@ class Project < ActiveRecord::Base
# When we import a project overwriting the original project, there
# is a move operation. In that case we don't want to send the instructions.
send_move_instructions(full_path_was) unless import_started?
- expires_full_path_cache
self.old_path_with_namespace = full_path_was
SystemHooksService.new.execute_hooks_for(self, :rename)
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index bfb8d703ec9..9c768b13f78 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -19,6 +19,7 @@ class ProjectFeature < ActiveRecord::Base
ENABLED = 20
FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
+ STATISTICS_ATTRIBUTE = 'wikis_count'.freeze
class << self
def access_level_attribute(feature)
@@ -52,6 +53,9 @@ class ProjectFeature < ActiveRecord::Base
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
+ after_create ->(model) { SiteStatistic.track(STATISTICS_ATTRIBUTE) if model.wiki_enabled? }
+ after_update :update_site_statistics
+
def feature_available?(feature, user)
get_permission(user, access_level(feature))
end
@@ -76,8 +80,30 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED
end
+ # This is a workaround for the removal hooks not been triggered when removing a Project.
+ #
+ # ProjectFeature is removed using database cascade index rule.
+ # This method is called by Project model when deletion starts.
+ def untrack_statistics_for_deletion!
+ return unless wiki_enabled?
+
+ SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
+ end
+
private
+ def update_site_statistics
+ return unless wiki_access_level_changed?
+
+ if self.wiki_access_level_was == DISABLED
+ # possible new states are PRIVATE / ENABLED, both should be tracked
+ SiteStatistic.track(STATISTICS_ATTRIBUTE)
+ elsif self.wiki_access_level == DISABLED
+ # old state was either PRIVATE / ENABLED, only untrack if new state is DISABLED
+ SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
+ end
+ end
+
# Validates builds and merge requests access level
# which cannot be higher than repository access level
def repository_children_level
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 976b501e297..6172bb38881 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -48,13 +48,13 @@ class RemoteMirror < ActiveRecord::Base
state :failed
after_transition any => :started do |remote_mirror, _|
- Gitlab::Metrics.add_event(:remote_mirrors_running, path: remote_mirror.project.full_path)
+ Gitlab::Metrics.add_event(:remote_mirrors_running)
remote_mirror.update(last_update_started_at: Time.now)
end
after_transition started: :finished do |remote_mirror, _|
- Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path)
+ Gitlab::Metrics.add_event(:remote_mirrors_finished)
timestamp = Time.now
remote_mirror.update!(
@@ -63,7 +63,7 @@ class RemoteMirror < ActiveRecord::Base
end
after_transition started: :failed do |remote_mirror, _|
- Gitlab::Metrics.add_event(:remote_mirrors_failed, path: remote_mirror.project.full_path)
+ Gitlab::Metrics.add_event(:remote_mirrors_failed)
remote_mirror.update(last_update_at: Time.now)
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e248f94cbd8..9873d9a6327 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1029,7 +1029,7 @@ class Repository
end
def repository_event(event, tags = {})
- Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
+ Gitlab::Metrics.add_event(event, tags)
end
def initialize_raw_repository
diff --git a/app/models/site_statistic.rb b/app/models/site_statistic.rb
new file mode 100644
index 00000000000..9c9c3172fe6
--- /dev/null
+++ b/app/models/site_statistic.rb
@@ -0,0 +1,74 @@
+class SiteStatistic < ActiveRecord::Base
+ # prevents the creation of multiple rows
+ default_value_for :id, 1
+
+ COUNTER_ATTRIBUTES = %w(repositories_count wikis_count).freeze
+ REQUIRED_SCHEMA_VERSION = 20180629153018
+
+ # Tracks specific attribute
+ #
+ # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES
+ def self.track(raw_attribute)
+ with_statistics_available(raw_attribute) do |attribute|
+ SiteStatistic.update_all(["#{attribute} = #{attribute}+1"])
+ end
+ end
+
+ # Untracks specific attribute
+ #
+ # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES
+ def self.untrack(raw_attribute)
+ with_statistics_available(raw_attribute) do |attribute|
+ SiteStatistic.update_all(["#{attribute} = #{attribute}-1 WHERE #{attribute} > 0"])
+ end
+ end
+
+ # Wrapper for track/untrack operations with basic validations and enforced requirements
+ #
+ # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES
+ # @yield [String] attribute quoted to be used inside SQL / Arel query
+ def self.with_statistics_available(raw_attribute)
+ unless raw_attribute.in?(COUNTER_ATTRIBUTES)
+ raise ArgumentError, "Invalid attribute: '#{raw_attribute}' to '#{caller_locations(1, 1)[0].label}' method. " \
+ "Valid attributes are: #{COUNTER_ATTRIBUTES.join(', ')}"
+ end
+
+ return unless available?
+
+ self.fetch # make sure record exists
+
+ attribute = self.connection.quote_column_name(raw_attribute)
+
+ # will be running on its own transaction context
+ yield(attribute)
+ end
+
+ # Returns a site statistic record with tracked information
+ #
+ # @return [SiteStatistic] record with tracked information
+ def self.fetch
+ SiteStatistic.transaction(requires_new: true) do
+ SiteStatistic.first_or_create!
+ end
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+
+ # Return whether required schema change is available
+ #
+ # This is needed in order to degrade gracefully when testing schema migrations
+ #
+ # @return [Boolean] whether schema is available
+ def self.available?
+ @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION
+ end
+
+ # Resets cached column information
+ #
+ # This is called during schema migration specs, in order to reset internal cache state
+ def self.reset_column_information
+ @available_flag = nil
+
+ super
+ end
+end
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
new file mode 100644
index 00000000000..069d065280e
--- /dev/null
+++ b/app/policies/concerns/policy_actor.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+# Include this module if we want to pass something else than the user to
+# check policies. This defines several methods which the policy checker
+# would call and check.
+module PolicyActor
+ extend ActiveSupport::Concern
+
+ def blocked?
+ false
+ end
+
+ def admin?
+ false
+ end
+
+ def external?
+ false
+ end
+
+ def internal?
+ false
+ end
+
+ def access_locked?
+ false
+ end
+
+ def required_terms_not_accepted?
+ false
+ end
+
+ def can_create_group
+ false
+ end
+end
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 4a33160afa1..3205578b83e 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -11,10 +11,15 @@ class PipelineSerializer < BaseSerializer
:retryable_builds,
:cancelable_statuses,
:trigger_requests,
- :project,
:manual_actions,
:artifacts,
- { pending_builds: :project }
+ {
+ pending_builds: :project,
+ project: [:route, { namespace: :route }],
+ artifacts: {
+ project: [:route, { namespace: :route }]
+ }
+ }
])
end
diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb
index 4640c5a2d4b..a1165b0ab28 100644
--- a/app/services/clusters/applications/check_installation_progress_service.rb
+++ b/app/services/clusters/applications/check_installation_progress_service.rb
@@ -50,17 +50,17 @@ module Clusters
end
def remove_installation_pod
- helm_api.delete_installation_pod!(install_command.pod_name)
+ helm_api.delete_pod!(install_command.pod_name)
rescue
# no-op
end
def installation_phase
- helm_api.installation_status(install_command.pod_name)
+ helm_api.status(install_command.pod_name)
end
def installation_errors
- helm_api.installation_log(install_command.pod_name)
+ helm_api.log(install_command.pod_name)
end
end
end
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index a4a66330546..c2a0c5fa7f3 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -77,7 +77,6 @@ module Projects
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
project.old_path_with_namespace = @old_path
- project.expires_full_path_cache
write_repository_config(@new_path)
diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb
index cbba79690c5..a791845ba20 100644
--- a/app/services/prometheus/adapter_service.rb
+++ b/app/services/prometheus/adapter_service.rb
@@ -30,7 +30,7 @@ module Prometheus
return unless deployment_platform.respond_to?(:cluster)
cluster = deployment_platform.cluster
- return unless cluster.application_prometheus&.installed?
+ return unless cluster.application_prometheus&.ready?
cluster.application_prometheus
end
diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml
index 00933d726d9..fdaacc098e0 100644
--- a/app/views/admin/projects/_projects.html.haml
+++ b/app/views/admin/projects/_projects.html.haml
@@ -17,7 +17,7 @@
- if project.archived
%span.badge.badge-warning archived
.title
- = link_to [:admin, project.namespace.becomes(Namespace), project] do
+ = link_to(admin_namespace_project_path(project.namespace, project)) do
.dash-project-avatar
.avatar-container.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml
index 9f8b0acd763..d29dda43c89 100644
--- a/app/views/ide/index.html.haml
+++ b/app/views/ide/index.html.haml
@@ -1,6 +1,9 @@
- @body_class = 'ide'
- page_title 'IDE'
+- content_for :page_specific_javascripts do
+ = stylesheet_link_tag 'page_bundles/ide'
+
#ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg'),
"no-changes-state-svg-path" => image_path('illustrations/multi-editor_no_changes_empty.svg'),
"committed-state-svg-path" => image_path('illustrations/multi-editor_all_changes_committed_empty.svg'),
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 9253a0652da..ac5916d129c 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -3,6 +3,11 @@
- site_name = "GitLab"
%head{ prefix: "og: http://ogp.me/ns#" }
%meta{ charset: "utf-8" }
+
+ - if Feature.enabled?('asset_host_prefetch') && ActionController::Base.asset_host
+ %link{ rel: 'dns-prefetch', href: ActionController::Base.asset_host }
+ %link{ rel: 'preconnnect', href: ActionController::Base.asset_host, crossorigin: '' }
+
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
-# Open Graph - http://ogp.me/
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 97c04dda8cb..e8d31992149 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -67,5 +67,5 @@
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
%span.sr-only= _("Toggle navigation")
- = sprite_icon('more', size: 12, css_class: 'more-icon js-navbar-toggle-right')
+ = sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right')
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 0a3b5ec7eea..d471dd84550 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -15,7 +15,7 @@
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
= link_to group_path(@group) do
.nav-icon-container
- = sprite_icon('project')
+ = sprite_icon('home')
%span.nav-item-name
= _('Overview')
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 94863a3460d..d65f153b451 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -110,7 +110,7 @@
= nav_link(controller: :gpg_keys) do
= link_to profile_gpg_keys_path do
.nav-icon-container
- = sprite_icon('key-2')
+ = sprite_icon('key-modern')
%span.nav-item-name
= _('GPG Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 0ec61df1f0a..2c262a2b7dd 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -11,7 +11,7 @@
= nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do
= link_to project_path(@project), class: 'shortcuts-project' do
.nav-icon-container
- = sprite_icon('project')
+ = sprite_icon('home')
%span.nav-item-name
= _('Project')
@@ -40,7 +40,7 @@
= nav_link(controller: sidebar_repository_paths) do
= link_to project_tree_path(@project), class: 'shortcuts-tree' do
.nav-icon-container
- = sprite_icon('doc_text')
+ = sprite_icon('doc-text')
%span.nav-item-name
= _('Repository')
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index 290970a1045..af86b8e8e67 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -2,17 +2,11 @@
- page_title "Metrics for environment", @environment.name
.prometheus-container{ class: container_class }
- #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'),
- "clusters-path": project_clusters_path(@project),
- "current-environment-name": @environment.name,
- "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'),
- "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'),
- "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'),
- "empty-no-data-svg-path": image_path('illustrations/monitoring/no_data.svg'),
- "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'),
- "metrics-endpoint": additional_metrics_project_environment_path(@project, @environment, format: :json),
- "deployment-endpoint": project_environment_deployments_path(@project, @environment, format: :json),
- "environments-endpoint": project_environments_path(@project, format: :json),
- "project-path": project_path(@project),
- "tags-path": project_tags_path(@project),
- "has-metrics": "#{@environment.has_metrics?}" } }
+ .top-area
+ .row
+ .col-sm-6
+ %h3
+ Environment:
+ = link_to @environment.name, environment_path(@environment)
+
+ #prometheus-graphs{ data: metrics_data(@project, @environment) }
diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml
index b88fe47726d..759efd4e9d4 100644
--- a/app/views/projects/jobs/_sidebar.html.haml
+++ b/app/views/projects/jobs/_sidebar.html.haml
@@ -86,7 +86,7 @@
- HasStatus::ORDERED_STATUSES.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
- - tooltip = build.tooltip_message
+ - tooltip = sanitize(build.tooltip_message.dup)
= link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do
= sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
%span{ class: "ci-status-icon-#{build.status}" }
diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml
index ca0f7d6098f..afa7eb06cb4 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -27,7 +27,7 @@
= dropdown_filter(_("Search branches"))
= dropdown_content
= dropdown_loading
- .panel-footer
+ .card-footer
.text-center= icon('spinner spin', class: 'js-source-loading')
%ul.list-unstyled.mr_source_commit
diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml
index 36f56fbad1a..c7f0511d1de 100644
--- a/app/views/shared/snippets/_embed.html.haml
+++ b/app/views/shared/snippets/_embed.html.haml
@@ -2,7 +2,7 @@
.gitlab-embed-snippets
.js-file-title.file-title-flex-parent
.file-header-content
- = external_snippet_icon('doc_text')
+ = external_snippet_icon('doc-text')
%strong.file-title-name
%a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) }
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index 100d86e38c8..eeeff6e93a0 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -22,7 +22,7 @@ module Gitlab
importer_class.new(object, project, client).execute
- counter.increment(project: project.full_path)
+ counter.increment
end
def counter
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 5ef9b744db3..68ec66e8499 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -23,9 +23,7 @@ class RepositoryForkWorker
def fork_repository(target_project, source_repository_storage_name, source_disk_path)
return unless start_fork(target_project)
- Gitlab::Metrics.add_event(:fork_repository,
- source_path: source_disk_path,
- target_path: target_project.disk_path)
+ Gitlab::Metrics.add_event(:fork_repository)
result = gitlab_shell.fork_repository(source_repository_storage_name, source_disk_path,
target_project.repository_storage, target_project.disk_path)
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 25fec542ac7..8c64c513c74 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -11,9 +11,7 @@ class RepositoryImportWorker
return unless start_import(project)
- Gitlab::Metrics.add_event(:import_repository,
- import_url: project.import_url,
- path: project.full_path)
+ Gitlab::Metrics.add_event(:import_repository)
service = Projects::ImportService.new(project, project.creator)
result = service.execute
diff --git a/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml
new file mode 100644
index 00000000000..21d9d25d342
--- /dev/null
+++ b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml
@@ -0,0 +1,5 @@
+---
+title: Redirect commits to root if no ref is provided (31576)
+merge_request: 20738
+author: Kia Mei Somabes
+type: added
diff --git a/changelogs/unreleased/32783-api-all-members-with-ancestors.yml b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml
new file mode 100644
index 00000000000..ca53d02845d
--- /dev/null
+++ b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml
@@ -0,0 +1,6 @@
+---
+title: Adds API endpoint /api/v4/(project/group)/:id/members/all to list also inherited
+ members
+merge_request: 19748
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/4525-fix-project-indexes.yml b/changelogs/unreleased/4525-fix-project-indexes.yml
deleted file mode 100644
index 930e3b934c2..00000000000
--- a/changelogs/unreleased/4525-fix-project-indexes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rework some projects table indexes around repository_storage field
-merge_request: 20377
-author:
-type: fixed
diff --git a/changelogs/unreleased/48055-web-ide-resize-handles.yml b/changelogs/unreleased/48055-web-ide-resize-handles.yml
new file mode 100644
index 00000000000..0f650cdda6f
--- /dev/null
+++ b/changelogs/unreleased/48055-web-ide-resize-handles.yml
@@ -0,0 +1,5 @@
+---
+title: Increase width of Web IDE sidebar resize handles
+merge_request: 20818
+author:
+type: fixed
diff --git a/changelogs/unreleased/48542-code-link.yml b/changelogs/unreleased/48542-code-link.yml
new file mode 100644
index 00000000000..8d8d9bf8d74
--- /dev/null
+++ b/changelogs/unreleased/48542-code-link.yml
@@ -0,0 +1,5 @@
+---
+title: Fix link color in markdown code brackets
+merge_request: 20841
+author:
+type: fixed
diff --git a/changelogs/unreleased/48617-promoting-milestone.yml b/changelogs/unreleased/48617-promoting-milestone.yml
new file mode 100644
index 00000000000..7fbc15051cf
--- /dev/null
+++ b/changelogs/unreleased/48617-promoting-milestone.yml
@@ -0,0 +1,5 @@
+---
+title: Escapes milestone and label's names on flash notice when promoting them
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/48636-new-mr-card-styles.yml b/changelogs/unreleased/48636-new-mr-card-styles.yml
new file mode 100644
index 00000000000..94f62e677fb
--- /dev/null
+++ b/changelogs/unreleased/48636-new-mr-card-styles.yml
@@ -0,0 +1,5 @@
+---
+title: Fix new MR card styles
+merge_request: 20822
+author:
+type: fixed
diff --git a/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml b/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml
deleted file mode 100644
index ec4b843b863..00000000000
--- a/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix navigation to First and Next discussion on MR Changes tab
-merge_request: 20434
-author:
-type: fixed
diff --git a/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml
new file mode 100644
index 00000000000..541b562adac
--- /dev/null
+++ b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml
@@ -0,0 +1,5 @@
+---
+title: DNS prefetching if asset_host for CDN hosting is set
+merge_request: 20781
+author:
+type: performance
diff --git a/changelogs/unreleased/49364-fix-broadcast-margin.yml b/changelogs/unreleased/49364-fix-broadcast-margin.yml
new file mode 100644
index 00000000000..821fb9df1af
--- /dev/null
+++ b/changelogs/unreleased/49364-fix-broadcast-margin.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misalignment of broadcast message on login page
+merge_request: 20794
+author: Robin Naundorf
+type: fixed
diff --git a/changelogs/unreleased/_acet-fix-expanding-context-lines.yml b/changelogs/unreleased/_acet-fix-expanding-context-lines.yml
deleted file mode 100644
index 41b4dbca5d6..00000000000
--- a/changelogs/unreleased/_acet-fix-expanding-context-lines.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix rendering of the context lines in MR diffs page
-merge_request: 20642
-author:
-type: fixed
diff --git a/changelogs/unreleased/_acet-fix-mr-autosave.yml b/changelogs/unreleased/_acet-fix-mr-autosave.yml
deleted file mode 100644
index f87b32f68e2..00000000000
--- a/changelogs/unreleased/_acet-fix-mr-autosave.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix autosave and ESC confirmation issues for MR discussions
-merge_request: 20569
-author:
-type: fixed
diff --git a/changelogs/unreleased/_acet-fix-outdated-discussions.yml b/changelogs/unreleased/_acet-fix-outdated-discussions.yml
deleted file mode 100644
index d31483b4765..00000000000
--- a/changelogs/unreleased/_acet-fix-outdated-discussions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix showing outdated discussions on Changes tab
-merge_request: 20445
-author:
-type: fixed
diff --git a/changelogs/unreleased/bvl-graphql-wip-mutation.yml b/changelogs/unreleased/bvl-graphql-wip-mutation.yml
new file mode 100644
index 00000000000..00aa1c48677
--- /dev/null
+++ b/changelogs/unreleased/bvl-graphql-wip-mutation.yml
@@ -0,0 +1,5 @@
+---
+title: Add the first mutations for merge requests to GraphQL
+merge_request: 20443
+author:
+type: added
diff --git a/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml
new file mode 100644
index 00000000000..b76437a8773
--- /dev/null
+++ b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml
@@ -0,0 +1,5 @@
+---
+title: Tracking the number of repositories and wikis with a cached counter for site-wide statistics
+merge_request: 20413
+author:
+type: performance
diff --git a/changelogs/custom_wiki_sidebar.yml b/changelogs/unreleased/custom_wiki_sidebar.yml
index 988fccc929c..988fccc929c 100644
--- a/changelogs/custom_wiki_sidebar.yml
+++ b/changelogs/unreleased/custom_wiki_sidebar.yml
diff --git a/changelogs/unreleased/fix-email-confirmation-addtional-email.yml b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml
new file mode 100644
index 00000000000..56a2efa4d60
--- /dev/null
+++ b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml
@@ -0,0 +1,5 @@
+---
+title: Fix email confirmation bug when user adds additional email to account
+merge_request: 20084
+author: muhammadn
+type: fixed
diff --git a/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml
new file mode 100644
index 00000000000..b26eb82b6c9
--- /dev/null
+++ b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml
@@ -0,0 +1,5 @@
+---
+title: Removes "show all" on reports and adds an actionButtons slot
+merge_request: 20855
+author:
+type: changed
diff --git a/changelogs/unreleased/ide-delete-entries.yml b/changelogs/unreleased/ide-delete-entries.yml
new file mode 100644
index 00000000000..8cbc0739406
--- /dev/null
+++ b/changelogs/unreleased/ide-delete-entries.yml
@@ -0,0 +1,5 @@
+---
+title: Enabled deletion of files in the Web IDE
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml
new file mode 100644
index 00000000000..8e71377d93f
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml
@@ -0,0 +1,5 @@
+---
+title: Fix namespace move callback behavior, especially to fix Geo replication of namespace moves during certain exceptions.
+merge_request: 19297
+author:
+type: fixed
diff --git a/changelogs/unreleased/project-dropdown-list-overflow.yml b/changelogs/unreleased/project-dropdown-list-overflow.yml
deleted file mode 100644
index 9b74a68291b..00000000000
--- a/changelogs/unreleased/project-dropdown-list-overflow.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't overflow project/group dropdown results
-merge_request: 20704
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/rails5-update-gemfile-lock-2.yml b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml
new file mode 100644
index 00000000000..1f3e9bd2238
--- /dev/null
+++ b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 update Gemfile.rails5.lock
+merge_request: 20858
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml
new file mode 100644
index 00000000000..fabf48acbbc
--- /dev/null
+++ b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml
@@ -0,0 +1,5 @@
+---
+title: Adding CSRF protection to Hooks test action
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-ide-branch-name-xss.yml b/changelogs/unreleased/security-ide-branch-name-xss.yml
new file mode 100644
index 00000000000..51742ffa4e9
--- /dev/null
+++ b/changelogs/unreleased/security-ide-branch-name-xss.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed XSS in branch name in Web IDE
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-bump-haml-5-0-4.yml b/changelogs/unreleased/sh-bump-haml-5-0-4.yml
new file mode 100644
index 00000000000..269b1e55417
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-haml-5-0-4.yml
@@ -0,0 +1,5 @@
+---
+title: Bump haml gem to 5.0.4
+merge_request: 20847
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-include-rbtrace.yml b/changelogs/unreleased/sh-include-rbtrace.yml
new file mode 100644
index 00000000000..41f0655e3f8
--- /dev/null
+++ b/changelogs/unreleased/sh-include-rbtrace.yml
@@ -0,0 +1,5 @@
+---
+title: Add rbtrace to Gemfile
+merge_request: 20831
+author:
+type: other
diff --git a/changelogs/unreleased/sh-revert-markdown-changes.yml b/changelogs/unreleased/sh-revert-markdown-changes.yml
deleted file mode 100644
index 72540f710a1..00000000000
--- a/changelogs/unreleased/sh-revert-markdown-changes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix slow Markdown rendering
-merge_request: 20820
-author:
-type: performance
diff --git a/changelogs/unreleased/stop-dynamic-routable-creation.yml b/changelogs/unreleased/stop-dynamic-routable-creation.yml
new file mode 100644
index 00000000000..8bfcb5b2d11
--- /dev/null
+++ b/changelogs/unreleased/stop-dynamic-routable-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Stop dynamically creating project and namespace routes
+merge_request: 20313
+author:
+type: performance
diff --git a/changelogs/unreleased/tc-reorder-mail-notify-references.yml b/changelogs/unreleased/tc-reorder-mail-notify-references.yml
new file mode 100644
index 00000000000..689afda0259
--- /dev/null
+++ b/changelogs/unreleased/tc-reorder-mail-notify-references.yml
@@ -0,0 +1,5 @@
+---
+title: Put fallback reply-key address first in the References header
+merge_request: 20871
+author:
+type: changed
diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
deleted file mode 100644
index 16003fa9cad..00000000000
--- a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Reduces the client side memory footprint on merge requests
-merge_request: 20744
-author:
-type: performance
diff --git a/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml
new file mode 100644
index 00000000000..97fa1592753
--- /dev/null
+++ b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Wrap job name on pipeline job sidebar
+merge_request: 20804
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/zj-backup-timeout.yml b/changelogs/unreleased/zj-backup-timeout.yml
deleted file mode 100644
index b2ad2ed8c63..00000000000
--- a/changelogs/unreleased/zj-backup-timeout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable Gitaly timeouts when creating or restoring backups
-merge_request: 20810
-author:
-type: fixed
diff --git a/config/application.rb b/config/application.rb
index 0304f466734..b9d4f6765e3 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -43,10 +43,12 @@ module Gitlab
#{config.root}/app/models/members
#{config.root}/app/models/project_services
#{config.root}/app/workers/concerns
+ #{config.root}/app/policies/concerns
#{config.root}/app/services/concerns
#{config.root}/app/serializers/concerns
#{config.root}/app/finders/concerns
- #{config.root}/app/graphql/resolvers/concerns])
+ #{config.root}/app/graphql/resolvers/concerns
+ #{config.root}/app/graphql/mutations/concerns])
config.generators.templates.push("#{config.root}/generator_templates")
@@ -132,6 +134,7 @@ module Gitlab
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "xterm/xterm.css"
+ config.assets.precompile << "page_bundles/ide.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "test.css"
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 21c20cd5e93..73115449871 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -540,3 +540,9 @@
:why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE
:versions: []
:when: 2018-06-08 05:30:56.764116000 Z
+- - :approve
+ - "@gitlab-org/gitlab-ui"
+ - :who: Clement Ho
+ :why: Our own library
+ :versions: []
+ :when: 2018-07-17 21:02:54.529227000 Z
diff --git a/config/initializers/rbtrace.rb b/config/initializers/rbtrace.rb
new file mode 100644
index 00000000000..3a076c99ad0
--- /dev/null
+++ b/config/initializers/rbtrace.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require 'rbtrace' if ENV['ENABLE_RBTRACE']
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index ff27ceb50dc..109f00631fb 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -54,7 +54,7 @@ namespace :admin do
resources :hooks, only: [:index, :create, :edit, :update, :destroy] do
member do
- get :test
+ post :test
end
resources :hook_logs, only: [:show] do
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 5057e937941..8e019f8c8bb 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -301,7 +301,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do
member do
- get :test
+ post :test
end
resources :hook_logs, only: [:show] do
diff --git a/config/routes/repository.rb b/config/routes/repository.rb
index e2bf8d6a7ff..d439cb9acbd 100644
--- a/config/routes/repository.rb
+++ b/config/routes/repository.rb
@@ -83,6 +83,7 @@ scope format: false do
get '/raw/*id', to: 'raw#show', as: :raw
get '/blame/*id', to: 'blame#show', as: :blame
+ get '/commits', to: 'commits#commits_root', as: :commits_root
get '/commits/*id/signatures', to: 'commits#signatures', as: :signatures
get '/commits/*id', to: 'commits#show', as: :commits
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index 6f48994945a..ad5f1c1e0f3 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -41,8 +41,8 @@ end
all_files = git.added_files + git.modified_files
-non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb/}).empty?
-geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb/}).empty?
+non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb}).empty?
+geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty?
non_geo_migration_created = !git.added_files.grep(%r{\A(db/(post_)?migrate)/}).empty?
geo_migration_created = !git.added_files.grep(%r{\Aee/db/geo/(post_)?migrate/}).empty?
diff --git a/danger/frozen_string/Dangerfile b/danger/frozen_string/Dangerfile
index 595176d597d..b9687ef6b83 100644
--- a/danger/frozen_string/Dangerfile
+++ b/danger/frozen_string/Dangerfile
@@ -6,22 +6,21 @@ MAGIC_COMMENT = "# frozen_string_literal: true"
def get_files_with_no_magic_comment(files)
files.select do |file|
file.end_with?(FILE_EXTENSION) &&
- !File.open(file, &:gets).start_with?(MAGIC_COMMENT)
+ !File.open(file, &:gets)&.start_with?(MAGIC_COMMENT)
end
end
-files_to_check = git.added_files
-files_to_fix = get_files_with_no_magic_comment(files_to_check)
+files_to_fix = get_files_with_no_magic_comment(git.added_files)
if files_to_fix.any?
warn 'This merge request adds files that do not enforce frozen string literal. ' \
'See https://gitlab.com/gitlab-org/gitlab-ce/issues/47424 for more information.'
markdown(<<~MARKDOWN)
-## Enable Frozen String Literal
+ ## Enable Frozen String Literal
-The following files should have `#{MAGIC_COMMENT}` in the first line:
+ The following files should have `#{MAGIC_COMMENT}` on the first line:
-* #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")}
+ * #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")}
MARKDOWN
end
diff --git a/db/migrate/20140313092127_init_schema.rb b/db/migrate/20140313092127_init_schema.rb
index 895298ed4ed..29fb386ad76 100644
--- a/db/migrate/20140313092127_init_schema.rb
+++ b/db/migrate/20140313092127_init_schema.rb
@@ -1,6 +1,7 @@
class InitSchema < ActiveRecord::Migration
DOWNTIME = true
+ # rubocop:disable Metrics/AbcSize
def up
create_table "broadcast_messages", force: :cascade do |t|
t.text "message", null: false
@@ -157,9 +158,9 @@ class InitSchema < ActiveRecord::Migration
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
- add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
+ add_index "notes", %w[noteable_id noteable_type], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
- add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
+ add_index "notes", %w[project_id noteable_type], name: "index_notes_on_project_id_and_noteable_type", using: :btree
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
create_table "project_group_links", force: :cascade do |t|
t.integer "project_id", null: false
@@ -241,7 +242,7 @@ class InitSchema < ActiveRecord::Migration
t.datetime "created_at"
end
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
- add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
+ add_index "taggings", %w[taggable_id taggable_type context], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: :cascade do |t|
t.string "name"
end
@@ -292,7 +293,7 @@ class InitSchema < ActiveRecord::Migration
add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
- add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
+ add_index "users", %w[extern_uid provider], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "users", ["username"], name: "index_users_on_username", using: :btree
diff --git a/db/migrate/20140407135544_fix_namespaces.rb b/db/migrate/20140407135544_fix_namespaces.rb
index 0026ce645a6..3d2ca5c13c4 100644
--- a/db/migrate/20140407135544_fix_namespaces.rb
+++ b/db/migrate/20140407135544_fix_namespaces.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FixNamespaces < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20140415124820_limits_to_mysql.rb b/db/migrate/20140415124820_limits_to_mysql.rb
index c712423bcd1..3f6e62617c5 100644
--- a/db/migrate/20140415124820_limits_to_mysql.rb
+++ b/db/migrate/20140415124820_limits_to_mysql.rb
@@ -1,2 +1 @@
-# rubocop:disable all
require_relative 'limits_to_mysql'
diff --git a/db/migrate/20140729145339_migrate_project_tags.rb b/db/migrate/20140729145339_migrate_project_tags.rb
index ac46847f3e6..5760e4bfeaa 100644
--- a/db/migrate/20140729145339_migrate_project_tags.rb
+++ b/db/migrate/20140729145339_migrate_project_tags.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateProjectTags < ActiveRecord::Migration
def up
ActsAsTaggableOn::Tagging.where(taggable_type: 'Project', context: 'labels').update_all(context: 'tags')
diff --git a/db/migrate/20141121133009_add_timestamps_to_members.rb b/db/migrate/20141121133009_add_timestamps_to_members.rb
index 68f164cd35d..ef6d4dedf32 100644
--- a/db/migrate/20141121133009_add_timestamps_to_members.rb
+++ b/db/migrate/20141121133009_add_timestamps_to_members.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# In 20140914145549_migrate_to_new_members_model.rb we forgot to set the
# created_at and updated_at times for new records in the 'members' table. This
# became a problem after commit c8e78d972a5a628870eefca0f2ccea0199c55bda which
diff --git a/db/migrate/20141223135007_add_import_data_to_project_table.rb b/db/migrate/20141223135007_add_import_data_to_project_table.rb
index 9c8a483e4d5..5db78f94cc9 100644
--- a/db/migrate/20141223135007_add_import_data_to_project_table.rb
+++ b/db/migrate/20141223135007_add_import_data_to_project_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportDataToProjectTable < ActiveRecord::Migration
def change
add_column :projects, :import_type, :string
diff --git a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
index 10e6549c729..aa179ce3a4d 100644
--- a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
+++ b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddHomePageUrlForApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :home_page_url, :string
diff --git a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
index e083973615a..c28ba3197ac 100644
--- a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
+++ b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddGitlabAccessTokenToUser < ActiveRecord::Migration
def change
add_column :users, :gitlab_access_token, :string
diff --git a/db/migrate/20150206222854_add_notification_email_to_user.rb b/db/migrate/20150206222854_add_notification_email_to_user.rb
index ebae092cac8..ab80f7e582f 100644
--- a/db/migrate/20150206222854_add_notification_email_to_user.rb
+++ b/db/migrate/20150206222854_add_notification_email_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddNotificationEmailToUser < ActiveRecord::Migration
def up
add_column :users, :notification_email, :string
diff --git a/db/migrate/20150211174341_allow_null_in_services_project_id.rb b/db/migrate/20150211174341_allow_null_in_services_project_id.rb
index fea95c79adf..68f02812791 100644
--- a/db/migrate/20150211174341_allow_null_in_services_project_id.rb
+++ b/db/migrate/20150211174341_allow_null_in_services_project_id.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AllowNullInServicesProjectId < ActiveRecord::Migration
def change
change_column :services, :project_id, :integer, null: true
diff --git a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
index 78e9fd0c3a9..23ac1b399ec 100644
--- a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
+++ b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddBitbucketAccessTokenAndSecretToUser < ActiveRecord::Migration
def change
add_column :users, :bitbucket_access_token, :string
diff --git a/db/migrate/20150223022001_set_missing_last_activity_at.rb b/db/migrate/20150223022001_set_missing_last_activity_at.rb
index 300381ad65b..3f6d4d83474 100644
--- a/db/migrate/20150223022001_set_missing_last_activity_at.rb
+++ b/db/migrate/20150223022001_set_missing_last_activity_at.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class SetMissingLastActivityAt < ActiveRecord::Migration
def up
execute "UPDATE projects SET last_activity_at = updated_at WHERE last_activity_at IS NULL"
diff --git a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
index 7d8d65ef2ee..494c3033bff 100644
--- a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
+++ b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRestrictedVisibilityLevelsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_visibility_levels, :text
diff --git a/db/migrate/20150320234437_add_location_to_user.rb b/db/migrate/20150320234437_add_location_to_user.rb
index df046570361..32731d37d75 100644
--- a/db/migrate/20150320234437_add_location_to_user.rb
+++ b/db/migrate/20150320234437_add_location_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddLocationToUser < ActiveRecord::Migration
def change
add_column :users, :location, :string
diff --git a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
index 9f8b6f4bd59..42dc8173e46 100644
--- a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
+++ b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class SetIncorrectAssigneeIdToNull < ActiveRecord::Migration
def up
execute "UPDATE issues SET assignee_id = NULL WHERE assignee_id = -1"
diff --git a/db/migrate/20150327150017_add_import_data_to_project.rb b/db/migrate/20150327150017_add_import_data_to_project.rb
index 67b1554dfd1..12c00339eec 100644
--- a/db/migrate/20150327150017_add_import_data_to_project.rb
+++ b/db/migrate/20150327150017_add_import_data_to_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportDataToProject < ActiveRecord::Migration
def change
add_column :projects, :import_data, :text
diff --git a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
index eccb0123e77..11b026ee8f3 100644
--- a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
+++ b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration
def change
add_column :users, :encrypted_otp_secret, :string
diff --git a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
index 4c56a2fb78b..1d161674a9a 100644
--- a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
+++ b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMaxAttachmentSizeToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :max_attachment_size, :integer, default: 10, null: false
diff --git a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
index fdb6d72917e..913958db7c5 100644
--- a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
+++ b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration
def change
add_column :users, :otp_backup_codes, :text
diff --git a/db/migrate/20150411000035_fix_identities.rb b/db/migrate/20150411000035_fix_identities.rb
index a10fcc001f4..d9051f9fffd 100644
--- a/db/migrate/20150411000035_fix_identities.rb
+++ b/db/migrate/20150411000035_fix_identities.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FixIdentities < ActiveRecord::Migration
def up
# Up until now, legacy 'ldap' references in the database were charitably
diff --git a/db/migrate/20150411180045_rename_buildbox_service.rb b/db/migrate/20150411180045_rename_buildbox_service.rb
index 9f3b25c3971..5a0b5d07e50 100644
--- a/db/migrate/20150411180045_rename_buildbox_service.rb
+++ b/db/migrate/20150411180045_rename_buildbox_service.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RenameBuildboxService < ActiveRecord::Migration
def up
execute "UPDATE services SET type = 'BuildkiteService' WHERE type = 'BuildboxService';"
diff --git a/db/migrate/20150417121913_create_project_import_data.rb b/db/migrate/20150417121913_create_project_import_data.rb
index fc357cbacc8..c78f5fde85e 100644
--- a/db/migrate/20150417121913_create_project_import_data.rb
+++ b/db/migrate/20150417121913_create_project_import_data.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class CreateProjectImportData < ActiveRecord::Migration
def change
create_table :project_import_data do |t|
diff --git a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
index 129ce4d04af..50a9b2439e0 100644
--- a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
+++ b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDefaultProjectVisibililtyToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_project_visibility, :integer
diff --git a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
index 8f352414ffd..281c88d2a7d 100644
--- a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration is a duplicate of 20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
# It shold be applied before the index additions to ensure that `name` is case sensitive.
diff --git a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
index 88829b87711..71f2d7f4330 100644
--- a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 4)
class AddMissingTaggableIndex < ActiveRecord::Migration
def self.up
diff --git a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
index 642c4745321..bfb06bc7cda 100644
--- a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 5)
# This migration is added to circumvent issue #623 and have special characters
# work properly
diff --git a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
index dd13def4176..8f1b0cc8935 100644
--- a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
+++ b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_snippet_visibility, :integer
diff --git a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
index d2c7f3c442e..244637e1c4a 100644
--- a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
+++ b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveAbandonedGroupMembersRecords < ActiveRecord::Migration
def up
execute("DELETE FROM members WHERE type = 'GroupMember' AND source_id NOT IN(\
diff --git a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
index b63ea9aec7a..184e2653610 100644
--- a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
+++ b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRestrictedSignupDomainsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_signup_domains, :text
diff --git a/db/migrate/20150509180749_convert_legacy_reference_notes.rb b/db/migrate/20150509180749_convert_legacy_reference_notes.rb
index cd8bf90108d..b02605489be 100644
--- a/db/migrate/20150509180749_convert_legacy_reference_notes.rb
+++ b/db/migrate/20150509180749_convert_legacy_reference_notes.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# Convert legacy Markdown-emphasized notes to the current, non-emphasized format
#
# _mentioned in 54f7727c850972f0401c1312a7c4a6a380de5666_
diff --git a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
index 9b02eda56ab..6a78294f0b2 100644
--- a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
+++ b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUserOauthApplicationsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :user_oauth_applications, :bool, default: true
diff --git a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
index 1f5cf1fe5f1..61ff0af41f4 100644
--- a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
+++ b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
def change
unless column_exists?(:application_settings, :session_expire_delay)
diff --git a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
index da0fd457a34..8eed8678b2f 100644
--- a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
+++ b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDefaultOtpRequiredForLoginValue < ActiveRecord::Migration
def up
execute %q{UPDATE users SET otp_required_for_login = FALSE WHERE otp_required_for_login IS NULL}
diff --git a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
index be30e881c74..78d45c7f96b 100644
--- a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
+++ b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUpdatedByToIssuablesAndNotes < ActiveRecord::Migration
def change
add_column :notes, :updated_by_id, :integer
diff --git a/db/migrate/20150818213832_add_sent_notifications.rb b/db/migrate/20150818213832_add_sent_notifications.rb
index fa0c3ce0acf..43e8d6a1a82 100644
--- a/db/migrate/20150818213832_add_sent_notifications.rb
+++ b/db/migrate/20150818213832_add_sent_notifications.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSentNotifications < ActiveRecord::Migration
def change
create_table :sent_notifications do |t|
diff --git a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
index 3f070139418..6e924262a13 100644
--- a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
+++ b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class EnableSslVerificationByDefault < ActiveRecord::Migration
def change
change_column :web_hooks, :enable_ssl_verification, :boolean, default: true
diff --git a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
index ea2ab6e4093..90ce6c2db3d 100644
--- a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
+++ b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class EnableSslVerificationForWebHooks < ActiveRecord::Migration
def up
execute("UPDATE web_hooks SET enable_ssl_verification = true")
diff --git a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
index a504f25b1be..37a27f11935 100644
--- a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
+++ b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddHelpPageTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :help_page_text, :text
diff --git a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
index c9b6e035122..6cf668a170e 100644
--- a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
+++ b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddCiEnabledToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :ci_enabled, :boolean, null: false, default: true
diff --git a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
index e1818b566d7..0aad6fe5e6e 100644
--- a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
+++ b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration
def up
execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)")
diff --git a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
index e6975f5b9fe..c8438b3f6aa 100644
--- a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
+++ b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddConsumedTimestepToUsers < ActiveRecord::Migration
def change
add_column :users, :consumed_timestep, :integer
diff --git a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
index 1bcb06e4bda..d9af4e71751 100644
--- a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
+++ b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddLineCodeToSentNotification < ActiveRecord::Migration
def change
add_column :sent_notifications, :line_code, :string
diff --git a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
index 905332b7dc7..1a761fe0f86 100644
--- a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
+++ b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddProjectIdToCiCommit < ActiveRecord::Migration
def up
add_column :ci_commits, :gl_project_id, :integer
diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
index fb0e0ba1fa5..2be57b6062e 100644
--- a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
+++ b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateProjectIdForCiCommits < ActiveRecord::Migration
def up
subquery = 'SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = ci_commits.project_id'
diff --git a/db/migrate/20150930001110_merge_request_error_field.rb b/db/migrate/20150930001110_merge_request_error_field.rb
index 71a8ae3938a..c2ee498ef3f 100644
--- a/db/migrate/20150930001110_merge_request_error_field.rb
+++ b/db/migrate/20150930001110_merge_request_error_field.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MergeRequestErrorField < ActiveRecord::Migration
def up
add_column :merge_requests, :merge_error, :string
diff --git a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
index 229c9942b50..8d47dac6441 100644
--- a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
+++ b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddNullToNameForCiProjects < ActiveRecord::Migration
def up
change_column_null :ci_projects, :name, true
diff --git a/db/migrate/20151002112914_add_stage_idx_to_builds.rb b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
index 4297ba0e7c8..68a745ffef4 100644
--- a/db/migrate/20151002112914_add_stage_idx_to_builds.rb
+++ b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddStageIdxToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :stage_idx, :integer
diff --git a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
index 3c0fcf6c45d..e3d2ac1cea5 100644
--- a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
+++ b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRefAndTagToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :tag, :boolean
diff --git a/db/migrate/20151005075649_add_user_id_to_build.rb b/db/migrate/20151005075649_add_user_id_to_build.rb
index be9d403e002..0f4b92b8b79 100644
--- a/db/migrate/20151005075649_add_user_id_to_build.rb
+++ b/db/migrate/20151005075649_add_user_id_to_build.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUserIdToBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :user_id, :integer
diff --git a/db/migrate/20151008143519_add_admin_notification_email_setting.rb b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
index f48ec9aa4a6..0bb581efe2c 100644
--- a/db/migrate/20151008143519_add_admin_notification_email_setting.rb
+++ b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddAdminNotificationEmailSetting < ActiveRecord::Migration
def change
add_column :application_settings, :admin_notification_email, :string
diff --git a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
index a54ac9d57a4..5a299f7b26d 100644
--- a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
+++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddArtifactsFileToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_file, :text
diff --git a/db/migrate/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb
index 8c05acfc190..299a24b0a7c 100644
--- a/db/migrate/20151019111551_fix_build_tags.rb
+++ b/db/migrate/20151019111551_fix_build_tags.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FixBuildTags < ActiveRecord::Migration
def up
execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'")
diff --git a/db/migrate/20151019111703_fail_build_without_names.rb b/db/migrate/20151019111703_fail_build_without_names.rb
index 362e31eb435..dcdb5d1b25d 100644
--- a/db/migrate/20151019111703_fail_build_without_names.rb
+++ b/db/migrate/20151019111703_fail_build_without_names.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FailBuildWithoutNames < ActiveRecord::Migration
def up
execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'")
diff --git a/db/migrate/20151020173516_ci_limits_to_mysql.rb b/db/migrate/20151020173516_ci_limits_to_mysql.rb
index 5314611cbcd..9bb960082f5 100644
--- a/db/migrate/20151020173516_ci_limits_to_mysql.rb
+++ b/db/migrate/20151020173516_ci_limits_to_mysql.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class CiLimitsToMysql < ActiveRecord::Migration
def change
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb
index 0666dfeaef4..41c0f0649cd 100644
--- a/db/migrate/20151023112551_fail_build_with_empty_name.rb
+++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class FailBuildWithEmptyName < ActiveRecord::Migration
def up
execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
diff --git a/db/migrate/20151023144219_remove_satellites.rb b/db/migrate/20151023144219_remove_satellites.rb
index 98fe0bd7d1d..e73f300028a 100644
--- a/db/migrate/20151023144219_remove_satellites.rb
+++ b/db/migrate/20151023144219_remove_satellites.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
require 'fileutils'
class RemoveSatellites < ActiveRecord::Migration
diff --git a/db/migrate/20151103133339_add_shared_runners_setting.rb b/db/migrate/20151103133339_add_shared_runners_setting.rb
index b5b34d4ca61..4231dfd5c2e 100644
--- a/db/migrate/20151103133339_add_shared_runners_setting.rb
+++ b/db/migrate/20151103133339_add_shared_runners_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSharedRunnersSetting < ActiveRecord::Migration
def up
add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false
diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
index 4e46ae8101c..7c57f3f0df6 100644
--- a/db/migrate/20151104105513_add_file_to_lfs_objects.rb
+++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddFileToLfsObjects < ActiveRecord::Migration
def change
add_column :lfs_objects, :file, :string
diff --git a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
index 25106ace7e9..01d8c0f043e 100644
--- a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
+++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false
diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb
index 793358c305e..7fc990f8d0a 100644
--- a/db/migrate/20151110125604_add_import_error_to_project.rb
+++ b/db/migrate/20151110125604_add_import_error_to_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportErrorToProject < ActiveRecord::Migration
def change
add_column :projects, :import_error, :text
diff --git a/db/migrate/20151201203948_raise_hook_url_limit.rb b/db/migrate/20151201203948_raise_hook_url_limit.rb
index c490b7ace0f..98a7fca6f6f 100644
--- a/db/migrate/20151201203948_raise_hook_url_limit.rb
+++ b/db/migrate/20151201203948_raise_hook_url_limit.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RaiseHookUrlLimit < ActiveRecord::Migration
def change
change_column :web_hooks, :url, :string, limit: 2000
diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
index 62a6d334f04..e1e4729f821 100644
--- a/db/migrate/20151209144329_migrate_ci_web_hooks.rb
+++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiWebHooks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb
index 5de7b205fb1..e1d92f0157e 100644
--- a/db/migrate/20151209145909_migrate_ci_emails.rb
+++ b/db/migrate/20151209145909_migrate_ci_emails.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiEmails < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151210030143_add_unlock_token_to_user.rb b/db/migrate/20151210030143_add_unlock_token_to_user.rb
index d23c648f782..0ea66ba65df 100644
--- a/db/migrate/20151210030143_add_unlock_token_to_user.rb
+++ b/db/migrate/20151210030143_add_unlock_token_to_user.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddUnlockTokenToUser < ActiveRecord::Migration
def change
add_column :users, :unlock_token, :string
diff --git a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
index 92c7b5befd2..00f88180e46 100644
--- a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
+++ b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRunnersRegistrationTokenToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :runners_registration_token, :string
diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb
index fff130b7b10..e6dca4c0008 100644
--- a/db/migrate/20151210125232_migrate_ci_slack_service.rb
+++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiSlackService < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
index 824f6f84195..72fcebf2959 100644
--- a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
+++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiHipChatService < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20151210125929_add_project_id_to_ci.rb b/db/migrate/20151210125929_add_project_id_to_ci.rb
index b5de64b82ca..84273591fa2 100644
--- a/db/migrate/20151210125929_add_project_id_to_ci.rb
+++ b/db/migrate/20151210125929_add_project_id_to_ci.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddProjectIdToCi < ActiveRecord::Migration
def change
add_column :ci_builds, :gl_project_id, :integer
diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb
index bb6d74ae212..c32c7feb193 100644
--- a/db/migrate/20151210125930_migrate_ci_to_project.rb
+++ b/db/migrate/20151210125930_migrate_ci_to_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class MigrateCiToProject < ActiveRecord::Migration
def up
migrate_project_id_for_table('ci_runner_projects')
diff --git a/db/migrate/20151218154042_add_tfa_to_application_settings.rb b/db/migrate/20151218154042_add_tfa_to_application_settings.rb
index afdaf76b917..dd95db775c5 100644
--- a/db/migrate/20151218154042_add_tfa_to_application_settings.rb
+++ b/db/migrate/20151218154042_add_tfa_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTfaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20151221234414_add_tfa_additional_fields.rb b/db/migrate/20151221234414_add_tfa_additional_fields.rb
index c3e4aaa606a..c16df47932f 100644
--- a/db/migrate/20151221234414_add_tfa_additional_fields.rb
+++ b/db/migrate/20151221234414_add_tfa_additional_fields.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTfaAdditionalFields < ActiveRecord::Migration
def change
change_table :users do |t|
diff --git a/db/migrate/20151224123230_rename_emojis.rb b/db/migrate/20151224123230_rename_emojis.rb
index 2c24f3beeea..62d921dfdcc 100644
--- a/db/migrate/20151224123230_rename_emojis.rb
+++ b/db/migrate/20151224123230_rename_emojis.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# Migration type: online without errors (works on previous version and new one)
class RenameEmojis < ActiveRecord::Migration
def up
diff --git a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
index e0dd19b2b06..259fd0248d2 100644
--- a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
+++ b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRecaptchaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20151229102248_influxdb_udp_port_setting.rb b/db/migrate/20151229102248_influxdb_udp_port_setting.rb
index 3e1bfd43899..ae0499f936d 100644
--- a/db/migrate/20151229102248_influxdb_udp_port_setting.rb
+++ b/db/migrate/20151229102248_influxdb_udp_port_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class InfluxdbUdpPortSetting < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_port, :integer, default: 8089
diff --git a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
index 4fcca06d905..6c282fc5039 100644
--- a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
+++ b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddArtifactsMetadataToCiBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_metadata, :text
diff --git a/db/migrate/20151231152326_add_akismet_to_application_settings.rb b/db/migrate/20151231152326_add_akismet_to_application_settings.rb
index 7b0fab6f557..3f52c758f9a 100644
--- a/db/migrate/20151231152326_add_akismet_to_application_settings.rb
+++ b/db/migrate/20151231152326_add_akismet_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddAkismetToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20160113111034_add_metrics_sample_interval.rb b/db/migrate/20160113111034_add_metrics_sample_interval.rb
index c1041da818c..b741f5d2c75 100644
--- a/db/migrate/20160113111034_add_metrics_sample_interval.rb
+++ b/db/migrate/20160113111034_add_metrics_sample_interval.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMetricsSampleInterval < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_sample_interval, :integer,
diff --git a/db/migrate/20160118155830_add_sentry_to_application_settings.rb b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
index a6f715263ef..fa7ff9d9228 100644
--- a/db/migrate/20160118155830_add_sentry_to_application_settings.rb
+++ b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSentryToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
diff --git a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
index 3837208f81e..d6c6aa4a4e8 100644
--- a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
+++ b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddBaseCommitShaToMergeRequestDiffs < ActiveRecord::Migration
def change
add_column :merge_request_diffs, :base_commit_sha, :string
diff --git a/db/migrate/20160128233227_change_lfs_objects_size_column.rb b/db/migrate/20160128233227_change_lfs_objects_size_column.rb
index 645c0cdb192..e7fd1f71777 100644
--- a/db/migrate/20160128233227_change_lfs_objects_size_column.rb
+++ b/db/migrate/20160128233227_change_lfs_objects_size_column.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class ChangeLfsObjectsSizeColumn < ActiveRecord::Migration
def change
change_column :lfs_objects, :size, :integer, limit: 8
diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
index b10c0602e24..d3ea956952e 100644
--- a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
+++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
include Gitlab::ShellAdapter
diff --git a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
index 332b5a756e8..f0d94226514 100644
--- a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
+++ b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMergeCommitShaToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :merge_commit_sha, :string
diff --git a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
index 11b6ff31000..f996ae74dca 100644
--- a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
+++ b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRealSizeToMergeRequestDiffs < ActiveRecord::Migration
def change
add_column :merge_request_diffs, :real_size, :string
diff --git a/db/migrate/20160217100506_add_description_to_label.rb b/db/migrate/20160217100506_add_description_to_label.rb
index af5af167470..eed6d1f236a 100644
--- a/db/migrate/20160217100506_add_description_to_label.rb
+++ b/db/migrate/20160217100506_add_description_to_label.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDescriptionToLabel < ActiveRecord::Migration
def change
add_column :labels, :description, :string
diff --git a/db/migrate/20160217174422_add_note_to_tasks.rb b/db/migrate/20160217174422_add_note_to_tasks.rb
index a9a2b77e423..da5cb2e05db 100644
--- a/db/migrate/20160217174422_add_note_to_tasks.rb
+++ b/db/migrate/20160217174422_add_note_to_tasks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddNoteToTasks < ActiveRecord::Migration
def change
add_reference :tasks, :note, index: true
diff --git a/db/migrate/20160220123949_rename_tasks_to_todos.rb b/db/migrate/20160220123949_rename_tasks_to_todos.rb
index f16b37537f3..30c10d27146 100644
--- a/db/migrate/20160220123949_rename_tasks_to_todos.rb
+++ b/db/migrate/20160220123949_rename_tasks_to_todos.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RenameTasksToTodos < ActiveRecord::Migration
def change
rename_table :tasks, :todos
diff --git a/db/migrate/20160229193553_add_main_language_to_repository.rb b/db/migrate/20160229193553_add_main_language_to_repository.rb
index ad5167b4c93..b5446c6a447 100644
--- a/db/migrate/20160229193553_add_main_language_to_repository.rb
+++ b/db/migrate/20160229193553_add_main_language_to_repository.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMainLanguageToRepository < ActiveRecord::Migration
def change
add_column :projects, :main_language, :string
diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
index 1f400566f9f..ffcd64266e3 100644
--- a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
+++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImportCredentialsToProjectImportData < ActiveRecord::Migration
def change
add_column :project_import_data, :encrypted_credentials, :text
diff --git a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
index 10f2b8cc56a..49e787d9a9a 100644
--- a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
+++ b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class DisallowBlankLineCodeOnNote < ActiveRecord::Migration
def up
execute("UPDATE notes SET line_code = NULL WHERE line_code = ''")
diff --git a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
index 65e0e61c78f..6871b3920df 100644
--- a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
+++ b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class ChangeTargetIdToNullOnTodos < ActiveRecord::Migration
def change
change_column_null :todos, :target_id, true
diff --git a/db/migrate/20160317092222_add_moved_to_to_issue.rb b/db/migrate/20160317092222_add_moved_to_to_issue.rb
index 9dde668ddff..461e7fb3a9b 100644
--- a/db/migrate/20160317092222_add_moved_to_to_issue.rb
+++ b/db/migrate/20160317092222_add_moved_to_to_issue.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMovedToToIssue < ActiveRecord::Migration
def change
add_reference :issues, :moved_to, references: :issues
diff --git a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
index a9a851cfe63..1fff9759d1e 100644
--- a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
+++ b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveTodosForDeletedIssues < ActiveRecord::Migration
def up
execute <<-SQL
diff --git a/db/migrate/20160328115649_migrate_new_notification_setting.rb b/db/migrate/20160328115649_migrate_new_notification_setting.rb
index eb6b7d07219..3c81b2c37bf 100644
--- a/db/migrate/20160328115649_migrate_new_notification_setting.rb
+++ b/db/migrate/20160328115649_migrate_new_notification_setting.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This migration will create one row of NotificationSetting for each Member row
# It can take long time on big instances.
#
diff --git a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
index b15af79b9b5..54cea964ff2 100644
--- a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
+++ b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveTodosForDeletedMergeRequests < ActiveRecord::Migration
def up
execute <<-SQL
diff --git a/db/migrate/20160407120251_add_images_enabled_for_project.rb b/db/migrate/20160407120251_add_images_enabled_for_project.rb
index fcffc98b47a..47f0ca8e8de 100644
--- a/db/migrate/20160407120251_add_images_enabled_for_project.rb
+++ b/db/migrate/20160407120251_add_images_enabled_for_project.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddImagesEnabledForProject < ActiveRecord::Migration
def change
add_column :projects, :container_registry_enabled, :boolean
diff --git a/db/migrate/20160413115152_add_token_to_web_hooks.rb b/db/migrate/20160413115152_add_token_to_web_hooks.rb
index 628b1d51b30..f04225068cd 100644
--- a/db/migrate/20160413115152_add_token_to_web_hooks.rb
+++ b/db/migrate/20160413115152_add_token_to_web_hooks.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTokenToWebHooks < ActiveRecord::Migration
def change
add_column :web_hooks, :token, :string
diff --git a/db/migrate/20160415062917_create_personal_access_tokens.rb b/db/migrate/20160415062917_create_personal_access_tokens.rb
index c7b49870bf7..94650026994 100644
--- a/db/migrate/20160415062917_create_personal_access_tokens.rb
+++ b/db/migrate/20160415062917_create_personal_access_tokens.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class CreatePersonalAccessTokens < ActiveRecord::Migration
def change
diff --git a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
index b53b9bc6c3d..d493044c67b 100644
--- a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
+++ b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :shared_runners_text, :text
diff --git a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
index 95ee03611d9..50f159a80b1 100644
--- a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
+++ b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class ConvertAwardNoteToEmojiAward < ActiveRecord::Migration
disable_ddl_transaction!
diff --git a/db/migrate/20160419120017_add_metrics_packet_size.rb b/db/migrate/20160419120017_add_metrics_packet_size.rb
index c759427c590..78c163d62ac 100644
--- a/db/migrate/20160419120017_add_metrics_packet_size.rb
+++ b/db/migrate/20160419120017_add_metrics_packet_size.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddMetricsPacketSize < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_packet_size, :integer, default: 1
diff --git a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
index bf50616656c..facd33875ba 100644
--- a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
+++ b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :disabled_oauth_sign_in_sources, :text
diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
index c60892a6279..84e5e4eabe2 100644
--- a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
+++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddRunUntaggedToCiRunner < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
diff --git a/db/migrate/20160508215820_add_type_to_notes.rb b/db/migrate/20160508215820_add_type_to_notes.rb
index c1d07c9363f..58944d4e651 100644
--- a/db/migrate/20160508215820_add_type_to_notes.rb
+++ b/db/migrate/20160508215820_add_type_to_notes.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddTypeToNotes < ActiveRecord::Migration
def change
add_column :notes, :type, :string
diff --git a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
index b6a5bea79b6..9d729fec189 100644
--- a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
+++ b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :health_check_access_token, :string
diff --git a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
index 3e26be7c09c..7910120b4e0 100644
--- a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
+++ b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveNotificationSettingsForDeletedProjects < ActiveRecord::Migration
def up
execute <<-SQL
diff --git a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
index d811fd5271e..e21376bd571 100644
--- a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
+++ b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
# This is ONLINE migration
class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration
diff --git a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
index 4f4f58b1619..fe1c863b5b9 100644
--- a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
+++ b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveDuplicatedNotificationSettings < ActiveRecord::Migration
def up
duplicates = exec_query(%Q{
diff --git a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
index 3c5d2ad910e..89826fb96cb 100644
--- a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
+++ b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class AddAfterSignUpTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :after_sign_up_text, :text
diff --git a/db/migrate/20160610204157_add_deployments.rb b/db/migrate/20160610204157_add_deployments.rb
index 0e7e6e747a3..0ee0b1f5a86 100644
--- a/db/migrate/20160610204157_add_deployments.rb
+++ b/db/migrate/20160610204157_add_deployments.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
class AddDeployments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160610204158_add_environments.rb b/db/migrate/20160610204158_add_environments.rb
index 699cee2b246..534a73a5fb6 100644
--- a/db/migrate/20160610204158_add_environments.rb
+++ b/db/migrate/20160610204158_add_environments.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
class AddEnvironments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb b/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb
index 013904b3f4f..d0e6d8d1ea1 100644
--- a/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb
+++ b/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb
@@ -1,6 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-# rubocop:disable all
class AddEnabledGitAccessProtocolsToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160616102642_remove_duplicated_keys.rb b/db/migrate/20160616102642_remove_duplicated_keys.rb
index 180a75e0998..5e41cc53e32 100644
--- a/db/migrate/20160616102642_remove_duplicated_keys.rb
+++ b/db/migrate/20160616102642_remove_duplicated_keys.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class RemoveDuplicatedKeys < ActiveRecord::Migration
def up
select_all("SELECT fingerprint FROM #{quote_table_name(:keys)} GROUP BY fingerprint HAVING COUNT(*) > 1").each do |row|
diff --git a/db/migrate/20160824124900_add_table_issue_metrics.rb b/db/migrate/20160824124900_add_table_issue_metrics.rb
index 30d35ef1db2..49be8bc949b 100644
--- a/db/migrate/20160824124900_add_table_issue_metrics.rb
+++ b/db/migrate/20160824124900_add_table_issue_metrics.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class AddTableIssueMetrics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160825052008_add_table_merge_request_metrics.rb b/db/migrate/20160825052008_add_table_merge_request_metrics.rb
index 56b39634dfd..3c9dcc08190 100644
--- a/db/migrate/20160825052008_add_table_merge_request_metrics.rb
+++ b/db/migrate/20160825052008_add_table_merge_request_metrics.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class AddTableMergeRequestMetrics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb
index 62ccb599f2e..7bead07fd76 100644
--- a/db/migrate/20161113184239_create_user_chat_names_table.rb
+++ b/db/migrate/20161113184239_create_user_chat_names_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class CreateUserChatNamesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170130221926_create_uploads.rb b/db/migrate/20170130221926_create_uploads.rb
index 4d9fa0bb692..6f06c5dd840 100644
--- a/db/migrate/20170130221926_create_uploads.rb
+++ b/db/migrate/20170130221926_create_uploads.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class CreateUploads < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170222143317_drop_ci_projects.rb b/db/migrate/20170222143317_drop_ci_projects.rb
index 9973e53501c..4db8658f36f 100644
--- a/db/migrate/20170222143317_drop_ci_projects.rb
+++ b/db/migrate/20170222143317_drop_ci_projects.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class DropCiProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
index 0535c2ddaf2..ee802ab34ca 100644
--- a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
+++ b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
@@ -1,5 +1,4 @@
# rubocop:disable Migration/RemoveColumn
-# rubocop:disable Migration/Datetime
class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170329095907_create_ci_trigger_schedules.rb b/db/migrate/20170329095907_create_ci_trigger_schedules.rb
index 06a2010db23..cfcfa27ebb5 100644
--- a/db/migrate/20170329095907_create_ci_trigger_schedules.rb
+++ b/db/migrate/20170329095907_create_ci_trigger_schedules.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class CreateCiTriggerSchedules < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
index 84635fa39b9..42c90103262 100644
--- a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
+++ b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
@@ -1,7 +1,5 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
-# rubocop:disable RemoveIndex
class RemoveIndexForUsersCurrentSignInAt < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170425112128_create_pipeline_schedules_table.rb b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
index 4f9c56a1ad8..bd15b9eef19 100644
--- a/db/migrate/20170425112128_create_pipeline_schedules_table.rb
+++ b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
# rubocop:disable Migration/Timestamps
class CreatePipelineSchedulesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb b/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb
index 008a94d8334..1c5d4997d40 100644
--- a/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb
+++ b/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb
@@ -1,2 +1 @@
-# rubocop:disable all
require_relative 'markdown_cache_limits_to_mysql'
diff --git a/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb b/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb
index 470c3b8166c..f858611d24b 100644
--- a/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb
+++ b/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb
@@ -6,6 +6,7 @@ class RenameUsersLdapEmailToExternalEmail < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :ldap_email, :external_email
end
diff --git a/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb b/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb
index ad540b1e509..0f1e937545b 100644
--- a/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb
+++ b/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb
@@ -10,6 +10,7 @@ class AddTimezoneToIssuesClosedAt < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
change_column_type_concurrently(:issues, :closed_at, :datetime_with_timezone)
end
diff --git a/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb b/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb
index 5a36dec6a9a..36a85b61968 100644
--- a/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb
+++ b/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb
@@ -25,6 +25,7 @@ class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration
# Due to some EE merge problems some environments may not have the
# "closed_at_for_type_change" column. If this is the case we have no
# other option than to migrate the data _right now_.
+ # rubocop:disable Migration/UpdateLargeTable
change_column_type_concurrently(:issues, :closed_at, :datetime_with_timezone)
cleanup_concurrent_column_type_change(:issues, :closed_at)
end
diff --git a/db/migrate/20180214093516_create_badges.rb b/db/migrate/20180214093516_create_badges.rb
index 6559f834484..a1d77328f77 100644
--- a/db/migrate/20180214093516_create_badges.rb
+++ b/db/migrate/20180214093516_create_badges.rb
@@ -12,6 +12,7 @@ class CreateBadges < ActiveRecord::Migration
t.timestamps_with_timezone null: false
end
+ # rubocop:disable Migration/AddConcurrentForeignKey
add_foreign_key :badges, :namespaces, column: :group_id, on_delete: :cascade
end
end
diff --git a/db/migrate/20180227182112_add_group_id_to_boards_ce.rb b/db/migrate/20180227182112_add_group_id_to_boards_ce.rb
index f54dd8d7687..5b2691b3a00 100644
--- a/db/migrate/20180227182112_add_group_id_to_boards_ce.rb
+++ b/db/migrate/20180227182112_add_group_id_to_boards_ce.rb
@@ -8,6 +8,7 @@ class AddGroupIdToBoardsCe < ActiveRecord::Migration
def up
return if group_id_exists?
+ # rubocop:disable Migration/AddConcurrentForeignKey
add_column :boards, :group_id, :integer
add_foreign_key :boards, :namespaces, column: :group_id, on_delete: :cascade
add_concurrent_index :boards, :group_id
@@ -18,6 +19,7 @@ class AddGroupIdToBoardsCe < ActiveRecord::Migration
def down
return unless group_id_exists?
+ # rubocop:disable Migration/RemoveIndex
remove_foreign_key :boards, column: :group_id
remove_index :boards, :group_id if index_exists? :boards, :group_id
remove_column :boards, :group_id
diff --git a/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb b/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb
index 8298979e96a..e6cec39e61f 100644
--- a/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb
+++ b/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb
@@ -8,6 +8,7 @@ class EnsureForeignKeysOnClustersApplications < ActiveRecord::Migration
disable_ddl_transaction!
+ # rubocop:disable Cop/InBatches
def up
existing = Clusters::Cluster
.joins(:application_ingress)
diff --git a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
index 18a5c69df1b..671fa743cec 100644
--- a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
+++ b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb
@@ -7,15 +7,16 @@ class AddPartialIndexesOnTodos < ActiveRecord::Migration
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
- disable_ddl_transaction!
+ disable_ddl_transaction!
+
+ INDEX_NAME_PENDING = "index_todos_on_user_id_and_id_pending"
+ INDEX_NAME_DONE = "index_todos_on_user_id_and_id_done"
- INDEX_NAME_PENDING="index_todos_on_user_id_and_id_pending"
- INDEX_NAME_DONE="index_todos_on_user_id_and_id_done"
-
def up
unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_PENDING)
add_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
end
+
unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_DONE)
add_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
end
@@ -24,5 +25,5 @@ class AddPartialIndexesOnTodos < ActiveRecord::Migration
def down
remove_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING)
remove_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE)
- end
+ end
end
diff --git a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb
index 007cbebaf1b..e852d50b25e 100644
--- a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb
+++ b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb
@@ -6,6 +6,7 @@ class RenameUsersRssTokenToFeedToken < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :rss_token, :feed_token
end
diff --git a/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb b/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb
index 582b89a3948..58de795472a 100644
--- a/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb
+++ b/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb
@@ -26,6 +26,7 @@ class AddIndexConstraintsToInternalIdTable < ActiveRecord::Migration
end
private
+
def replace_index(table, columns, name:)
temporary_name = "#{name}_old"
diff --git a/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb b/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb
index 1084ca14a34..ac91624c3d5 100644
--- a/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb
+++ b/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb
@@ -10,6 +10,7 @@ class AddIndexToCiJobArtifactsFileStore < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :ci_job_artifacts, :file_store if index_exists?(:ci_job_artifacts, :file_store)
end
end
diff --git a/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb b/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb
index 306cd737771..f5afdb0e4e6 100644
--- a/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb
+++ b/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb
@@ -4,6 +4,7 @@ class AddEnforceTermsToApplicationSettings < ActiveRecord::Migration
DOWNTIME = false
def change
+ # rubocop:disable Migration/SaferBooleanColumn
add_column :application_settings, :enforce_terms, :boolean, default: false
end
end
diff --git a/db/migrate/20180425075446_create_term_agreements.rb b/db/migrate/20180425075446_create_term_agreements.rb
index 22a9d7b574d..1fa2c8dd3be 100644
--- a/db/migrate/20180425075446_create_term_agreements.rb
+++ b/db/migrate/20180425075446_create_term_agreements.rb
@@ -21,6 +21,7 @@ class CreateTermAgreements < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :term_agreements, name: 'term_agreements_unique_index'
drop_table :term_agreements
diff --git a/db/migrate/20180503131624_create_remote_mirrors.rb b/db/migrate/20180503131624_create_remote_mirrors.rb
index 7800186455f..249882f8613 100644
--- a/db/migrate/20180503131624_create_remote_mirrors.rb
+++ b/db/migrate/20180503131624_create_remote_mirrors.rb
@@ -23,6 +23,7 @@ class CreateRemoteMirrors < ActiveRecord::Migration
t.string :encrypted_credentials_iv
t.string :encrypted_credentials_salt
+ # rubocop:disable Migration/Timestamps
t.timestamps null: false
end
end
diff --git a/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb b/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb
index 4c4e576d49f..9e55690bd33 100644
--- a/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb
+++ b/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb
@@ -14,6 +14,7 @@ class AddIndexToNamespacesRunnersToken < ActiveRecord::Migration
def down
if index_exists?(:namespaces, :runners_token, unique: true)
+ # rubocop:disable Migration/RemoveIndex
remove_index :namespaces, :runners_token
end
end
diff --git a/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb b/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb
index 17570269b2e..b59b941c815 100644
--- a/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb
+++ b/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb
@@ -11,6 +11,7 @@ class AddIndexesToProjectMirrorData < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :project_mirror_data, :jid if index_exists? :project_mirror_data, :jid
remove_index :project_mirror_data, :status if index_exists? :project_mirror_data, :status
end
diff --git a/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb b/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb
index 9a9decffdab..4af42b4fb29 100644
--- a/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb
+++ b/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb
@@ -10,6 +10,7 @@ class AddIndexesToRemoteMirror < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :remote_mirrors, :last_successful_update_at if index_exists? :remote_mirrors, :last_successful_update_at
end
end
diff --git a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb
index 580f56007c7..f3ed20fd243 100644
--- a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb
+++ b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb
@@ -10,6 +10,7 @@ class AddIndexOnCiRunnersRunnerType < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/RemoveIndex
remove_index :ci_runners, :runner_type
end
end
diff --git a/db/migrate/20180515005612_add_squash_to_merge_requests.rb b/db/migrate/20180515005612_add_squash_to_merge_requests.rb
index f526b45bd4b..fd85e968acd 100644
--- a/db/migrate/20180515005612_add_squash_to_merge_requests.rb
+++ b/db/migrate/20180515005612_add_squash_to_merge_requests.rb
@@ -9,6 +9,7 @@ class AddSquashToMergeRequests < ActiveRecord::Migration
def up
unless column_exists?(:merge_requests, :squash)
+ # rubocop:disable Migration/UpdateLargeTable
add_column_with_default :merge_requests, :squash, :boolean, default: false, allow_null: false
end
end
diff --git a/db/migrate/20180515121227_create_notes_diff_files.rb b/db/migrate/20180515121227_create_notes_diff_files.rb
index 7108bc1a64b..efcd3bb9c7e 100644
--- a/db/migrate/20180515121227_create_notes_diff_files.rb
+++ b/db/migrate/20180515121227_create_notes_diff_files.rb
@@ -16,6 +16,7 @@ class CreateNotesDiffFiles < ActiveRecord::Migration
t.text :old_path, null: false
end
+ # rubocop:disable Migration/AddConcurrentForeignKey
add_foreign_key :note_diff_files, :notes, column: :diff_note_id, on_delete: :cascade
end
end
diff --git a/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb b/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb
index df84898003f..08ce8cc3094 100644
--- a/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb
+++ b/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb
@@ -1,2 +1 @@
-# rubocop:disable all
require_relative 'gpg_keys_limits_to_mysql'
diff --git a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb
index 290416cb61c..22e9482cb1d 100644
--- a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb
+++ b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb
@@ -6,6 +6,7 @@ class EnsureRemoteMirrorColumns < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/Datetime
add_column :remote_mirrors, :last_update_started_at, :datetime unless column_exists?(:remote_mirrors, :last_update_started_at)
add_column :remote_mirrors, :remote_name, :string unless column_exists?(:remote_mirrors, :remote_name)
diff --git a/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb b/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb
index d0dcacc5b66..dbbbcd1f622 100644
--- a/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb
+++ b/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb
@@ -4,13 +4,13 @@ class ChangeDefaultValueForDsaKeyRestriction < ActiveRecord::Migration
def up
change_column :application_settings, :dsa_key_restriction, :integer, null: false,
- default: -1
+ default: -1
execute("UPDATE application_settings SET dsa_key_restriction = -1")
end
def down
change_column :application_settings, :dsa_key_restriction, :integer, null: false,
- default: 0
+ default: 0
end
end
diff --git a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
index dcbbef9bd4a..36f2a593fbe 100644
--- a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
+++ b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
@@ -11,6 +11,7 @@ class RenameMergeRequestsAllowCollaboration < ActiveRecord::Migration
def up
if column_exists?(:merge_requests, :allow_collaboration)
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
diff --git a/db/migrate/20180629153018_create_site_statistics.rb b/db/migrate/20180629153018_create_site_statistics.rb
new file mode 100644
index 00000000000..085ce1ba64b
--- /dev/null
+++ b/db/migrate/20180629153018_create_site_statistics.rb
@@ -0,0 +1,18 @@
+class CreateSiteStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ create_table :site_statistics do |t|
+ t.integer :repositories_count, default: 0, null: false
+ t.integer :wikis_count, default: 0, null: false
+ end
+
+ execute('INSERT INTO site_statistics (id) VALUES(1)')
+ end
+
+ def down
+ drop_table :site_statistics
+ end
+end
diff --git a/db/migrate/20180702124358_remove_orphaned_routes.rb b/db/migrate/20180702124358_remove_orphaned_routes.rb
new file mode 100644
index 00000000000..6f6e289ba87
--- /dev/null
+++ b/db/migrate/20180702124358_remove_orphaned_routes.rb
@@ -0,0 +1,49 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveOrphanedRoutes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class Route < ActiveRecord::Base
+ self.table_name = 'routes'
+ include EachBatch
+
+ def self.orphaned_namespace_routes
+ where(source_type: 'Namespace')
+ .where('NOT EXISTS ( SELECT 1 FROM namespaces WHERE namespaces.id = routes.source_id )')
+ end
+
+ def self.orphaned_project_routes
+ where(source_type: 'Project')
+ .where('NOT EXISTS ( SELECT 1 FROM projects WHERE projects.id = routes.source_id )')
+ end
+ end
+
+ def up
+ # Some of these queries can take up to 10 seconds to run on GitLab.com,
+ # which is pretty close to our 15 second statement timeout. To ensure a
+ # smooth deployment procedure we disable the statement timeouts for this
+ # migration, just in case.
+ disable_statement_timeout
+
+ # On GitLab.com there are around 4000 orphaned project routes, and around
+ # 150 orphaned namespace routes.
+ [
+ Route.orphaned_project_routes,
+ Route.orphaned_namespace_routes
+ ].each do |relation|
+ relation.each_batch(of: 1_000) do |batch|
+ batch.delete_all
+ end
+ end
+ end
+
+ def down
+ # There is no way to restore orphaned routes, and this doesn't make any
+ # sense anyway.
+ end
+end
diff --git a/db/migrate/20180702134423_generate_missing_routes.rb b/db/migrate/20180702134423_generate_missing_routes.rb
new file mode 100644
index 00000000000..994725f9bd1
--- /dev/null
+++ b/db/migrate/20180702134423_generate_missing_routes.rb
@@ -0,0 +1,143 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+# This migration generates missing routes for any projects and namespaces that
+# don't already have a route.
+#
+# On GitLab.com this would insert 611 project routes, and 0 namespace routes.
+# The exact number could vary per instance, so we take care of both just in
+# case.
+class GenerateMissingRoutes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class User < ActiveRecord::Base
+ self.table_name = 'users'
+ end
+
+ class Route < ActiveRecord::Base
+ self.table_name = 'routes'
+ end
+
+ module Routable
+ def build_full_path
+ if parent && path
+ parent.build_full_path + '/' + path
+ else
+ path
+ end
+ end
+
+ def build_full_name
+ if parent && name
+ parent.human_name + ' / ' + name
+ else
+ name
+ end
+ end
+
+ def human_name
+ build_full_name
+ end
+
+ def attributes_for_insert
+ time = Time.zone.now
+
+ {
+ # We can't use "self.class.name" here as that would include the
+ # migration namespace.
+ source_type: source_type_for_route,
+ source_id: id,
+ created_at: time,
+ updated_at: time,
+ name: build_full_name,
+
+ # The route path might already be taken. Instead of trying to generate a
+ # new unique name on every conflict, we just append the row ID to the
+ # route path.
+ path: "#{build_full_path}-#{id}"
+ }
+ end
+ end
+
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ include EachBatch
+ include GenerateMissingRoutes::Routable
+
+ belongs_to :namespace, class_name: 'GenerateMissingRoutes::Namespace'
+
+ has_one :route,
+ as: :source,
+ inverse_of: :source,
+ class_name: 'GenerateMissingRoutes::Route'
+
+ alias_method :parent, :namespace
+ alias_attribute :parent_id, :namespace_id
+
+ def self.without_routes
+ where(
+ 'NOT EXISTS (
+ SELECT 1
+ FROM routes
+ WHERE source_type = ?
+ AND source_id = projects.id
+ )',
+ 'Project'
+ )
+ end
+
+ def source_type_for_route
+ 'Project'
+ end
+ end
+
+ class Namespace < ActiveRecord::Base
+ self.table_name = 'namespaces'
+
+ include EachBatch
+ include GenerateMissingRoutes::Routable
+
+ belongs_to :parent, class_name: 'GenerateMissingRoutes::Namespace'
+ belongs_to :owner, class_name: 'GenerateMissingRoutes::User'
+
+ has_one :route,
+ as: :source,
+ inverse_of: :source,
+ class_name: 'GenerateMissingRoutes::Route'
+
+ def self.without_routes
+ where(
+ 'NOT EXISTS (
+ SELECT 1
+ FROM routes
+ WHERE source_type = ?
+ AND source_id = namespaces.id
+ )',
+ 'Namespace'
+ )
+ end
+
+ def source_type_for_route
+ 'Namespace'
+ end
+ end
+
+ def up
+ [Namespace, Project].each do |model|
+ model.without_routes.each_batch(of: 100) do |batch|
+ rows = batch.map(&:attributes_for_insert)
+
+ Gitlab::Database.bulk_insert(:routes, rows)
+ end
+ end
+ end
+
+ def down
+ # Removing routes we previously generated makes no sense.
+ end
+end
diff --git a/db/migrate/gpg_keys_limits_to_mysql.rb b/db/migrate/gpg_keys_limits_to_mysql.rb
index 780340d0564..38729320d8c 100644
--- a/db/migrate/gpg_keys_limits_to_mysql.rb
+++ b/db/migrate/gpg_keys_limits_to_mysql.rb
@@ -1,5 +1,4 @@
class IncreaseMysqlTextLimitForGpgKeys < ActiveRecord::Migration
-
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
diff --git a/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb b/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb
index e1771912c3c..9fd23aae1e5 100644
--- a/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb
+++ b/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb
@@ -4,6 +4,6 @@ class LimitsCiBuildTraceChunksRawDataForMysql < ActiveRecord::Migration
# Mysql needs MEDIUMTEXT type (up to 16MB) rather than TEXT (up to 64KB)
# Because 'raw_data' is always capped by Ci::BuildTraceChunk::CHUNK_SIZE, which is 128KB
- change_column :ci_build_trace_chunks, :raw_data, :binary, limit: 16.megabytes - 1 #MEDIUMTEXT
+ change_column :ci_build_trace_chunks, :raw_data, :binary, limit: 16.megabytes - 1 # MEDIUMTEXT
end
end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 8f8d8f27410..7507a4bb431 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -1,4 +1,3 @@
-# rubocop:disable all
class LimitsToMysql < ActiveRecord::Migration
def up
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/optional_migrations/composite_primary_keys.rb b/db/optional_migrations/composite_primary_keys.rb
index 0fd3fca52dd..d45705021b0 100644
--- a/db/optional_migrations/composite_primary_keys.rb
+++ b/db/optional_migrations/composite_primary_keys.rb
@@ -21,8 +21,8 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
Index.new(:merge_request_diff_commits, 'index_merge_request_diff_commits_on_mr_diff_id_and_order', %i(merge_request_diff_id relative_order)),
Index.new(:project_authorizations, 'index_project_authorizations_on_user_id_project_id_access_level', %i(user_id project_id access_level)),
Index.new(:push_event_payloads, 'index_push_event_payloads_on_event_id', %i(event_id)),
- Index.new(:schema_migrations, 'unique_schema_migrations', %(version)),
- ]
+ Index.new(:schema_migrations, 'unique_schema_migrations', %(version))
+ ].freeze
disable_ddl_transaction!
@@ -45,6 +45,7 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
end
private
+
def add_primary_key(index)
execute "ALTER TABLE #{index.table} ADD PRIMARY KEY USING INDEX #{index.name}"
end
@@ -60,4 +61,3 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
execute "ALTER TABLE #{index.table} DROP CONSTRAINT IF EXISTS #{temp_index_name}"
end
end
-
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
index d322844e2fd..017c58477ac 100644
--- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -124,7 +124,7 @@ class RenameReservedProjectNames < ActiveRecord::Migration
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
- project.update_attributes(path: path) &&
+ project.update(path: path) &&
project.respond_to?(:rename_repo)
end
end
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 6a49450cc50..3e8ccfdb899 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -66,7 +66,7 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
- project.update_attributes(path: path) &&
+ project.update(path: path) &&
project.respond_to?(:rename_repo)
end
end
diff --git a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
index 159b533eaaa..24750c58ef0 100644
--- a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
+++ b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/Datetime
class DropCiTriggerSchedulesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb b/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb
index 15edb402b86..a70e3985005 100644
--- a/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb
+++ b/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb
@@ -10,6 +10,7 @@ class CleanupUsersLdapEmailRename < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :external_email, :ldap_email
end
end
diff --git a/db/post_migrate/20170711145558_migrate_stages_statuses.rb b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
index aeb900354db..65755c0e824 100644
--- a/db/post_migrate/20170711145558_migrate_stages_statuses.rb
+++ b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
@@ -28,6 +28,7 @@ class MigrateStagesStatuses < ActiveRecord::Migration
def down
disable_statement_timeout
+ # rubocop:disable Migration/UpdateLargeTable
update_column_in_batches(:ci_stages, :status, nil)
end
end
diff --git a/db/post_migrate/20170830150306_drop_events_for_migration_table.rb b/db/post_migrate/20170830150306_drop_events_for_migration_table.rb
index 763ee9a810d..69a612ead40 100644
--- a/db/post_migrate/20170830150306_drop_events_for_migration_table.rb
+++ b/db/post_migrate/20170830150306_drop_events_for_migration_table.rb
@@ -18,7 +18,6 @@ class DropEventsForMigrationTable < ActiveRecord::Migration
end
end
- # rubocop: disable Migration/Datetime
def down
create_table :events_for_migration do |t|
t.string :target_type, index: true
diff --git a/db/post_migrate/20171106154015_remove_issues_branch_name.rb b/db/post_migrate/20171106154015_remove_issues_branch_name.rb
index 162b6bafab4..3d08225c96d 100644
--- a/db/post_migrate/20171106154015_remove_issues_branch_name.rb
+++ b/db/post_migrate/20171106154015_remove_issues_branch_name.rb
@@ -1,4 +1,3 @@
-# rubocop:disable Migration/RemoveColumn
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
diff --git a/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb b/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb
index 88dd8f89ba6..53f376f216b 100644
--- a/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb
+++ b/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb
@@ -13,6 +13,7 @@ class CleanupAddTimezoneToIssuesClosedAt < ActiveRecord::Migration
end
# rubocop:disable Migration/Datetime
+ # rubocop:disable Migration/UpdateLargeTable
def down
change_column_type_concurrently(:issues, :closed_at, :datetime)
end
diff --git a/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb b/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb
index fce1829c982..980f76e7d57 100644
--- a/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb
+++ b/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable GitlabSecurity/SqlInjection
class SchedulePopulateMergeRequestMetricsWithEventsData < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb
index 9addd36dca6..8c8dbb1a043 100644
--- a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb
+++ b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb
@@ -43,8 +43,6 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration
end
end
- private
-
class PostgresStrategy < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
@@ -79,6 +77,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration
end
private
+
def insert_missing_records
iteration = 0
records = 0
diff --git a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb
index bff83379087..3d77ff921c7 100644
--- a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb
+++ b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb
@@ -8,6 +8,7 @@ class CleanupUsersRssTokenRename < ActiveRecord::Migration
end
def down
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :users, :feed_token, :rss_token
end
end
diff --git a/db/post_migrate/20180424151928_fill_file_store.rb b/db/post_migrate/20180424151928_fill_file_store.rb
index b41feb233be..03d54dab250 100644
--- a/db/post_migrate/20180424151928_fill_file_store.rb
+++ b/db/post_migrate/20180424151928_fill_file_store.rb
@@ -38,7 +38,7 @@ class FillFileStore < ActiveRecord::Migration
def up
# NOTE: Schedule background migrations that fill 'NULL' value by '1'(ObjectStorage::Store::LOCAL) on `file_store`, `store` columns
- #
+ #
# Here are the target columns
# - ci_job_artifacts.file_store
# - lfs_objects.file_store
diff --git a/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb b/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb
index 38af5aae924..0e6ec46e5f0 100644
--- a/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb
+++ b/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb
@@ -9,6 +9,7 @@ class BackfillRunnerTypeForCiRunnersPostMigrate < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateColumnInBatches
update_column_in_batches(:ci_runners, :runner_type, INSTANCE_RUNNER_TYPE) do |table, query|
query.where(table[:is_shared].eq(true)).where(table[:runner_type].eq(nil))
end
diff --git a/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb b/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb
index e39cd33c414..08d7d64a2c5 100644
--- a/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb
+++ b/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb
@@ -34,5 +34,4 @@ class MigrateImportAttributesDataFromProjectsToProjectMirrorData < ActiveRecord:
queue_background_migration_jobs_by_range_at_intervals(import_state, DOWN_MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
-
end
diff --git a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb
index d9d9e93f5a3..fb9616f0c07 100644
--- a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb
+++ b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb
@@ -8,6 +8,8 @@ class SetMinimalProjectBuildTimeout < ActiveRecord::Migration
disable_ddl_transaction!
def up
+ # rubocop:disable Migration/UpdateLargeTable
+ # rubocop:disable Migration/UpdateColumnInBatches
update_column_in_batches(:projects, :build_timeout, MINIMUM_TIMEOUT) do |table, query|
query.where(table[:build_timeout].lt(MINIMUM_TIMEOUT))
end
diff --git a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
index 7301bcf2c6c..7eca7394f5f 100644
--- a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
+++ b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
@@ -11,6 +11,7 @@ class CleanupMergeRequestsAllowMaintainerToPushRename < ActiveRecord::Migration
def down
if column_exists?(:merge_requests, :allow_collaboration)
+ # rubocop:disable Migration/UpdateLargeTable
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
diff --git a/db/post_migrate/20180704145007_update_project_indexes.rb b/db/post_migrate/20180704145007_update_project_indexes.rb
index 193563b36db..0e2601ad4fa 100644
--- a/db/post_migrate/20180704145007_update_project_indexes.rb
+++ b/db/post_migrate/20180704145007_update_project_indexes.rb
@@ -14,7 +14,7 @@ class UpdateProjectIndexes < ActiveRecord::Migration
[:repository_storage, :created_at],
name: NEW_INDEX_NAME,
where: 'last_repository_check_at IS NULL'
- )
+ )
end
def down
diff --git a/db/post_migrate/20180706223200_populate_site_statistics.rb b/db/post_migrate/20180706223200_populate_site_statistics.rb
new file mode 100644
index 00000000000..e78e9eb900a
--- /dev/null
+++ b/db/post_migrate/20180706223200_populate_site_statistics.rb
@@ -0,0 +1,25 @@
+class PopulateSiteStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET repositories_count = (SELECT COUNT(*) FROM projects)")
+ end
+
+ transaction do
+ execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
+
+ execute("UPDATE site_statistics SET wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)")
+ end
+ end
+
+ def down
+ # No downside in keeping the counter up-to-date
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3db11d8447e..8ae0197d1b4 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1841,6 +1841,11 @@ ActiveRecord::Schema.define(version: 20180722103201) do
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
add_index "services", ["template"], name: "index_services_on_template", using: :btree
+ create_table "site_statistics", force: :cascade do |t|
+ t.integer "repositories_count", default: 0, null: false
+ t.integer "wikis_count", default: 0, null: false
+ end
+
create_table "snippets", force: :cascade do |t|
t.string "title"
t.text "content"
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index 83a714810c1..7ae4f7c1515 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -66,6 +66,19 @@ On CentOS:
sudo yum install gdb
```
+### rbtrace
+
+GitLab 11.2 ships with [rbtrace](https://github.com/tmm1/rbtrace), which
+allows you to trace Ruby code, view all running threads, take memory dumps,
+and more. However, this is not enabled by default. To enable it, define the
+`ENABLE_RBTRACE` variable to the environment. For example, in Omnibus:
+
+```ruby
+gitlab_rails['env'] = {"ENABLE_RBTRACE" => "1"}
+```
+
+Then reconfigure the system and restart Unicorn and Sidekiq.
+
## Common Problems
Many of the tips to diagnose issues below apply to many different situations. We'll use one
diff --git a/doc/api/members.md b/doc/api/members.md
index 8ebe464c359..7b228b92594 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -40,7 +40,9 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
"access_level": 30
},
{
@@ -48,7 +50,65 @@ Example response:
"username": "john_doe",
"name": "John Doe",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ }
+]
+```
+
+## List all members of a group or project including inherited members
+
+Gets a list of group or project members viewable by the authenticated user, including inherited members through ancestor groups.
+
+```
+GET /groups/:id/members/all
+GET /projects/:id/members/all
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `query` | string | no | A query string to search for members |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/all
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/all
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 1,
+ "username": "raymond_smith",
+ "name": "Raymond Smith",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ },
+ {
+ "id": 2,
+ "username": "john_doe",
+ "name": "John Doe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ },
+ {
+ "id": 3,
+ "username": "foo_bar",
+ "name": "Foo bar",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-11-22T14:13:35Z",
"access_level": 30
}
]
@@ -81,7 +141,8 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
"access_level": 30,
"expires_at": null
}
@@ -116,7 +177,9 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
"access_level": 30
}
```
@@ -150,7 +213,9 @@ Example response:
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
- "created_at": "2012-10-22T14:13:35Z",
+ "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
+ "web_url": "http://192.168.1.8:3000/root",
+ "expires_at": "2012-10-22T14:13:35Z",
"access_level": 40
}
```
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index b4a2349844b..6c6e198a7c3 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -201,6 +201,148 @@ lot of dependant objects.
To limit the amount of queries performed, we can use `BatchLoader`.
+## Mutations
+
+Mutations are used to change any stored values, or to trigger
+actions. In the same way a GET-request should not modify data, we
+cannot modify data in a regular GraphQL-query. We can however in a
+mutation.
+
+### Fields
+
+In the most common situations, a mutation would return 2 fields:
+
+- The resource being modified
+- A list of errors explaining why the action could not be
+ performed. If the mutation succeeded, this list would be empty.
+
+By inheriting any new mutations from `Mutations::BaseMutation` the
+`errors` field is automatically added. A `clientMutationId` field is
+also added, this can be used by the client to identify the result of a
+single mutation when multiple are performed within a single request.
+
+### Building Mutations
+
+Mutations live in `app/graphql/mutations` ideally grouped per
+resources they are mutating, similar to our services. They should
+inherit `Mutations::BaseMutation`. The fields defined on the mutation
+will be returned as the result of the mutation.
+
+Always provide a consistent GraphQL-name to the mutation, this name is
+used to generate the input types and the field the mutation is mounted
+on. The name should look like `<Resource being modified><Mutation
+class name>`, for example the `Mutations::MergeRequests::SetWip`
+mutation has GraphQL name `MergeRequestSetWip`.
+
+Arguments required by the mutation can be defined as arguments
+required for a field. These will be wrapped up in an input type for
+the mutation. For example, the `Mutations::MergeRequests::SetWip`
+with GraphQL-name `MergeRequestSetWip` defines these arguments:
+
+```ruby
+argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: "The project the merge request to mutate is in"
+
+argument :iid, GraphQL::ID_TYPE,
+ required: true,
+ description: "The iid of the merge request to mutate"
+
+argument :wip,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: <<~DESC
+ Whether or not to set the merge request as a WIP.
+ If not passed, the value will be toggled.
+ DESC
+```
+
+This would automatically generate an input type called
+`MergeRequestSetWipInput` with the 3 arguments we specified and the
+`clientMutationId`.
+
+These arguments are then passed to the `resolve` method of a mutation
+as keyword arguments. From here, we can call the service that will
+modify the resource.
+
+The `resolve` method should then return a hash with the same field
+names as defined on the mutation and an `errors` array. For example,
+the `Mutations::MergeRequests::SetWip` defines a `merge_request`
+field:
+
+```ruby
+field :merge_request,
+ Types::MergeRequestType,
+ null: true,
+ description: "The merge request after mutation"
+```
+
+This means that the hash returned from `resolve` in this mutation
+should look like this:
+
+```ruby
+{
+ # The merge request modified, this will be wrapped in the type
+ # defined on the field
+ merge_request: merge_request,
+ # An array if strings if the mutation failed after authorization
+ errors: merge_request.errors.full_messages
+}
+```
+
+To make the mutation available it should be defined on the mutation
+type that lives in `graphql/types/mutation_types`. The
+`mount_mutation` helper method will define a field based on the
+GraphQL-name of the mutation:
+
+```ruby
+module Types
+ class MutationType < BaseObject
+ include Gitlab::Graphql::MountMutation
+
+ graphql_name "Mutation"
+
+ mount_mutation Mutations::MergeRequests::SetWip
+ end
+end
+```
+
+Will generate a field called `mergeRequestSetWip` that
+`Mutations::MergeRequests::SetWip` to be resolved.
+
+### Authorizing resources
+
+To authorize resources inside a mutation, we can include the
+`Gitlab::Graphql::Authorize::AuthorizeResource` concern in the
+mutation.
+
+This allows us to provide the required abilities on the mutation like
+this:
+
+```ruby
+module Mutations
+ module MergeRequests
+ class SetWip < Base
+ graphql_name 'MergeRequestSetWip'
+
+ authorize :update_merge_request
+ end
+ end
+end
+```
+
+We can then call `authorize!` in the `resolve` method, passing in the resource we
+want to validate the abilities for.
+
+Alternatively, we can add a `find_object` method that will load the
+object on the mutation. This would allow you to use the
+`authorized_find!` and `authorized_find!` helper methods.
+
+When a user is not allowed to perform the action, or an object is not
+found, we should raise a
+`Gitlab::Graphql::Errors::ResourceNotAvailable` error. Which will be
+correctly rendered to the clients.
+
## Testing
_full stack_ tests for a graphql query or mutation live in
@@ -212,3 +354,35 @@ be used to test if the query renders valid results.
Using the `GraphqlHelpers#all_graphql_fields_for`-helper, a query
including all available fields can be constructed. This makes it easy
to add a test rendering all possible fields for a query.
+
+To test GraphQL mutation requests, `GraphqlHelpers` provides 2
+helpers: `graphql_mutation` which takes the name of the mutation, and
+a hash with the input for the mutation. This will return a struct with
+a mutation query, and prepared variables.
+
+This struct can then be passed to the `post_graphql_mutation` helper,
+that will post the request with the correct params, like a GraphQL
+client would do.
+
+To access the response of a mutation, the `graphql_mutation_response`
+helper is available.
+
+Using these helpers, we can build specs like this:
+
+```ruby
+let(:mutation) do
+ graphql_mutation(
+ :merge_request_set_wip,
+ project_path: 'gitlab-org/gitlab-ce',
+ iid: '1',
+ wip: true
+ )
+end
+
+it 'returns a successfull response' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_mutation_response(:merge_request_set_wip)['errors']).to be_empty
+end
+```
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index a50ea0b52aa..fed8846e505 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -10,6 +10,31 @@ module API
def authorize_admin_source!(source_type, source)
authorize! :"admin_#{source_type}", source
end
+
+ def find_all_members(source_type, source)
+ members = source_type == 'project' ? find_all_members_for_project(source) : find_all_members_for_group(source)
+ members.non_invite
+ .non_request
+ end
+
+ def find_all_members_for_project(project)
+ shared_group_ids = project.project_group_links.pluck(:group_id)
+ project_group_ids = project.group&.self_and_ancestors&.pluck(:id)
+ source_ids = [project.id, project_group_ids, shared_group_ids]
+ .flatten
+ .compact
+ Member.includes(:user)
+ .joins(user: :project_authorizations)
+ .where(project_authorizations: { project_id: project.id })
+ .where(source_id: source_ids)
+ end
+
+ def find_all_members_for_group(group)
+ source_ids = group.self_and_ancestors.pluck(:id)
+ Member.includes(:user)
+ .where(source_id: source_ids)
+ .where(source_type: 'Namespace')
+ end
end
end
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 8b12986d09e..3d2220fed96 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -28,6 +28,23 @@ module API
present members, with: Entities::Member
end
+ desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do
+ success Entities::Member
+ end
+ params do
+ optional :query, type: String, desc: 'A query string to search for members'
+ use :pagination
+ end
+ get ":id/members/all" do
+ source = find_source(source_type, params[:id])
+
+ members = find_all_members(source_type, source)
+ members = members.includes(:user).references(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = paginate(members)
+
+ present members, with: Entities::Member
+ end
+
desc 'Gets a member of a group or project.' do
success Entities::Member
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index d0cc0945a5f..06c034444a1 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -108,8 +108,7 @@ module API
if result.valid?
if result.build
- Gitlab::Metrics.add_event(:build_found,
- project: result.build.project.full_path)
+ Gitlab::Metrics.add_event(:build_found)
present result.build, with: Entities::JobRequest::Response
else
Gitlab::Metrics.add_event(:build_not_found)
@@ -140,8 +139,7 @@ module API
job.trace.set(params[:trace]) if params[:trace]
- Gitlab::Metrics.add_event(:update_build,
- project: job.project.full_path)
+ Gitlab::Metrics.add_event(:update_build)
case params[:state].to_s
when 'running'
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index 764f93f6d3d..fc8615afcae 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -36,10 +36,6 @@ module Gitlab
@project ||= Project.find_by_full_path(project_path)
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def create_issue
diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb
index 2f864f2082b..2316e58c3fc 100644
--- a/lib/gitlab/email/handler/create_merge_request_handler.rb
+++ b/lib/gitlab/email/handler/create_merge_request_handler.rb
@@ -40,10 +40,6 @@ module Gitlab
@project ||= Project.find_by_full_path(project_path)
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def create_merge_request
diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb
index 5791dbd0484..379b114e957 100644
--- a/lib/gitlab/email/handler/create_note_handler.rb
+++ b/lib/gitlab/email/handler/create_note_handler.rb
@@ -28,10 +28,6 @@ module Gitlab
record_name: 'comment')
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def author
diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb
index ea80e21532e..56751e4e41e 100644
--- a/lib/gitlab/email/handler/unsubscribe_handler.rb
+++ b/lib/gitlab/email/handler/unsubscribe_handler.rb
@@ -20,10 +20,6 @@ module Gitlab
noteable.unsubscribe(sent_notification.recipient)
end
- def metrics_params
- super.merge(project: project&.full_path)
- end
-
private
def sent_notification
diff --git a/lib/gitlab/git/conflict/resolver.rb b/lib/gitlab/git/conflict/resolver.rb
index 0e4a973301f..6dc792c16b8 100644
--- a/lib/gitlab/git/conflict/resolver.rb
+++ b/lib/gitlab/git/conflict/resolver.rb
@@ -17,7 +17,7 @@ module Gitlab
end
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message)
- rescue Rugged::ReferenceError, Rugged::OdbError, GRPC::BadStatus => e
+ rescue GRPC::BadStatus => e
raise Gitlab::Git::CommandError.new(e)
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 0356e8efc5c..eb02c7ac8e1 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -558,7 +558,9 @@ module Gitlab
if is_enabled
gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev)
else
- OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
+ end
end
end
end
@@ -832,18 +834,9 @@ module Gitlab
Gitlab::Git.check_namespace!(source_repository)
source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
- message, status = GitalyClient.migrate(:fetch_ref) do |is_enabled|
- if is_enabled
- gitaly_fetch_ref(source_repository, source_ref: source_ref, target_ref: target_ref)
- else
- # When removing this code, also remove source_repository#path
- # to remove deprecated method calls
- local_fetch_ref(source_repository.path, source_ref: source_ref, target_ref: target_ref)
- end
- end
-
- # Make sure ref was created, and raise Rugged::ReferenceError when not
- raise Rugged::ReferenceError, message if status != 0
+ args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
+ message, status = run_git(args, env: source_repository.fetch_env)
+ raise Gitlab::Git::CommandError, message if status != 0
target_ref
end
@@ -1244,17 +1237,6 @@ module Gitlab
gitaly_repository_client.apply_gitattributes(revision)
end
- def local_fetch_ref(source_path, source_ref:, target_ref:)
- args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
- run_git(args)
- end
-
- def gitaly_fetch_ref(source_repository, source_ref:, target_ref:)
- args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
-
- run_git(args, env: source_repository.fetch_env)
- end
-
def gitaly_delete_refs(*ref_names)
gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
end
diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb
index 8835bfb2481..65eb5cc18cf 100644
--- a/lib/gitlab/git/repository_mirroring.rb
+++ b/lib/gitlab/git/repository_mirroring.rb
@@ -6,7 +6,9 @@ module Gitlab
if is_enabled
gitaly_ref_client.remote_branches(remote_name)
else
- rugged_remote_branches(remote_name)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ rugged_remote_branches(remote_name)
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb
index e70361c163b..a52866c4b08 100644
--- a/lib/gitlab/github_import/importer/pull_requests_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb
@@ -43,7 +43,7 @@ module Gitlab
Rails.logger
.info("GitHub importer finished updating repository for #{pname}")
- repository_updates_counter.increment(project: pname)
+ repository_updates_counter.increment
end
def update_repository?(pr)
diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb
index 04f25c53e49..93a903915b0 100644
--- a/lib/gitlab/graphql/authorize.rb
+++ b/lib/gitlab/graphql/authorize.rb
@@ -10,7 +10,14 @@ module Gitlab
end
def required_permissions
- @required_permissions ||= []
+ # If the `#authorize` call is used on multiple classes, we add the
+ # permissions specified on a subclass, to the ones that were specified
+ # on it's superclass.
+ @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions)
+ superclass.required_permissions.dup
+ else
+ []
+ end
end
def authorize(*permissions)
diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb
new file mode 100644
index 00000000000..40895686a8a
--- /dev/null
+++ b/lib/gitlab/graphql/authorize/authorize_resource.rb
@@ -0,0 +1,46 @@
+module Gitlab
+ module Graphql
+ module Authorize
+ module AuthorizeResource
+ extend ActiveSupport::Concern
+
+ included do
+ extend Gitlab::Graphql::Authorize
+ end
+
+ def find_object(*args)
+ raise NotImplementedError, "Implement #find_object in #{self.class.name}"
+ end
+
+ def authorized_find(*args)
+ object = find_object(*args)
+
+ object if authorized?(object)
+ end
+
+ def authorized_find!(*args)
+ object = find_object(*args)
+ authorize!(object)
+
+ object
+ end
+
+ def authorize!(object)
+ unless authorized?(object)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable,
+ "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ end
+ end
+
+ def authorized?(object)
+ self.class.required_permissions.all? do |ability|
+ # The actions could be performed across multiple objects. In which
+ # case the current user is common, and we could benefit from the
+ # caching in `DeclarativePolicy`.
+ Ability.allowed?(current_user, ability, object, scope: :user)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb
index 1d8e8307ab9..f8c7ec24be1 100644
--- a/lib/gitlab/graphql/errors.rb
+++ b/lib/gitlab/graphql/errors.rb
@@ -3,6 +3,7 @@ module Gitlab
module Errors
BaseError = Class.new(GraphQL::ExecutionError)
ArgumentError = Class.new(BaseError)
+ ResourceNotAvailable = Class.new(BaseError)
end
end
end
diff --git a/lib/gitlab/graphql/mount_mutation.rb b/lib/gitlab/graphql/mount_mutation.rb
new file mode 100644
index 00000000000..8cab84d7a5f
--- /dev/null
+++ b/lib/gitlab/graphql/mount_mutation.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module MountMutation
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def mount_mutation(mutation_class)
+ # Using an underscored field name symbol will make `graphql-ruby`
+ # standardize the field name
+ field mutation_class.graphql_name.underscore.to_sym,
+ mutation: mutation_class
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index d0527f014a7..62a1833b39c 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -27,7 +27,11 @@ module Gitlab
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295
def fetch_ref
- @project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch)
+ target_ref = Gitlab::Git::BRANCH_REF_PREFIX + @merge_request.source_branch
+
+ unless @project.repository.fetch_source_branch!(@project.repository, @diff_head_sha, target_ref)
+ Rails.logger.warn("Import/Export warning: Failed to create #{target_ref} for MR: #{@merge_request.iid}")
+ end
end
def branch_exists?(branch_name)
diff --git a/lib/gitlab/kubernetes/config_map.rb b/lib/gitlab/kubernetes/config_map.rb
index 95e1054919d..8a8a59a9cd4 100644
--- a/lib/gitlab/kubernetes/config_map.rb
+++ b/lib/gitlab/kubernetes/config_map.rb
@@ -1,7 +1,7 @@
module Gitlab
module Kubernetes
class ConfigMap
- def initialize(name, values)
+ def initialize(name, values = "")
@name = name
@values = values
end
@@ -13,6 +13,10 @@ module Gitlab
resource
end
+ def config_map_name
+ "values-content-configuration-#{name}"
+ end
+
private
attr_reader :name, :values
@@ -25,10 +29,6 @@ module Gitlab
}
end
- def config_map_name
- "values-content-configuration-#{name}"
- end
-
def namespace
Gitlab::Kubernetes::Helm::NAMESPACE
end
diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb
index 2edd34109ba..c4de9a398cc 100644
--- a/lib/gitlab/kubernetes/helm/api.rb
+++ b/lib/gitlab/kubernetes/helm/api.rb
@@ -8,9 +8,9 @@ module Gitlab
end
def install(command)
- @namespace.ensure_exists!
+ namespace.ensure_exists!
create_config_map(command) if command.config_map?
- @kubeclient.create_pod(command.pod_resource)
+ kubeclient.create_pod(command.pod_resource)
end
##
@@ -20,23 +20,25 @@ module Gitlab
#
# values: "Pending", "Running", "Succeeded", "Failed", "Unknown"
#
- def installation_status(pod_name)
- @kubeclient.get_pod(pod_name, @namespace.name).status.phase
+ def status(pod_name)
+ kubeclient.get_pod(pod_name, namespace.name).status.phase
end
- def installation_log(pod_name)
- @kubeclient.get_pod_log(pod_name, @namespace.name).body
+ def log(pod_name)
+ kubeclient.get_pod_log(pod_name, namespace.name).body
end
- def delete_installation_pod!(pod_name)
- @kubeclient.delete_pod(pod_name, @namespace.name)
+ def delete_pod!(pod_name)
+ kubeclient.delete_pod(pod_name, namespace.name)
end
private
+ attr_reader :kubeclient, :namespace
+
def create_config_map(command)
command.config_map_resource.tap do |config_map_resource|
- @kubeclient.create_config_map(config_map_resource)
+ kubeclient.create_config_map(config_map_resource)
end
end
end
diff --git a/lib/gitlab/prometheus/metric.rb b/lib/gitlab/prometheus/metric.rb
index f54b2c6aaff..13cc59df795 100644
--- a/lib/gitlab/prometheus/metric.rb
+++ b/lib/gitlab/prometheus/metric.rb
@@ -3,7 +3,7 @@ module Gitlab
class Metric
include ActiveModel::Model
- attr_accessor :title, :required_metrics, :weight, :y_label, :queries
+ attr_accessor :id, :title, :required_metrics, :weight, :y_label, :queries
validates :title, :required_metrics, :weight, :y_label, :queries, presence: true
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
index e677ec84cd4..8534afcc849 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
@@ -8,6 +8,7 @@ module Gitlab
Deployment.find_by(id: deployment_id).try do |deployment|
query_metrics(
deployment.project,
+ deployment.environment,
common_query_context(
deployment.environment,
timeframe_start: (deployment.created_at - 30.minutes).to_f,
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
index 9273e69e158..e3af217b202 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
@@ -8,6 +8,7 @@ module Gitlab
::Environment.find_by(id: environment_id).try do |environment|
query_metrics(
environment.project,
+ environment,
common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f)
)
end
diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
index f5879de1e94..3be35f189d0 100644
--- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb
+++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
@@ -2,7 +2,7 @@ module Gitlab
module Prometheus
module Queries
module QueryAdditionalMetrics
- def query_metrics(project, query_context)
+ def query_metrics(project, environment, query_context)
matched_metrics(project).map(&query_group(query_context))
.select(&method(:group_with_any_metrics))
end
@@ -14,12 +14,16 @@ module Gitlab
lambda do |group|
metrics = group.metrics.map do |metric|
- {
+ metric_hsh = {
title: metric.title,
weight: metric.weight,
y_label: metric.y_label,
queries: metric.queries.map(&query_processor).select(&method(:query_with_result))
}
+
+ metric_hsh[:id] = metric.id if metric.id
+
+ metric_hsh
end
{
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a313713a9a8..372032ef4a1 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -120,6 +120,11 @@ msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts
msgstr[0] ""
msgstr[1] ""
+msgid "%{text} %{files}"
+msgid_plural "%{text} %{files} files"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{text} is available"
msgstr ""
@@ -1993,6 +1998,9 @@ msgstr ""
msgid "Delete list"
msgstr ""
+msgid "Deleted"
+msgstr ""
+
msgid "Deny"
msgstr ""
@@ -5547,10 +5555,8 @@ msgstr ""
msgid "Up to date"
msgstr ""
-msgid "Update %{files}"
-msgid_plural "Update %{files} files"
-msgstr[0] ""
-msgstr[1] ""
+msgid "Update"
+msgstr ""
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
@@ -6011,9 +6017,6 @@ msgstr ""
msgid "branch name"
msgstr ""
-msgid "ciReport|Show complete code vulnerabilities report"
-msgstr ""
-
msgid "command line instructions"
msgstr ""
diff --git a/package.json b/package.json
index 256ebc1fb6e..2a8761e32d6 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,8 @@
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
"dependencies": {
- "@gitlab-org/gitlab-svgs": "^1.26.0",
+ "@gitlab-org/gitlab-svgs": "^1.27.0",
+ "@gitlab-org/gitlab-ui": "1.0.5",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.3",
@@ -37,6 +38,7 @@
"core-js": "^2.4.1",
"cropper": "^2.3.0",
"css-loader": "^1.0.0",
+ "d3": "4.12.2",
"d3-array": "^1.2.1",
"d3-axis": "^1.0.8",
"d3-brush": "^1.0.4",
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 0c8eca5229e..4c64270ce92 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -72,6 +72,7 @@ module QA
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
+ clear_local_storage: true,
desired_capabilities: capabilities,
options: options
)
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index d44048fdf55..a43bdd3ea80 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -9,6 +9,18 @@ describe Projects::CommitsController do
project.add_maintainer(user)
end
+ describe "GET commits_root" do
+ context "no ref is provided" do
+ it 'should redirect to the default branch of the project' do
+ get(:commits_root,
+ namespace_id: project.namespace,
+ project_id: project)
+
+ expect(response).to redirect_to project_commits_path(project)
+ end
+ end
+ end
+
describe "GET show" do
render_views
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 273702e6d21..e03d23bcdf6 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -143,6 +143,14 @@ describe Projects::LabelsController do
expect(GroupLabel.find_by(title: promoted_label_name)).not_to be_nil
end
+ it 'renders label name without parsing it as HTML' do
+ label_1.update!(name: 'CCC&lt;img src=x onerror=alert(document.domain)&gt;')
+
+ post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param
+
+ expect(flash[:notice]).to eq("CCC&lt;img src=x onerror=alert(document.domain)&gt; promoted to <a href=\"#{group_labels_path(project.group)}\"><u>group label</u></a>.")
+ end
+
context 'service raising InvalidRecord' do
before do
expect_any_instance_of(Labels::PromoteService).to receive(:execute) do |label|
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index ea906cf7f32..6c2d1c7e92b 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -127,6 +127,14 @@ describe Projects::MilestonesController do
expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
expect(response).to redirect_to(project_milestones_path(project))
end
+
+ it 'renders milestone name without parsing it as HTML' do
+ milestone.update!(name: 'CCC&lt;img src=x onerror=alert(document.domain)&gt;')
+
+ post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
+
+ expect(flash[:notice]).to eq("CCC promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.")
+ end
end
context 'promotion fails' do
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 290fcd4f8e6..d89716b1b50 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -55,10 +55,8 @@ describe Projects::PipelinesController do
stub_feature_flags(ci_pipeline_persisted_stages: false)
end
- it 'returns JSON with serialized pipelines', :request_store do
- queries = ActiveRecord::QueryRecorder.new do
- get_pipelines_index_json
- end
+ it 'returns JSON with serialized pipelines' do
+ get_pipelines_index_json
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('pipeline')
@@ -73,8 +71,14 @@ describe Projects::PipelinesController do
json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages|
expect(stages.count).to eq 3
end
+ end
+
+ it 'does not execute N+1 queries' do
+ queries = ActiveRecord::QueryRecorder.new do
+ get_pipelines_index_json
+ end
- expect(queries.count).to be_within(5).of(30)
+ expect(queries.count).to be <= 36
end
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index fec1bea2751..1215b04913e 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -34,7 +34,7 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
- project.project_feature.update_columns(
+ project.project_feature.update(
wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level,
diff --git a/spec/factories/site_statistics.rb b/spec/factories/site_statistics.rb
new file mode 100644
index 00000000000..dd8c795515a
--- /dev/null
+++ b/spec/factories/site_statistics.rb
@@ -0,0 +1,7 @@
+FactoryBot.define do
+ factory :site_statistics, class: 'SiteStatistic' do
+ id 1
+ repositories_count 999
+ wikis_count 555
+ end
+end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 35b1c46ecf6..83293c0ca7d 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -135,6 +135,20 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
+ context 'sidebar' do
+ let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') }
+
+ before do
+ visit project_job_path(project, job)
+ end
+
+ it 'renders escaped tooltip name' do
+ page.within('aside.right-sidebar') do
+ expect(find('.active.build-job a')['data-title']).to eq('<img src="x"> - passed')
+ end
+ end
+ end
+
context 'when job is not running', :js do
let(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }
diff --git a/spec/features/users/add_email_to_existing_account.rb b/spec/features/users/add_email_to_existing_account.rb
new file mode 100644
index 00000000000..4355f769429
--- /dev/null
+++ b/spec/features/users/add_email_to_existing_account.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe 'AdditionalEmailToExistingAccount' do
+ describe 'add secondary email associated with account' do
+ let(:user) { create(:user) }
+
+ it 'verifies confirmation of additional email' do
+ sign_in(user)
+
+ email = create(:email, user: user)
+ visit email_confirmation_path(confirmation_token: email.confirmation_token)
+ expect(page).to have_content 'Your email address has been successfully confirmed.'
+ end
+ end
+end
diff --git a/spec/fixtures/emails/commands_in_reply.eml b/spec/fixtures/emails/commands_in_reply.eml
index 712f6f797b4..6a467ccbbc6 100644
--- a/spec/fixtures/emails/commands_in_reply.eml
+++ b/spec/fixtures/emails/commands_in_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/commands_only_reply.eml b/spec/fixtures/emails/commands_only_reply.eml
index 2d2e2f94290..9b8d25c406e 100644
--- a/spec/fixtures/emails/commands_only_reply.eml
+++ b/spec/fixtures/emails/commands_only_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
index 39d5cefbc2a..609d9d9e93d 100644
--- a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
+++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml
index 6823db0cfc8..7be1ed5bf44 100644
--- a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml
+++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>,<exchange@microsoft.com>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>,<exchange@microsoft.com>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/update_commands_only_reply.eml b/spec/fixtures/emails/update_commands_only_reply.eml
index bb0d2b0e03a..927a62f6475 100644
--- a/spec/fixtures/emails/update_commands_only_reply.eml
+++ b/spec/fixtures/emails/update_commands_only_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml
index 980e10a8812..5fbeebdb6b0 100644
--- a/spec/fixtures/emails/valid_reply.eml
+++ b/spec/fixtures/emails/valid_reply.eml
@@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
In-Reply-To: <issue_1@localhost>
-References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index 515bbe78cb7..b9ddb427e85 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -18,8 +18,6 @@ describe GitlabSchema do
end
it 'has the base mutation' do
- pending('Adding an empty mutation breaks the documentation explorer')
-
expect(described_class.mutation).to eq(::Types::MutationType.to_graphql)
end
diff --git a/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
new file mode 100644
index 00000000000..19f5a8907a2
--- /dev/null
+++ b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Mutations::ResolvesProject do
+ let(:mutation_class) do
+ Class.new(Mutations::BaseMutation) do
+ include Mutations::ResolvesProject
+ end
+ end
+
+ let(:context) { double }
+ subject(:mutation) { mutation_class.new(object: nil, context: context) }
+
+ it 'uses the ProjectsResolver to resolve projects by path' do
+ project = create(:project)
+
+ expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original
+ expect(mutation.resolve_project(full_path: project.full_path)).to eq(project)
+ end
+end
diff --git a/spec/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/graphql/mutations/merge_requests/set_wip_spec.rb
new file mode 100644
index 00000000000..e600abf3941
--- /dev/null
+++ b/spec/graphql/mutations/merge_requests/set_wip_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Mutations::MergeRequests::SetWip do
+ let(:merge_request) { create(:merge_request) }
+ let(:user) { create(:user) }
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
+
+ describe '#resolve' do
+ let(:wip) { true }
+ let(:mutated_merge_request) { subject[:merge_request] }
+ subject { mutation.resolve(project_path: merge_request.project.full_path, iid: merge_request.iid, wip: wip) }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when the user can update the merge request' do
+ before do
+ merge_request.project.add_developer(user)
+ end
+
+ it 'returns the merge request as a wip' do
+ expect(mutated_merge_request).to eq(merge_request)
+ expect(mutated_merge_request).to be_work_in_progress
+ expect(subject[:errors]).to be_empty
+ end
+
+ it 'returns errors merge request could not be updated' do
+ # Make the merge request invalid
+ merge_request.allow_broken = true
+ merge_request.update!(source_project: nil)
+
+ expect(subject[:errors]).not_to be_empty
+ end
+
+ context 'when passing wip as false' do
+ let(:wip) { false }
+
+ it 'removes `wip` from the title' do
+ merge_request.update(title: "WIP: working on it")
+
+ expect(mutated_merge_request).not_to be_work_in_progress
+ end
+
+ it 'does not do anything if the title did not start with wip' do
+ expect(mutated_merge_request).not_to be_work_in_progress
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/types/mutation_type_spec.rb b/spec/graphql/types/mutation_type_spec.rb
new file mode 100644
index 00000000000..a67d83b1edf
--- /dev/null
+++ b/spec/graphql/types/mutation_type_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Types::MutationType do
+ it 'is expected to have the MergeRequestSetWip' do
+ expect(described_class).to have_graphql_mutation(Mutations::MergeRequests::SetWip)
+ end
+end
diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb
index 0323ffb641c..ce5e037f88d 100644
--- a/spec/helpers/snippets_helper_spec.rb
+++ b/spec/helpers/snippets_helper_spec.rb
@@ -7,13 +7,13 @@ describe SnippetsHelper do
it 'gives view raw button of embedded snippets for project snippets' do
@snippet = create(:project_snippet, :public)
- expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc_code')}</a>")
+ expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc-code')}</a>")
end
it 'gives view raw button of embedded snippets for personal snippets' do
@snippet = create(:personal_snippet, :public)
- expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc_code')}</a>")
+ expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc-code')}</a>")
end
end
diff --git a/spec/javascripts/fixtures/graph.html.haml b/spec/javascripts/fixtures/graph.html.haml
deleted file mode 100644
index 4fedb0f1ded..00000000000
--- a/spec/javascripts/fixtures/graph.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-#js-pipeline-graph-vue{ data: { endpoint: "foo" } }
diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js
index 5ba17ecf5b5..1057f0aca3e 100644
--- a/spec/javascripts/helpers/vue_mount_component_helper.js
+++ b/spec/javascripts/helpers/vue_mount_component_helper.js
@@ -15,4 +15,14 @@ export const mountComponentWithStore = (Component, { el, props, store }) =>
propsData: props || {},
}).$mount(el);
+export const mountComponentWithSlots = (Component, { props, slots }) => {
+ const component = new Component({
+ propsData: props || {},
+ });
+
+ component.$slots = slots;
+
+ return component.$mount();
+};
+
export default mountComponent;
diff --git a/spec/javascripts/ide/components/changed_file_icon_spec.js b/spec/javascripts/ide/components/changed_file_icon_spec.js
index 541864e912e..7308219f705 100644
--- a/spec/javascripts/ide/components/changed_file_icon_spec.js
+++ b/spec/javascripts/ide/components/changed_file_icon_spec.js
@@ -33,14 +33,14 @@ describe('IDE changed file icon', () => {
});
describe('changedIconClass', () => {
- it('includes multi-file-modified when not a temp file', () => {
- expect(vm.changedIconClass).toContain('multi-file-modified');
+ it('includes ide-file-modified when not a temp file', () => {
+ expect(vm.changedIconClass).toContain('ide-file-modified');
});
- it('includes multi-file-addition when a temp file', () => {
+ it('includes ide-file-addition when a temp file', () => {
vm.file.tempFile = true;
- expect(vm.changedIconClass).toContain('multi-file-addition');
+ expect(vm.changedIconClass).toContain('ide-file-addition');
});
});
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js
index 27f10caccb1..3a5d6c8a90b 100644
--- a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js
@@ -46,4 +46,12 @@ describe('IDE commit sidebar actions', () => {
done();
});
});
+
+ describe('commitToCurrentBranchText', () => {
+ it('escapes current branch', () => {
+ vm.$store.state.currentBranchId = '<img src="x" />';
+
+ expect(vm.commitToCurrentBranchText).not.toContain('<img src="x" />');
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index bf96170f703..41d8bfff7e7 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -76,17 +76,29 @@ describe('Multi-file editor commit sidebar list item', () => {
expect(vm.iconName).toBe('file-addition');
});
+
+ it('returns deletion', () => {
+ f.deleted = true;
+
+ expect(vm.iconName).toBe('file-deletion');
+ });
});
describe('iconClass', () => {
it('returns modified when not a tempFile', () => {
- expect(vm.iconClass).toContain('multi-file-modified');
+ expect(vm.iconClass).toContain('ide-file-modified');
});
it('returns addition when not a tempFile', () => {
f.tempFile = true;
- expect(vm.iconClass).toContain('multi-file-addition');
+ expect(vm.iconClass).toContain('ide-file-addition');
+ });
+
+ it('returns deletion', () => {
+ f.deleted = true;
+
+ expect(vm.iconClass).toContain('ide-file-deletion');
});
});
});
diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js
index 4d704b80209..092c405a70b 100644
--- a/spec/javascripts/ide/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js
@@ -14,6 +14,7 @@ describe('new dropdown component', () => {
branch: 'master',
path: '',
mouseOver: false,
+ type: 'tree',
});
vm.$store.state.currentProjectId = 'abcproject';
@@ -67,4 +68,14 @@ describe('new dropdown component', () => {
});
});
});
+
+ describe('delete entry', () => {
+ it('calls delete action', () => {
+ spyOn(vm, 'deleteEntry');
+
+ vm.$el.querySelectorAll('.dropdown-menu button')[3].click();
+
+ expect(vm.deleteEntry).toHaveBeenCalledWith('');
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index 2256deb7dac..0e2e246defd 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
+import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
import repoEditor from '~/ide/components/repo_editor.vue';
@@ -25,6 +26,8 @@ describe('RepoEditor', () => {
vm.$store.state.openFiles.push(f);
Vue.set(vm.$store.state.entries, f.path, f);
+ spyOn(vm, 'getFileData').and.returnValue(Promise.resolve());
+
vm.$mount();
Vue.nextTick(() => setTimeout(done));
diff --git a/spec/javascripts/ide/components/repo_file_spec.js b/spec/javascripts/ide/components/repo_file_spec.js
index 156233653ab..f99d1f9890a 100644
--- a/spec/javascripts/ide/components/repo_file_spec.js
+++ b/spec/javascripts/ide/components/repo_file_spec.js
@@ -91,25 +91,6 @@ describe('RepoFile', () => {
done();
});
});
-
- it('disables action dropdown', done => {
- createComponent({
- file: {
- ...file('t4'),
- type: 'tree',
- branchId: 'master',
- projectId: 'project',
- },
- level: 0,
- disableActionDropdown: true,
- });
-
- setTimeout(() => {
- expect(vm.$el.querySelector('.ide-new-btn')).toBeNull();
-
- done();
- });
- });
});
describe('locked file', () => {
diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js
index fc0695a4263..278a0753322 100644
--- a/spec/javascripts/ide/components/repo_tab_spec.js
+++ b/spec/javascripts/ide/components/repo_tab_spec.js
@@ -93,13 +93,13 @@ describe('RepoTab', () => {
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.multi-file-modified')).toBeNull();
+ expect(vm.$el.querySelector('.ide-file-modified')).toBeNull();
vm.$el.dispatchEvent(new Event('mouseout'));
})
.then(Vue.nextTick)
.then(() => {
- expect(vm.$el.querySelector('.multi-file-modified')).not.toBeNull();
+ expect(vm.$el.querySelector('.ide-file-modified')).not.toBeNull();
done();
})
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index f570c0b16bd..72eb20bdc87 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -366,6 +366,23 @@ describe('IDE store file actions', () => {
});
});
+ describe('return JSON', () => {
+ beforeEach(() => {
+ mock.onGet(/(.*)/).replyOnce(200, JSON.stringify({ test: '123' }));
+ });
+
+ it('does not parse returned JSON', done => {
+ store
+ .dispatch('getRawFileData', { path: tmpFile.path })
+ .then(() => {
+ expect(tmpFile.raw).toEqual('{"test":"123"}');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
describe('error', () => {
beforeEach(() => {
mock.onGet(/(.*)/).networkError();
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 8b665a6d79e..792a716565c 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -7,6 +7,7 @@ import actions, {
updateActivityBarView,
updateTempFlagForEntry,
setErrorMessage,
+ deleteEntry,
} from '~/ide/stores/actions';
import store from '~/ide/stores';
import * as types from '~/ide/stores/mutation_types';
@@ -457,4 +458,19 @@ describe('Multi-file store actions', () => {
);
});
});
+
+ describe('deleteEntry', () => {
+ it('commits entry deletion', done => {
+ store.state.entries.path = 'testing';
+
+ testAction(
+ deleteEntry,
+ 'path',
+ store.state,
+ [{ type: types.DELETE_ENTRY, payload: 'path' }],
+ [{ type: 'burstUnusedSeal' }, { type: 'closeFile', payload: store.state.entries.path }],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/modules/commit/getters_spec.js b/spec/javascripts/ide/stores/modules/commit/getters_spec.js
index 44c941d6dbb..3f4bf407a1f 100644
--- a/spec/javascripts/ide/stores/modules/commit/getters_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/getters_spec.js
@@ -123,6 +123,22 @@ describe('IDE commit module getters', () => {
'Update test-file, index.js files',
);
});
+
+ it('returns commitMessage with deleted files', () => {
+ rootState[key].push(
+ {
+ path: 'test-file',
+ deleted: true,
+ },
+ {
+ path: 'index.js',
+ },
+ );
+
+ expect(getters.preBuiltCommitMessage(state, null, rootState)).toBe(
+ 'Update index.js\nDeleted test-file',
+ );
+ });
});
});
});
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index 52f83be8e8c..efd0d86552b 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -94,6 +94,35 @@ describe('IDE store file mutations', () => {
expect(localFile.raw).toBe('testing');
});
+
+ it('adds raw data to open pending file', () => {
+ localState.openFiles.push({
+ ...localFile,
+ pending: true,
+ });
+
+ mutations.SET_FILE_RAW_DATA(localState, {
+ file: localFile,
+ raw: 'testing',
+ });
+
+ expect(localState.openFiles[0].raw).toBe('testing');
+ });
+
+ it('does not add raw data to open pending tempFile file', () => {
+ localState.openFiles.push({
+ ...localFile,
+ pending: true,
+ tempFile: true,
+ });
+
+ mutations.SET_FILE_RAW_DATA(localState, {
+ file: localFile,
+ raw: 'testing',
+ });
+
+ expect(localState.openFiles[0].raw).not.toBe('testing');
+ });
});
describe('SET_FILE_BASE_RAW_DATA', () => {
@@ -205,6 +234,11 @@ describe('IDE store file mutations', () => {
beforeEach(() => {
localFile.content = 'test';
localFile.changed = true;
+ localState.currentProjectId = 'gitlab-ce';
+ localState.currentBranchId = 'master';
+ localState.trees['gitlab-ce/master'] = {
+ tree: [],
+ };
});
it('resets content and changed', () => {
@@ -213,6 +247,36 @@ describe('IDE store file mutations', () => {
expect(localFile.content).toBe('');
expect(localFile.changed).toBeFalsy();
});
+
+ it('adds to root tree if deleted', () => {
+ localFile.deleted = true;
+
+ mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
+
+ expect(localState.trees['gitlab-ce/master'].tree).toEqual([
+ {
+ ...localFile,
+ deleted: false,
+ },
+ ]);
+ });
+
+ it('adds to parent tree if deleted', () => {
+ localFile.deleted = true;
+ localFile.parentPath = 'parentPath';
+ localState.entries.parentPath = {
+ tree: [],
+ };
+
+ mutations.DISCARD_FILE_CHANGES(localState, localFile.path);
+
+ expect(localState.entries.parentPath.tree).toEqual([
+ {
+ ...localFile,
+ deleted: false,
+ },
+ ]);
+ });
});
describe('ADD_FILE_TO_CHANGED', () => {
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 98016f593aa..8b5f2d0bdfa 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -156,4 +156,61 @@ describe('Multi-file store mutations', () => {
expect(localState.errorMessage).toBe('error');
});
});
+
+ describe('DELETE_ENTRY', () => {
+ beforeEach(() => {
+ localState.currentProjectId = 'gitlab-ce';
+ localState.currentBranchId = 'master';
+ localState.trees['gitlab-ce/master'] = {
+ tree: [],
+ };
+ });
+
+ it('sets deleted flag', () => {
+ localState.entries.filePath = {
+ deleted: false,
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.entries.filePath.deleted).toBe(true);
+ });
+
+ it('removes from root tree', () => {
+ localState.entries.filePath = {
+ path: 'filePath',
+ deleted: false,
+ };
+ localState.trees['gitlab-ce/master'].tree.push(localState.entries.filePath);
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.trees['gitlab-ce/master'].tree).toEqual([]);
+ });
+
+ it('removes from parent tree', () => {
+ localState.entries.filePath = {
+ path: 'filePath',
+ deleted: false,
+ parentPath: 'parentPath',
+ };
+ localState.entries.parentPath = {
+ tree: [localState.entries.filePath],
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.entries.parentPath.tree).toEqual([]);
+ });
+
+ it('adds to changedFiles', () => {
+ localState.entries.filePath = {
+ deleted: false,
+ };
+
+ mutations.DELETE_ENTRY(localState, 'filePath');
+
+ expect(localState.changedFiles).toEqual([localState.entries.filePath]);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index 6c5980cfae4..89db50b8874 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -86,6 +86,11 @@ describe('Multi-file store utils', () => {
base64: true,
lastCommitSha: '123456789',
},
+ {
+ ...file('deletedFile'),
+ path: 'deletedFile',
+ deleted: true,
+ },
],
currentBranchId: 'master',
};
@@ -115,6 +120,13 @@ describe('Multi-file store utils', () => {
encoding: 'base64',
last_commit_id: '123456789',
},
+ {
+ action: 'delete',
+ file_path: 'deletedFile',
+ content: '',
+ encoding: 'text',
+ last_commit_id: undefined,
+ },
],
start_branch: undefined,
});
@@ -173,4 +185,65 @@ describe('Multi-file store utils', () => {
});
});
});
+
+ describe('commitActionForFile', () => {
+ it('returns deleted for deleted file', () => {
+ expect(utils.commitActionForFile({ deleted: true })).toBe('delete');
+ });
+
+ it('returns create for tempFile', () => {
+ expect(utils.commitActionForFile({ tempFile: true })).toBe('create');
+ });
+
+ it('returns update by default', () => {
+ expect(utils.commitActionForFile({})).toBe('update');
+ });
+ });
+
+ describe('getCommitFiles', () => {
+ it('returns flattened list of files and folders', () => {
+ const files = [
+ {
+ path: 'a',
+ type: 'blob',
+ deleted: true,
+ },
+ {
+ path: 'b',
+ type: 'tree',
+ deleted: true,
+ tree: [
+ {
+ path: 'c',
+ type: 'blob',
+ },
+ {
+ path: 'd',
+ type: 'blob',
+ },
+ ],
+ },
+ ];
+
+ const flattendFiles = utils.getCommitFiles(files);
+
+ expect(flattendFiles).toEqual([
+ {
+ path: 'a',
+ type: 'blob',
+ deleted: true,
+ },
+ {
+ path: 'c',
+ type: 'blob',
+ deleted: true,
+ },
+ {
+ path: 'd',
+ type: 'blob',
+ deleted: true,
+ },
+ ]);
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js
new file mode 100644
index 00000000000..608a0d4be67
--- /dev/null
+++ b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import component from '~/pipelines/components/graph/dropdown_job_component.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('dropdown job component', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const mock = {
+ jobs: [
+ {
+ id: 4256,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'icon_status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 4299,
+ name: 'test',
+ status: {
+ icon: 'icon_status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4299',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4299/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ name: 'rspec:linux',
+ size: 2,
+ status: {
+ icon: 'icon_status_success',
+ text: 'passed',
+ label: 'passed',
+ tooltip: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4256',
+ has_details: true,
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/root/ci-mock/builds/4256/retry',
+ method: 'post',
+ },
+ },
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, { job: mock });
+ });
+
+ it('renders button with job name and size', () => {
+ expect(vm.$el.querySelector('button').textContent).toContain(mock.name);
+ expect(vm.$el.querySelector('button').textContent).toContain(mock.size);
+ });
+
+ it('renders dropdown with jobs', () => {
+ expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length);
+ });
+
+ it('escapes tooltip title', () => {
+ expect(
+ vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'),
+ ).toEqual(
+ '&lt;img src=x onerror=alert(document.domain)&gt; - passed',
+ );
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js
index 713baa65a17..b6fa4272c8b 100644
--- a/spec/javascripts/pipelines/graph/graph_component_spec.js
+++ b/spec/javascripts/pipelines/graph/graph_component_spec.js
@@ -1,37 +1,33 @@
import Vue from 'vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import graphComponent from '~/pipelines/components/graph/graph_component.vue';
import graphJSON from './mock_data';
describe('graph component', () => {
- preloadFixtures('static/graph.html.raw');
+ const GraphComponent = Vue.extend(graphComponent);
+ let component;
- let GraphComponent;
-
- beforeEach(() => {
- loadFixtures('static/graph.html.raw');
- GraphComponent = Vue.extend(graphComponent);
+ afterEach(() => {
+ component.$destroy();
});
describe('while is loading', () => {
it('should render a loading icon', () => {
- const component = new GraphComponent({
- propsData: {
- isLoading: true,
- pipeline: {},
- },
- }).$mount('#js-pipeline-graph-vue');
+ component = mountComponent(GraphComponent, {
+ isLoading: true,
+ pipeline: {},
+ });
+
expect(component.$el.querySelector('.loading-icon')).toBeDefined();
});
});
describe('with data', () => {
it('should render the graph', () => {
- const component = new GraphComponent({
- propsData: {
- isLoading: false,
- pipeline: graphJSON,
- },
- }).$mount('#js-pipeline-graph-vue');
+ component = mountComponent(GraphComponent, {
+ isLoading: false,
+ pipeline: graphJSON,
+ });
expect(component.$el.classList.contains('js-pipeline-graph')).toEqual(true);
@@ -52,4 +48,15 @@ describe('graph component', () => {
expect(component.$el.querySelector('.stage-column-list')).toBeDefined();
});
});
+
+ describe('capitalizeStageName', () => {
+ it('capitalizes and escapes stage name', () => {
+ component = mountComponent(GraphComponent, {
+ isLoading: false,
+ pipeline: graphJSON,
+ });
+
+ expect(component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim()).toEqual('Deploy &lt;img src=x onerror=alert(document.domain)&gt;');
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index 56476253ad0..59f18d9397d 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -3,7 +3,7 @@ import jobComponent from '~/pipelines/components/graph/job_component.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('pipeline graph job component', () => {
- let JobComponent;
+ const JobComponent = Vue.extend(jobComponent);
let component;
const mockJob = {
@@ -26,10 +26,6 @@ describe('pipeline graph job component', () => {
},
};
- beforeEach(() => {
- JobComponent = Vue.extend(jobComponent);
- });
-
afterEach(() => {
component.$destroy();
});
@@ -165,4 +161,24 @@ describe('pipeline graph job component', () => {
expect(component.$el.querySelector(tooltipBoundary)).toBeNull();
});
});
+
+ describe('tooltipText', () => {
+ it('escapes job name', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'icon_status_success',
+ label: 'success',
+ tooltip: 'failed',
+ },
+ },
+ });
+
+ expect(
+ component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'),
+ ).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; - failed');
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js
index b2161d54bce..a4a5d78f906 100644
--- a/spec/javascripts/pipelines/graph/mock_data.js
+++ b/spec/javascripts/pipelines/graph/mock_data.js
@@ -91,7 +91,7 @@ export default {
dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test',
},
{
- name: 'deploy',
+ name: 'deploy <img src=x onerror=alert(document.domain)>',
title: 'deploy: passed',
groups: [
{
diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
index 9d1e71fd117..f6e6bd3132e 100644
--- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js
+++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
@@ -1,8 +1,11 @@
import Vue from 'vue';
import stageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('stage column component', () => {
let component;
+ const StageColumnComponent = Vue.extend(stageColumnComponent);
+
const mockJob = {
id: 4250,
name: 'test',
@@ -22,7 +25,6 @@ describe('stage column component', () => {
};
beforeEach(() => {
- const StageColumnComponent = Vue.extend(stageColumnComponent);
const mockJobs = [];
for (let i = 0; i < 3; i += 1) {
@@ -31,12 +33,10 @@ describe('stage column component', () => {
mockJobs.push(mockedJob);
}
- component = new StageColumnComponent({
- propsData: {
- title: 'foo',
- jobs: mockJobs,
- },
- }).$mount();
+ component = mountComponent(StageColumnComponent, {
+ title: 'foo',
+ jobs: mockJobs,
+ });
});
it('should render provided title', () => {
@@ -46,4 +46,27 @@ describe('stage column component', () => {
it('should render the provided jobs', () => {
expect(component.$el.querySelectorAll('.builds-container > ul > li').length).toEqual(3);
});
+
+ describe('jobId', () => {
+ it('escapes job name', () => {
+ component = mountComponent(StageColumnComponent, {
+ jobs: [
+ {
+ id: 4259,
+ name: '<img src=x onerror=alert(document.domain)>',
+ status: {
+ icon: 'icon_status_success',
+ label: 'success',
+ tooltip: '<img src=x onerror=alert(document.domain)>',
+ },
+ },
+ ],
+ title: 'test',
+ });
+
+ expect(
+ component.$el.querySelector('.builds-container li').getAttribute('id'),
+ ).toEqual('ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;');
+ });
+ });
});
diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
index b58de607ece..9dff52a9d49 100644
--- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
+++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
@@ -1,4 +1,3 @@
-import $ from 'jquery';
import Vue from 'vue';
import TimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
@@ -94,21 +93,14 @@ describe('Issuable Time Tracker', () => {
describe('Remaining meter', () => {
it('should display the remaining meter with the correct width', done => {
Vue.nextTick(() => {
- const meterWidth = vm.$el.querySelector('.time-tracking-comparison-pane .meter-fill')
- .style.width;
- const correctWidth = '5%';
-
- expect(meterWidth).toBe(correctWidth);
+ expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]')).not.toBeNull();
done();
});
});
it('should display the remaining meter with the correct background color when within estimate', done => {
Vue.nextTick(() => {
- const styledMeter = $(vm.$el).find(
- '.time-tracking-comparison-pane .within_estimate .meter-fill',
- );
- expect(styledMeter.length).toBe(1);
+ expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]')).not.toBeNull();
done();
});
});
@@ -117,10 +109,7 @@ describe('Issuable Time Tracker', () => {
vm.time_estimate = 100000;
vm.time_spent = 20000000;
Vue.nextTick(() => {
- const styledMeter = $(vm.$el).find(
- '.time-tracking-comparison-pane .over_estimate .meter-fill',
- );
- expect(styledMeter.length).toBe(1);
+ expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]')).not.toBeNull();
done();
});
});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index bc00fdfd73c..59e472789e2 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -91,6 +91,19 @@ beforeEach(() => {
Vue.http.interceptors = builtinVueHttpInterceptors.slice();
});
+let longRunningTestTimeoutHandle;
+
+beforeEach((done) => {
+ longRunningTestTimeoutHandle = setTimeout(() => {
+ done.fail('Test is running too long!');
+ }, 1000);
+ done();
+});
+
+afterEach(() => {
+ clearTimeout(longRunningTestTimeoutHandle);
+});
+
const axiosDefaultAdapter = getDefaultAdapter();
// render all of our tests
diff --git a/spec/javascripts/vue_shared/components/bar_chart_spec.js b/spec/javascripts/vue_shared/components/bar_chart_spec.js
new file mode 100644
index 00000000000..7e91cd6f63f
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/bar_chart_spec.js
@@ -0,0 +1,85 @@
+import Vue from 'vue';
+import BarChart from '~/vue_shared/components/bar_chart.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+function getRandomArbitrary(min, max) {
+ return Math.random() * (max - min) + min;
+}
+
+function generateRandomData(dataNumber) {
+ const randomGraphData = [];
+
+ for (let i = 1; i <= dataNumber; i += 1) {
+ randomGraphData.push({
+ name: `random ${i}`,
+ value: parseInt(getRandomArbitrary(1, 8), 10),
+ });
+ }
+
+ return randomGraphData;
+}
+
+describe('Bar chart component', () => {
+ let barChart;
+ const graphData = generateRandomData(10);
+
+ beforeEach(() => {
+ const BarChartComponent = Vue.extend(BarChart);
+
+ barChart = mountComponent(BarChartComponent, {
+ graphData,
+ yAxisLabel: 'data',
+ });
+ });
+
+ afterEach(() => {
+ barChart.$destroy();
+ });
+
+ it('calculates the padding for even distribution across bars', () => {
+ barChart.vbWidth = 1000;
+ const result = barChart.calculatePadding(30);
+
+ // since padding can't be higher than 1 and lower than 0
+ // for more info: https://github.com/d3/d3-scale#band-scales
+ expect(result).not.toBeLessThan(0);
+ expect(result).not.toBeGreaterThan(1);
+ });
+
+ it('formats the tooltip title', () => {
+ const tooltipTitle = barChart.setTooltipTitle(barChart.graphData[0]);
+
+ expect(tooltipTitle).toContain('random 1:');
+ });
+
+ it('has a translates the bar graphs on across the X axis', () => {
+ barChart.panX = 100;
+
+ expect(barChart.barTranslationTransform).toEqual('translate(100, 0)');
+ });
+
+ it('translates the scroll indicator to the far right side', () => {
+ barChart.vbWidth = 500;
+
+ expect(barChart.scrollIndicatorTransform).toEqual('translate(420, 0)');
+ });
+
+ it('translates the x-axis to the bottom of the viewbox and pan coordinates', () => {
+ barChart.panX = 100;
+ barChart.vbHeight = 250;
+
+ expect(barChart.xAxisLocation).toEqual('translate(100, 250)');
+ });
+
+ it('Contains a total of 4 ticks across the y axis', () => {
+ const ticks = barChart.$el.querySelector('.y-axis').querySelectorAll('.tick').length;
+
+ expect(ticks).toEqual(4);
+ });
+
+ it('rotates the x axis labels a total of 90 degress (CCW)', () => {
+ const xAxisLabel = barChart.$el.querySelector('.x-axis').querySelectorAll('text')[0];
+
+ expect(xAxisLabel.getAttribute('transform')).toEqual('rotate(-90)');
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
index 8efcb54659d..f1e62069462 100644
--- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -29,7 +29,7 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('dragHandle dragleft');
+ expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-left');
expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;');
});
@@ -40,7 +40,7 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('dragHandle dragright');
+ expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-right');
});
it('drag the resizer', () => {
diff --git a/spec/javascripts/vue_shared/components/reports/report_issues_spec.js b/spec/javascripts/vue_shared/components/reports/report_issues_spec.js
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/spec/javascripts/vue_shared/components/reports/report_issues_spec.js
+++ /dev/null
diff --git a/spec/javascripts/vue_shared/components/reports/report_section_spec.js b/spec/javascripts/vue_shared/components/reports/report_section_spec.js
index 07401181ffd..4e3986acb16 100644
--- a/spec/javascripts/vue_shared/components/reports/report_section_spec.js
+++ b/spec/javascripts/vue_shared/components/reports/report_section_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import reportSection from '~/vue_shared/components/reports/report_section.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper';
describe('Report section', () => {
let vm;
@@ -23,7 +23,7 @@ describe('Report section', () => {
describe('computed', () => {
beforeEach(() => {
vm = mountComponent(ReportSection, {
- type: 'codequality',
+ component: '',
status: 'SUCCESS',
loadingText: 'Loading codeclimate report',
errorText: 'foo',
@@ -89,7 +89,7 @@ describe('Report section', () => {
describe('when it is loading', () => {
it('should render loading indicator', () => {
vm = mountComponent(ReportSection, {
- type: 'codequality',
+ component: '',
status: 'LOADING',
loadingText: 'Loading codeclimate report',
errorText: 'foo',
@@ -103,7 +103,7 @@ describe('Report section', () => {
describe('with success status', () => {
beforeEach(() => {
vm = mountComponent(ReportSection, {
- type: 'codequality',
+ component: '',
status: 'SUCCESS',
loadingText: 'Loading codeclimate report',
errorText: 'foo',
@@ -161,7 +161,7 @@ describe('Report section', () => {
describe('with failed request', () => {
it('should render error indicator', () => {
vm = mountComponent(ReportSection, {
- type: 'codequality',
+ component: '',
status: 'ERROR',
loadingText: 'Loading codeclimate report',
errorText: 'Failed to load codeclimate report',
@@ -171,4 +171,27 @@ describe('Report section', () => {
expect(vm.$el.textContent.trim()).toEqual('Failed to load codeclimate report');
});
});
+
+ describe('with action buttons passed to the slot', () => {
+ beforeEach(() => {
+ vm = mountComponentWithSlots(ReportSection, {
+ props: {
+ status: 'SUCCESS',
+ successText: 'success',
+ hasIssues: true,
+ },
+ slots: {
+ actionButtons: ['Action!'],
+ },
+ });
+ });
+
+ it('should render the passed button', () => {
+ expect(vm.$el.textContent.trim()).toContain('Action!');
+ });
+
+ it('should still render the expand/collapse button', () => {
+ expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
+ });
+ });
});
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
index b2e544e6fed..c51985f00a2 100644
--- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
@@ -158,7 +158,6 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
expect(importer.repository_updates_counter)
.to receive(:increment)
- .with(project: project.path_with_namespace)
.and_call_original
Timecop.freeze do
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
new file mode 100644
index 00000000000..95bf7685ade
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize::AuthorizeResource do
+ let(:fake_class) do
+ Class.new do
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ attr_reader :user, :found_object
+
+ authorize :read_the_thing
+
+ def initialize(user, found_object)
+ @user, @found_object = user, found_object
+ end
+
+ def find_object
+ found_object
+ end
+
+ def current_user
+ user
+ end
+ end
+ end
+
+ let(:user) { build(:user) }
+ let(:project) { build(:project) }
+ subject(:loading_resource) { fake_class.new(user, project) }
+
+ context 'when the user is allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ true
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find).to eq(project)
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find!).to eq(project)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.not_to raise_error
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is true' do
+ expect(loading_resource.authorized?(project)).to be(true)
+ end
+ end
+ end
+
+ context 'when the user is not allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ false
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns `nil`' do
+ expect(loading_resource.authorized_find).to be_nil
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'raises an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is false' do
+ expect(loading_resource.authorized?(project)).to be(false)
+ end
+ end
+ end
+
+ context 'when the class does not define #find_object' do
+ let(:fake_class) do
+ Class.new { include Gitlab::Graphql::Authorize::AuthorizeResource }
+ end
+
+ it 'raises a comprehensive error message' do
+ expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb
new file mode 100644
index 00000000000..9c17a3b0e4b
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize do
+ describe '#authorize' do
+ it 'adds permissions from subclasses to those of superclasses when used on classes' do
+ base_class = Class.new do
+ extend Gitlab::Graphql::Authorize
+
+ authorize :base_authorization
+ end
+ sub_class = Class.new(base_class) do
+ authorize :sub_authorization
+ end
+
+ expect(base_class.required_permissions).to contain_exactly(:base_authorization)
+ expect(sub_class.required_permissions)
+ .to contain_exactly(:base_authorization, :sub_authorization)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index bac5693c830..a88ac0a091e 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
@shared = @project.import_export_shared
allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/')
- allow_any_instance_of(Repository).to receive(:fetch_ref).and_return(true)
+ allow_any_instance_of(Repository).to receive(:fetch_source_branch!).and_return(true)
allow_any_instance_of(Gitlab::Git::Repository).to receive(:branch_exists?).and_return(false)
expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA')
diff --git a/spec/lib/gitlab/kubernetes/config_map_spec.rb b/spec/lib/gitlab/kubernetes/config_map_spec.rb
index 33dfa461202..e253b291277 100644
--- a/spec/lib/gitlab/kubernetes/config_map_spec.rb
+++ b/spec/lib/gitlab/kubernetes/config_map_spec.rb
@@ -22,4 +22,10 @@ describe Gitlab::Kubernetes::ConfigMap do
is_expected.to eq(resource)
end
end
+
+ describe '#config_map_name' do
+ it 'returns the config_map name' do
+ expect(config_map.config_map_name).to eq("values-content-configuration-#{application.name}")
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index aa7e43dfb16..6e9b4ca0869 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -49,33 +49,33 @@ describe Gitlab::Kubernetes::Helm::Api do
end
end
- describe '#installation_status' do
+ describe '#status' do
let(:phase) { Gitlab::Kubernetes::Pod::RUNNING }
let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation
it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod)
- expect(subject.installation_status(command.pod_name)).to eq(phase)
+ expect(subject.status(command.pod_name)).to eq(phase)
end
end
- describe '#installation_log' do
+ describe '#log' do
let(:log) { 'some output' }
let(:response) { RestClient::Response.new(log) }
it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response)
- expect(subject.installation_log(command.pod_name)).to eq(log)
+ expect(subject.log(command.pod_name)).to eq(log)
end
end
- describe '#delete_installation_pod!' do
+ describe '#delete_pod!' do
it 'deletes the POD from kubernetes cluster' do
expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once
- subject.delete_installation_pod!(command.pod_name)
+ subject.delete_pod!(command.pod_name)
end
end
end
diff --git a/spec/migrations/generate_missing_routes_spec.rb b/spec/migrations/generate_missing_routes_spec.rb
new file mode 100644
index 00000000000..32515d353b0
--- /dev/null
+++ b/spec/migrations/generate_missing_routes_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180702134423_generate_missing_routes.rb')
+
+describe GenerateMissingRoutes, :migration do
+ describe '#up' do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:routes) { table(:routes) }
+
+ it 'creates routes for projects without a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ routes.create!(
+ path: 'gitlab',
+ source_type: 'Namespace',
+ source_id: namespace.id
+ )
+
+ project = projects.create!(
+ name: 'GitLab CE',
+ path: 'gitlab-ce',
+ namespace_id: namespace.id
+ )
+
+ described_class.new.up
+
+ route = routes.where(source_type: 'Project').take
+
+ expect(route.source_id).to eq(project.id)
+ expect(route.path).to eq("gitlab/gitlab-ce-#{project.id}")
+ end
+
+ it 'creates routes for namespaces without a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ described_class.new.up
+
+ route = routes.where(source_type: 'Namespace').take
+
+ expect(route.source_id).to eq(namespace.id)
+ expect(route.path).to eq("gitlab-#{namespace.id}")
+ end
+
+ it 'does not create routes for namespaces that already have a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ routes.create!(
+ path: 'gitlab',
+ source_type: 'Namespace',
+ source_id: namespace.id
+ )
+
+ described_class.new.up
+
+ expect(routes.count).to eq(1)
+ end
+
+ it 'does not create routes for projects that already have a route' do
+ namespace = namespaces.create!(name: 'GitLab', path: 'gitlab')
+
+ routes.create!(
+ path: 'gitlab',
+ source_type: 'Namespace',
+ source_id: namespace.id
+ )
+
+ project = projects.create!(
+ name: 'GitLab CE',
+ path: 'gitlab-ce',
+ namespace_id: namespace.id
+ )
+
+ routes.create!(
+ path: 'gitlab/gitlab-ce',
+ source_type: 'Project',
+ source_id: project.id
+ )
+
+ described_class.new.up
+
+ expect(routes.count).to eq(2)
+ end
+ end
+end
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index efd57040005..e4b61552033 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -34,6 +34,47 @@ describe Clusters::Applications::Prometheus do
end
end
+ describe '#ready' do
+ let(:project) { create(:project) }
+ let(:cluster) { create(:cluster, projects: [project]) }
+
+ it 'returns true when installed' do
+ application = build(:clusters_applications_prometheus, :installed, cluster: cluster)
+
+ expect(application).to be_ready
+ end
+
+ it 'returns false when not_installable' do
+ application = build(:clusters_applications_prometheus, :not_installable, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when installable' do
+ application = build(:clusters_applications_prometheus, :installable, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when scheduled' do
+ application = build(:clusters_applications_prometheus, :scheduled, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when installing' do
+ application = build(:clusters_applications_prometheus, :installing, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+
+ it 'returns false when errored' do
+ application = build(:clusters_applications_prometheus, :errored, cluster: cluster)
+
+ expect(application).not_to be_ready
+ end
+ end
+
describe '#prometheus_client' do
context 'cluster is nil' do
it 'returns nil' do
@@ -102,15 +143,17 @@ describe Clusters::Applications::Prometheus do
let(:kubeclient) { double('kubernetes client') }
let(:prometheus) { create(:clusters_applications_prometheus) }
- subject { prometheus.install_command }
-
- it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
+ it 'returns an instance of Gitlab::Kubernetes::Helm::InstallCommand' do
+ expect(prometheus.install_command).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
+ end
it 'should be initialized with 3 arguments' do
- expect(subject.name).to eq('prometheus')
- expect(subject.chart).to eq('stable/prometheus')
- expect(subject.version).to eq('6.7.3')
- expect(subject.values).to eq(prometheus.values)
+ command = prometheus.install_command
+
+ expect(command.name).to eq('prometheus')
+ expect(command.chart).to eq('stable/prometheus')
+ expect(command.version).to eq('6.7.3')
+ expect(command.values).to eq(prometheus.values)
end
end
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index 0f156619e9e..79f75c0ffa0 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -125,6 +125,13 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
go!
end
+ it "calls a reactive_cache_updated only once if content did not change on subsequent update" do
+ expect(instance).to receive(:calculate_reactive_cache).twice
+ expect(instance).to receive(:reactive_cache_updated).once
+
+ 2.times { instance.exclusively_update_reactive_cache! }
+ end
+
context 'and #calculate_reactive_cache raises an exception' do
before do
stub_reactive_cache(instance, "preexisting")
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index ed3e28fbeca..565266321d3 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -12,16 +12,6 @@ describe Group, 'Routable' do
it { is_expected.to have_many(:redirect_routes).dependent(:destroy) }
end
- describe 'GitLab read-only instance' do
- it 'does not save route if route is not present' do
- group.route.path = ''
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
- expect(group).to receive(:update_route).and_call_original
-
- expect { group.full_path }.to change { Route.count }.by(0)
- end
- end
-
describe 'Callbacks' do
it 'creates route record on create' do
expect(group.route.path).to eq(group.path)
@@ -131,29 +121,6 @@ describe Group, 'Routable' do
it { expect(group.full_path).to eq(group.path) }
it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") }
-
- context 'with RequestStore active', :request_store do
- it 'does not load the route table more than once' do
- group.expires_full_path_cache
- expect(group).to receive(:uncached_full_path).once.and_call_original
-
- 3.times { group.full_path }
- expect(group.full_path).to eq(group.path)
- end
- end
- end
-
- describe '#expires_full_path_cache' do
- context 'with RequestStore active', :request_store do
- it 'expires the full_path cache' do
- expect(group.full_path).to eq('foo')
-
- group.route.update(path: 'bar', name: 'bar')
- group.expires_full_path_cache
-
- expect(group.full_path).to eq('bar')
- end
- end
end
describe '#full_name' do
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index c1b385aaf76..9b7f932ec3a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -205,6 +205,34 @@ describe Namespace do
expect(gitlab_shell.exists?(project.repository_storage, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
+ context 'when #write_projects_repository_config raises an error' do
+ context 'in test environment' do
+ it 'raises an exception' do
+ expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
+
+ expect do
+ namespace.update(path: namespace.full_path + '_new')
+ end.to raise_error('foo')
+ end
+ end
+
+ context 'in production environment' do
+ it 'does not cancel later callbacks' do
+ expect(namespace).to receive(:write_projects_repository_config).and_raise('foo')
+ expect(namespace).to receive(:move_dir).and_wrap_original do |m, *args|
+ move_dir_result = m.call(*args)
+
+ expect(move_dir_result).to be_truthy # Must be truthy, or else later callbacks would be canceled
+
+ move_dir_result
+ end
+ expect(Gitlab::Sentry).to receive(:should_raise?).and_return(false) # like prod
+
+ namespace.update(path: namespace.full_path + '_new')
+ end
+ end
+ end
+
context 'with subgroups', :nested_groups do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
@@ -295,6 +323,16 @@ describe Namespace do
parent.update(path: 'mygroup_new')
+ # Routes are loaded when creating the projects, so we need to manually
+ # reload them for the below code to be aware of the above UPDATE.
+ [
+ project_in_parent_group,
+ hashed_project_in_subgroup,
+ legacy_project_in_subgroup
+ ].each do |project|
+ project.route.reload
+ end
+
expect(project_rugged(project_in_parent_group).config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
expect(project_rugged(hashed_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
expect(project_rugged(legacy_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index cd7f77024da..10617edec0f 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -119,4 +119,46 @@ describe ProjectFeature do
end
end
end
+
+ context 'Site Statistics' do
+ set(:project_with_wiki) { create(:project, :wiki_enabled) }
+ set(:project_without_wiki) { create(:project, :wiki_disabled) }
+
+ context 'when creating a project' do
+ it 'tracks wiki availability when wikis are enabled by default' do
+ expect { create(:project) }.to change { SiteStatistic.fetch.wikis_count }.by(1)
+ end
+
+ it 'does not track wiki availability when wikis are disabled by default' do
+ expect { create(:project, :wiki_disabled) }.not_to change { SiteStatistic.fetch.wikis_count }
+ end
+ end
+
+ context 'when updating a project_feature' do
+ it 'untracks wiki availability when disabling wiki access' do
+ expect { project_with_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) }
+ .to change { SiteStatistic.fetch.wikis_count }.by(-1)
+ end
+
+ it 'tracks again wiki availability when re-enabling wiki access as public' do
+ expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) }
+ .to change { SiteStatistic.fetch.wikis_count }.by(1)
+ end
+
+ it 'tracks again wiki availability when re-enabling wiki access as private' do
+ expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::PRIVATE) }
+ .to change { SiteStatistic.fetch.wikis_count }.by(1)
+ end
+ end
+
+ context 'when removing a project' do
+ it 'untracks wiki availability when removing a project with previous wiki access' do
+ expect { project_with_wiki.destroy }.to change { SiteStatistic.fetch.wikis_count }.by(-1)
+ end
+
+ it 'does not untrack wiki availability when removing a project without wiki access' do
+ expect { project_without_wiki.destroy }.not_to change { SiteStatistic.fetch.wikis_count }
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index b0ec725bf70..b75ca91b007 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -102,6 +102,22 @@ describe Project do
end
end
+ context 'Site Statistics' do
+ context 'when creating a new project' do
+ it 'tracks project in SiteStatistic' do
+ expect { create(:project) }.to change { SiteStatistic.fetch.repositories_count }.by(1)
+ end
+ end
+
+ context 'when deleting a project' do
+ it 'untracks project in SiteStatistic' do
+ project = create(:project)
+
+ expect { project.destroy }.to change { SiteStatistic.fetch.repositories_count }.by(-1)
+ end
+ end
+ end
+
context 'updating cd_cd_settings' do
it 'does not raise an error' do
project = create(:project)
@@ -2942,8 +2958,6 @@ describe Project do
expect(project).to receive(:expire_caches_before_rename)
- expect(project).to receive(:expires_full_path_cache)
-
project.rename_repo
end
@@ -3103,8 +3117,6 @@ describe Project do
expect(project).to receive(:expire_caches_before_rename)
- expect(project).to receive(:expires_full_path_cache)
-
project.rename_repo
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 5d64602ca56..52ec8dbe25a 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1129,16 +1129,12 @@ describe Repository do
end
it 'raises Rugged::ReferenceError' do
- raise_reference_error = raise_error(Rugged::ReferenceError) do |err|
- expect(err.cause).to be_nil
- end
-
expect do
Gitlab::Git::OperationService.new(git_user, target_project.repository.raw_repository)
.with_branch('feature',
start_repository: project.repository.raw_repository,
&:itself)
- end.to raise_reference_error
+ end.to raise_error(Gitlab::Git::CommandError)
end
end
diff --git a/spec/models/site_statistic_spec.rb b/spec/models/site_statistic_spec.rb
new file mode 100644
index 00000000000..9b056fbf332
--- /dev/null
+++ b/spec/models/site_statistic_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe SiteStatistic do
+ describe '.fetch' do
+ context 'existing record' do
+ it 'returns existing SiteStatistic model' do
+ statistics = create(:site_statistics)
+
+ expect(described_class.fetch).to be_a(described_class)
+ expect(described_class.fetch).to eq(statistics)
+ end
+ end
+
+ context 'non existing record' do
+ it 'creates a new SiteStatistic model' do
+ expect(described_class.first).to be_nil
+ expect(described_class.fetch).to be_a(described_class)
+ end
+ end
+ end
+
+ describe '.track' do
+ context 'with allowed attributes' do
+ let(:statistics) { create(:site_statistics) }
+
+ it 'increases the attribute counter' do
+ expect { described_class.track('repositories_count') }.to change { statistics.reload.repositories_count }.by(1)
+ expect { described_class.track('wikis_count') }.to change { statistics.reload.wikis_count }.by(1)
+ end
+
+ it 'doesnt increase the attribute counter when an exception happens during transaction' do
+ expect do
+ begin
+ described_class.transaction do
+ described_class.track('repositories_count')
+
+ raise StandardError
+ end
+ rescue StandardError
+ # no-op
+ end
+ end.not_to change { statistics.reload.repositories_count }
+ end
+ end
+
+ context 'with not allowed attributes' do
+ it 'returns error' do
+ expect { described_class.track('something_else') }.to raise_error(ArgumentError).with_message(/Invalid attribute: \'something_else\' to \'track\' method/)
+ end
+ end
+ end
+
+ describe '.untrack' do
+ context 'with allowed attributes' do
+ let(:statistics) { create(:site_statistics) }
+
+ it 'decreases the attribute counter' do
+ expect { described_class.untrack('repositories_count') }.to change { statistics.reload.repositories_count }.by(-1)
+ expect { described_class.untrack('wikis_count') }.to change { statistics.reload.wikis_count }.by(-1)
+ end
+
+ it 'doesnt decrease the attribute counter when an exception happens during transaction' do
+ expect do
+ begin
+ described_class.transaction do
+ described_class.track('repositories_count')
+
+ raise StandardError
+ end
+ rescue StandardError
+ # no-op
+ end
+ end.not_to change { described_class.fetch.repositories_count }
+ end
+ end
+
+ context 'with not allowed attributes' do
+ it 'returns error' do
+ expect { described_class.untrack('something_else') }.to raise_error(ArgumentError).with_message(/Invalid attribute: \'something_else\' to \'untrack\' method/)
+ end
+ end
+ end
+end
diff --git a/spec/policies/concerns/policy_actor_spec.rb b/spec/policies/concerns/policy_actor_spec.rb
new file mode 100644
index 00000000000..27db9710a38
--- /dev/null
+++ b/spec/policies/concerns/policy_actor_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PolicyActor do
+ it 'implements all the methods from user' do
+ methods = subject.instance_methods
+
+ # User.instance_methods do not return all methods until an instance is
+ # initialized. So here we just use an instance
+ expect(build(:user).methods).to include(*methods)
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb
new file mode 100644
index 00000000000..8f427d71a32
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe 'Setting WIP status of a merge request' do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:input) { { wip: true } }
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ iid: merge_request.iid
+ }
+ graphql_mutation(:merge_request_set_wip, variables.merge(input))
+ end
+
+ def mutation_response
+ graphql_mutation_response(:merge_request_set_wip)
+ end
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'returns an error if the user is not allowed to update the merge request' do
+ post_graphql_mutation(mutation, current_user: create(:user))
+
+ expect(graphql_errors).not_to be_empty
+ end
+
+ it 'marks the merge request as WIP' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).to start_with('WIP:')
+ end
+
+ it 'does not do anything if the merge request was already marked `WIP`' do
+ merge_request.update!(title: 'wip: hello world')
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).to start_with('wip:')
+ end
+
+ context 'when passing WIP false as input' do
+ let(:input) { { wip: false } }
+
+ it 'does not do anything if the merge reqeust was not marked wip' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with(/wip\:/)
+ end
+
+ it 'unmarks the merge request as `WIP`' do
+ merge_request.update!(title: 'wip: hello world')
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with('/wip\:/')
+ end
+ end
+end
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 01bbe7f5ec6..c621760b6c4 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -22,10 +22,16 @@ describe API::Members do
end
end
- shared_examples 'GET /:sources/:id/members' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'GET /:source_type/:id/members/(all)' do |source_type, all|
+ let(:members_url) do
+ "/#{source_type.pluralize}/#{source.id}/members".tap do |url|
+ url << "/all" if all
+ end
+ end
+
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
- let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members", stranger) }
+ let(:route) { get api(members_url, stranger) }
end
%i[maintainer developer access_requester stranger].each do |type|
@@ -33,7 +39,7 @@ describe API::Members do
it 'returns 200' do
user = public_send(type)
- get api("/#{source_type.pluralize}/#{source.id}/members", user)
+ get api(members_url, user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
@@ -46,23 +52,23 @@ describe API::Members do
it 'avoids N+1 queries' do
# Establish baseline
- get api("/#{source_type.pluralize}/#{source.id}/members", maintainer)
+ get api(members_url, maintainer)
control = ActiveRecord::QueryRecorder.new do
- get api("/#{source_type.pluralize}/#{source.id}/members", maintainer)
+ get api(members_url, maintainer)
end
project.add_developer(create(:user))
expect do
- get api("/#{source_type.pluralize}/#{source.id}/members", maintainer)
+ get api(members_url, maintainer)
end.not_to exceed_query_limit(control)
end
it 'does not return invitees' do
create(:"#{source_type}_member", invite_token: '123', invite_email: 'test@abc.com', source: source, user: nil)
- get api("/#{source_type.pluralize}/#{source.id}/members", developer)
+ get api(members_url, developer)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
@@ -72,7 +78,7 @@ describe API::Members do
end
it 'finds members with query string' do
- get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: maintainer.username
+ get api(members_url, developer), query: maintainer.username
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
@@ -82,7 +88,7 @@ describe API::Members do
end
it 'finds all members with no query specified' do
- get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: ''
+ get api(members_url, developer), query: ''
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
@@ -93,8 +99,51 @@ describe API::Members do
end
end
- shared_examples 'GET /:sources/:id/members/:user_id' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ describe 'GET /:source_type/:id/members/all', :nested_groups do
+ let(:nested_user) { create(:user) }
+ let(:project_user) { create(:user) }
+ let(:linked_group_user) { create(:user) }
+ let!(:project_group_link) { create(:project_group_link, project: project, group: linked_group) }
+
+ let(:project) do
+ create(:project, :public, group: nested_group) do |project|
+ project.add_developer(project_user)
+ end
+ end
+
+ let(:linked_group) do
+ create(:group) do |linked_group|
+ linked_group.add_developer(linked_group_user)
+ end
+ end
+
+ let(:nested_group) do
+ create(:group, parent: group) do |nested_group|
+ nested_group.add_developer(nested_user)
+ end
+ end
+
+ it 'finds all project members including inherited members' do
+ get api("/projects/#{project.id}/members/all", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id, project_user.id, linked_group_user.id]
+ end
+
+ it 'finds all group members including inherited members' do
+ get api("/groups/#{nested_group.id}/members/all", developer)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id]
+ end
+ end
+
+ shared_examples 'GET /:source_type/:id/members/:user_id' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) }
end
@@ -124,8 +173,8 @@ describe API::Members do
end
end
- shared_examples 'POST /:sources/:id/members' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'POST /:source_type/:id/members' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) do
post api("/#{source_type.pluralize}/#{source.id}/members", stranger),
@@ -205,8 +254,8 @@ describe API::Members do
end
end
- shared_examples 'PUT /:sources/:id/members/:user_id' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'PUT /:source_type/:id/members/:user_id' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) do
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger),
@@ -262,8 +311,8 @@ describe API::Members do
end
end
- shared_examples 'DELETE /:sources/:id/members/:user_id' do |source_type|
- context "with :sources == #{source_type.pluralize}" do
+ shared_examples 'DELETE /:source_type/:id/members/:user_id' do |source_type|
+ context "with :source_type == #{source_type.pluralize}" do
it_behaves_like 'a 404 response when source is private' do
let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) }
end
@@ -323,43 +372,45 @@ describe API::Members do
end
end
- it_behaves_like 'GET /:sources/:id/members', 'project' do
- let(:source) { project }
- end
+ [false, true].each do |all|
+ it_behaves_like 'GET /:source_type/:id/members/(all)', 'project', all do
+ let(:source) { project }
+ end
- it_behaves_like 'GET /:sources/:id/members', 'group' do
- let(:source) { group }
+ it_behaves_like 'GET /:source_type/:id/members/(all)', 'group', all do
+ let(:source) { group }
+ end
end
- it_behaves_like 'GET /:sources/:id/members/:user_id', 'project' do
+ it_behaves_like 'GET /:source_type/:id/members/:user_id', 'project' do
let(:source) { project }
end
- it_behaves_like 'GET /:sources/:id/members/:user_id', 'group' do
+ it_behaves_like 'GET /:source_type/:id/members/:user_id', 'group' do
let(:source) { group }
end
- it_behaves_like 'POST /:sources/:id/members', 'project' do
+ it_behaves_like 'POST /:source_type/:id/members', 'project' do
let(:source) { project }
end
- it_behaves_like 'POST /:sources/:id/members', 'group' do
+ it_behaves_like 'POST /:source_type/:id/members', 'group' do
let(:source) { group }
end
- it_behaves_like 'PUT /:sources/:id/members/:user_id', 'project' do
+ it_behaves_like 'PUT /:source_type/:id/members/:user_id', 'project' do
let(:source) { project }
end
- it_behaves_like 'PUT /:sources/:id/members/:user_id', 'group' do
+ it_behaves_like 'PUT /:source_type/:id/members/:user_id', 'group' do
let(:source) { group }
end
- it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'project' do
+ it_behaves_like 'DELETE /:source_type/:id/members/:user_id', 'project' do
let(:source) { project }
end
- it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do
+ it_behaves_like 'DELETE /:source_type/:id/members/:user_id', 'group' do
let(:source) { group }
end
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 179fc9733ad..98df5f787f7 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -79,7 +79,7 @@ end
# edit_admin_hook GET /admin/hooks/:id(.:format) admin/hooks#edit
describe Admin::HooksController, "routing" do
it "to #test" do
- expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
+ expect(post("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
end
it "to #index" do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 56d93095a85..70a7707826e 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -389,7 +389,7 @@ describe 'project routing' do
# DELETE /:project_id/hooks/:id(.:format) hooks#destroy
describe Projects::HooksController, 'routing' do
it 'to #test' do
- expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ expect(post('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
it_behaves_like 'RESTful project resources' do
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index eb4235e3ee6..cf57776346a 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -125,7 +125,7 @@ describe PipelineSerializer do
it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { subject }
- expect(recorded.count).to be_within(2).of(27)
+ expect(recorded.count).to be_within(2).of(31)
expect(recorded.cached_count).to eq(0)
end
end
@@ -144,7 +144,7 @@ describe PipelineSerializer do
# pipeline. With the same ref this check is cached but if refs are
# different then there is an extra query per ref
# https://gitlab.com/gitlab-org/gitlab-ce/issues/46368
- expect(recorded.count).to be_within(2).of(30)
+ expect(recorded.count).to be_within(2).of(34)
expect(recorded.cached_count).to eq(0)
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 7e85f599afb..1a85c52fc97 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -31,12 +31,6 @@ describe Projects::TransferService do
transfer_project(project, user, group)
end
- it 'expires full_path cache' do
- expect(project).to receive(:expires_full_path_cache)
-
- transfer_project(project, user, group)
- end
-
it 'invalidates the user\'s personal_project_count cache' do
expect(user).to receive(:invalidate_personal_projects_count)
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index b9322975b5a..75827df80dc 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -1,4 +1,6 @@
module GraphqlHelpers
+ MutationDefinition = Struct.new(:query, :variables)
+
# makes an underscored string look like a fieldname
# "merge_request" => "mergeRequest"
def self.fieldnamerize(underscored_field_name)
@@ -41,6 +43,37 @@ module GraphqlHelpers
QUERY
end
+ def graphql_mutation(name, input, fields = nil)
+ mutation_name = GraphqlHelpers.fieldnamerize(name)
+ input_variable_name = "$#{input_variable_name_for_mutation(name)}"
+ mutation_field = GitlabSchema.mutation.fields[mutation_name]
+ fields ||= all_graphql_fields_for(mutation_field.type)
+
+ query = <<~MUTATION
+ mutation(#{input_variable_name}: #{mutation_field.arguments['input'].type}) {
+ #{mutation_name}(input: #{input_variable_name}) {
+ #{fields}
+ }
+ }
+ MUTATION
+ variables = variables_for_mutation(name, input)
+
+ MutationDefinition.new(query, variables)
+ end
+
+ def variables_for_mutation(name, input)
+ graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h
+ { input_variable_name_for_mutation(name) => graphql_input }.to_json
+ end
+
+ def input_variable_name_for_mutation(mutation_name)
+ mutation_name = GraphqlHelpers.fieldnamerize(mutation_name)
+ mutation_field = GitlabSchema.mutation.fields[mutation_name]
+ input_type = field_type(mutation_field.arguments['input'])
+
+ GraphqlHelpers.fieldnamerize(input_type)
+ end
+
def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes)
@@ -73,8 +106,12 @@ module GraphqlHelpers
end.join(", ")
end
- def post_graphql(query, current_user: nil)
- post api('/', current_user, version: 'graphql'), query: query
+ def post_graphql(query, current_user: nil, variables: nil)
+ post api('/', current_user, version: 'graphql'), query: query, variables: variables
+ end
+
+ def post_graphql_mutation(mutation, current_user: nil)
+ post_graphql(mutation.query, current_user: current_user, variables: mutation.variables)
end
def graphql_data
@@ -82,7 +119,11 @@ module GraphqlHelpers
end
def graphql_errors
- json_response['data']
+ json_response['errors']
+ end
+
+ def graphql_mutation_response(mutation_name)
+ graphql_data[GraphqlHelpers.fieldnamerize(mutation_name)]
end
def nested_fields?(field)
@@ -102,10 +143,14 @@ module GraphqlHelpers
end
def field_type(field)
- if field.type.respond_to?(:of_type)
- field.type.of_type
- else
- field.type
- end
+ field_type = field.type
+
+ # The type could be nested. For example `[GraphQL::STRING_TYPE]`:
+ # - List
+ # - String!
+ # - String
+ field_type = field_type.of_type while field_type.respond_to?(:of_type)
+
+ field_type
end
end
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index be6fa4c71a0..7be84838e00 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -34,6 +34,15 @@ RSpec::Matchers.define :have_graphql_field do |field_name|
end
end
+RSpec::Matchers.define :have_graphql_mutation do |mutation_class|
+ match do |mutation_type|
+ field = mutation_type.fields[GraphqlHelpers.fieldnamerize(mutation_class.graphql_name)]
+
+ expect(field).to be_present
+ expect(field.resolver).to eq(mutation_class)
+ end
+end
+
RSpec::Matchers.define :have_graphql_arguments do |*expected|
include GraphqlHelpers
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index c7c3346d39e..0fd67531c3b 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -25,7 +25,7 @@ RSpec.shared_examples 'additional metrics query' do
shared_examples 'query context containing environment slug and filter' do
it 'contains ci_environment_slug' do
- expect(subject).to receive(:query_metrics).with(project, hash_including(ci_environment_slug: environment.slug))
+ expect(subject).to receive(:query_metrics).with(project, environment, hash_including(ci_environment_slug: environment.slug))
subject.query(*query_params)
end
@@ -33,6 +33,7 @@ RSpec.shared_examples 'additional metrics query' do
it 'contains environment filter' do
expect(subject).to receive(:query_metrics).with(
project,
+ environment,
hash_including(
environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\""
)
@@ -50,7 +51,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains kube_namespace' do
- expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: kube_namespace))
+ expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: kube_namespace))
subject.query(*query_params)
end
@@ -74,7 +75,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains empty kube_namespace' do
- expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: ''))
+ expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: ''))
subject.query(*query_params)
end
diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index d176d3fa425..5beae005cf7 100644
--- a/spec/support/shared_examples/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
@@ -77,7 +77,7 @@ shared_examples 'a thread answer email with reply-by-email enabled' do
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
- is_expected.to have_header('References', /\A<#{route_key}@#{host}> <reply\-.*@#{host}>\Z/ )
+ is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
end
end
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index fe7b7bc306f..04140cad3f0 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -5,7 +5,7 @@ shared_examples 'a working graphql query' do
it 'returns a successful response', :aggregate_failures do
expect(response).to have_gitlab_http_status(:success)
- expect(graphql_errors['errors']).to be_nil
+ expect(graphql_errors).to be_nil
expect(json_response.keys).to include('data')
end
end
diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb
index e8e6d2e7a75..9d1efcabb80 100644
--- a/spec/views/layouts/_head.html.haml_spec.rb
+++ b/spec/views/layouts/_head.html.haml_spec.rb
@@ -29,6 +29,39 @@ describe 'layouts/_head' do
expect(rendered).to match(%{content="foo&quot; http-equiv=&quot;refresh"})
end
+ context 'when an asset_host is set and feature is activated in the config it will' do
+ let(:asset_host) { 'http://assets' }
+
+ before do
+ stub_feature_flags(asset_host_prefetch: true)
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ end
+
+ it 'add a link dns-prefetch tag' do
+ render
+ expect(rendered).to match('<link href="http://assets" rel="dns-prefetch">')
+ end
+
+ it 'add a link preconnect tag' do
+ render
+ expect(rendered).to match('<link crossorigin="" href="http://assets" rel="preconnnect">')
+ end
+ end
+
+ context 'when an asset_host is set and feature is not activated in the config it will' do
+ let(:asset_host) { 'http://assets' }
+
+ before do
+ stub_feature_flags(asset_host_prefetch: false)
+ allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+ end
+
+ it 'not add a link dns-prefetch tag' do
+ render
+ expect(rendered).not_to match('<link href="http://assets" rel="dns-prefetch">')
+ end
+ end
+
def stub_helper_with_safe_string(method)
allow_any_instance_of(PageLayoutHelper).to receive(method)
.and_return(%q{foo" http-equiv="refresh}.html_safe)
diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
index 615462380e0..9c187bead0a 100644
--- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb
@@ -51,7 +51,6 @@ describe Gitlab::GithubImport::ObjectImporter do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, { 'number' => 10 })
diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
index 48e7eaf32fc..5b1c6b6010a 100644
--- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
@@ -33,7 +33,6 @@ describe Gitlab::GithubImport::ImportDiffNoteWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, hash)
diff --git a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
index 8cf6ac15919..ab070d6d081 100644
--- a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb
@@ -36,7 +36,6 @@ describe Gitlab::GithubImport::ImportIssueWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, hash)
diff --git a/spec/workers/gitlab/github_import/import_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_note_worker_spec.rb
index 677697c02df..3a30f06bb2d 100644
--- a/spec/workers/gitlab/github_import/import_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_note_worker_spec.rb
@@ -31,7 +31,6 @@ describe Gitlab::GithubImport::ImportNoteWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, hash)
diff --git a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
index e287ddbe0d7..3cccd7cab21 100644
--- a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb
@@ -42,7 +42,6 @@ describe Gitlab::GithubImport::ImportPullRequestWorker do
expect(worker.counter)
.to receive(:increment)
- .with(project: 'foo/bar')
.and_call_original
worker.import(project, client, hash)
diff --git a/yarn.lock b/yarn.lock
index 85fdb150d34..21e62a6d36a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -78,9 +78,17 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.26.0":
- version "1.26.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.26.0.tgz#d89c633e866d031a9e4787b05eacc7144c3a7791"
+"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.27.0":
+ version "1.27.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.27.0.tgz#638e70399ebd59e503732177316bb9a18bf7a13f"
+
+"@gitlab-org/gitlab-ui@1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.0.5.tgz#a64b402650494115c8b494a44b72c2d6fbf33fff"
+ dependencies:
+ "@gitlab-org/gitlab-svgs" "^1.23.0"
+ bootstrap-vue "^2.0.0-rc.11"
+ vue "^2.5.16"
"@sindresorhus/is@^0.7.0":
version "0.7.0"
@@ -346,6 +354,10 @@ ansi-align@^2.0.0:
dependencies:
string-width "^2.0.0"
+ansi-escapes@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
ansi-escapes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
@@ -1045,6 +1057,14 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
+babel-polyfill@6.23.0:
+ version "6.23.0"
+ resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d"
+ dependencies:
+ babel-runtime "^6.22.0"
+ core-js "^2.4.0"
+ regenerator-runtime "^0.10.0"
+
babel-preset-es2015@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
@@ -1317,6 +1337,21 @@ boom@5.x.x:
dependencies:
hoek "4.x.x"
+bootstrap-vue@^2.0.0-rc.11:
+ version "2.0.0-rc.11"
+ resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.11.tgz#47aaa6d2a8d390477de75e636d8ea652b1d03f59"
+ dependencies:
+ bootstrap "^4.1.1"
+ lodash.get "^4.4.2"
+ lodash.startcase "^4.4.0"
+ opencollective "^1.0.3"
+ popper.js "^1.12.9"
+ vue-functional-data-merge "^2.0.5"
+
+bootstrap@^4.1.1:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.2.tgz#aee2a93472e61c471fc79fb475531dcbc87de326"
+
bootstrap@~4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.1.tgz#3aec85000fa619085da8d2e4983dfd67cf2114cb"
@@ -1576,7 +1611,7 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
-chalk@^1.1.1, chalk@^1.1.3:
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -1774,7 +1809,7 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
-commander@^2.13.0, commander@^2.15.1, commander@^2.9.0:
+commander@2, commander@^2.13.0, commander@^2.15.1, commander@^2.9.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
@@ -2070,15 +2105,15 @@ cyclist@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
-d3-array@^1.2.0, d3-array@^1.2.1:
+d3-array@1, d3-array@1.2.1, d3-array@^1.2.0, d3-array@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
-d3-axis@^1.0.8:
+d3-axis@1.0.8, d3-axis@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.8.tgz#31a705a0b535e65759de14173a31933137f18efa"
-d3-brush@^1.0.4:
+d3-brush@1.0.4, d3-brush@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.4.tgz#00c2f238019f24f6c0a194a26d41a1530ffe7bc4"
dependencies:
@@ -2088,44 +2123,103 @@ d3-brush@^1.0.4:
d3-selection "1"
d3-transition "1"
-d3-collection@1:
+d3-chord@1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.4.tgz#7dec4f0ba886f713fe111c45f763414f6f74ca2c"
+ dependencies:
+ d3-array "1"
+ d3-path "1"
+
+d3-collection@1, d3-collection@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2"
-d3-color@1:
+d3-color@1, d3-color@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b"
-d3-dispatch@1:
+d3-dispatch@1, d3-dispatch@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8"
-d3-drag@1:
+d3-drag@1, d3-drag@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.1.tgz#df8dd4c502fb490fc7462046a8ad98a5c479282d"
dependencies:
d3-dispatch "1"
d3-selection "1"
-d3-ease@1:
+d3-dsv@1, d3-dsv@1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.8.tgz#907e240d57b386618dc56468bacfe76bf19764ae"
+ dependencies:
+ commander "2"
+ iconv-lite "0.4"
+ rw "1"
+
+d3-ease@1, d3-ease@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.3.tgz#68bfbc349338a380c44d8acc4fbc3304aa2d8c0e"
-d3-format@1:
+d3-force@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.0.tgz#cebf3c694f1078fcc3d4daf8e567b2fbd70d4ea3"
+ dependencies:
+ d3-collection "1"
+ d3-dispatch "1"
+ d3-quadtree "1"
+ d3-timer "1"
+
+d3-format@1, d3-format@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f"
-d3-interpolate@1:
+d3-geo@1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356"
+ dependencies:
+ d3-array "1"
+
+d3-hierarchy@1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26"
+
+d3-interpolate@1, d3-interpolate@1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6"
dependencies:
d3-color "1"
-d3-path@1:
+d3-path@1, d3-path@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764"
-d3-scale@^1.0.7:
+d3-polygon@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.3.tgz#16888e9026460933f2b179652ad378224d382c62"
+
+d3-quadtree@1, d3-quadtree@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.3.tgz#ac7987e3e23fe805a990f28e1b50d38fcb822438"
+
+d3-queue@3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/d3-queue/-/d3-queue-3.0.7.tgz#c93a2e54b417c0959129d7d73f6cf7d4292e7618"
+
+d3-random@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.0.tgz#6642e506c6fa3a648595d2b2469788a8d12529d3"
+
+d3-request@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/d3-request/-/d3-request-1.0.6.tgz#a1044a9ef4ec28c824171c9379fae6d79474b19f"
+ dependencies:
+ d3-collection "1"
+ d3-dispatch "1"
+ d3-dsv "1"
+ xmlhttprequest "1"
+
+d3-scale@1.0.7, d3-scale@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d"
dependencies:
@@ -2137,31 +2231,31 @@ d3-scale@^1.0.7:
d3-time "1"
d3-time-format "2"
-d3-selection@1, d3-selection@^1.1.0, d3-selection@^1.2.0:
+d3-selection@1, d3-selection@1.2.0, d3-selection@^1.1.0, d3-selection@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88"
-d3-shape@^1.2.0:
+d3-shape@1.2.0, d3-shape@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777"
dependencies:
d3-path "1"
-d3-time-format@2, d3-time-format@^2.1.1:
+d3-time-format@2, d3-time-format@2.1.1, d3-time-format@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31"
dependencies:
d3-time "1"
-d3-time@1, d3-time@^1.0.8:
+d3-time@1, d3-time@1.0.8, d3-time@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84"
-d3-timer@1:
+d3-timer@1, d3-timer@1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531"
-d3-transition@1:
+d3-transition@1, d3-transition@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.1.tgz#d8ef89c3b848735b060e54a39b32aaebaa421039"
dependencies:
@@ -2172,10 +2266,59 @@ d3-transition@1:
d3-selection "^1.1.0"
d3-timer "1"
+d3-voronoi@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c"
+
+d3-zoom@1.7.1:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.1.tgz#02f43b3c3e2db54f364582d7e4a236ccc5506b63"
+ dependencies:
+ d3-dispatch "1"
+ d3-drag "1"
+ d3-interpolate "1"
+ d3-selection "1"
+ d3-transition "1"
+
d3@3.5.17:
version "3.5.17"
resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
+d3@4.12.2:
+ version "4.12.2"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.2.tgz#12f775564c6a9de229f63db03446e2cb7bb56c8f"
+ dependencies:
+ d3-array "1.2.1"
+ d3-axis "1.0.8"
+ d3-brush "1.0.4"
+ d3-chord "1.0.4"
+ d3-collection "1.0.4"
+ d3-color "1.0.3"
+ d3-dispatch "1.0.3"
+ d3-drag "1.2.1"
+ d3-dsv "1.0.8"
+ d3-ease "1.0.3"
+ d3-force "1.1.0"
+ d3-format "1.2.1"
+ d3-geo "1.9.1"
+ d3-hierarchy "1.1.5"
+ d3-interpolate "1.1.6"
+ d3-path "1.0.5"
+ d3-polygon "1.0.3"
+ d3-quadtree "1.0.3"
+ d3-queue "3.0.7"
+ d3-random "1.1.0"
+ d3-request "1.0.6"
+ d3-scale "1.0.7"
+ d3-selection "1.2.0"
+ d3-shape "1.2.0"
+ d3-time "1.0.8"
+ d3-time-format "2.1.1"
+ d3-timer "1.0.7"
+ d3-transition "1.1.1"
+ d3-voronoi "1.1.2"
+ d3-zoom "1.7.1"
+
dagre-d3-renderer@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.24.tgz#b36ce2fe4ea20de43e7698627c6ede2a9f15ec45"
@@ -2534,6 +2677,12 @@ encodeurl@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+encoding@^0.1.11:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ dependencies:
+ iconv-lite "~0.4.13"
+
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -3009,7 +3158,7 @@ extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
-external-editor@^2.0.4:
+external-editor@^2.0.1, external-editor@^2.0.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
dependencies:
@@ -3771,6 +3920,12 @@ https-proxy-agent@^2.2.1:
agent-base "^4.1.0"
debug "^3.1.0"
+iconv-lite@0.4:
+ version "0.4.23"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
@@ -3779,7 +3934,7 @@ iconv-lite@0.4.19, iconv-lite@^0.4.17:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
-iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4:
+iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
dependencies:
@@ -3884,6 +4039,24 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+inquirer@3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347"
+ dependencies:
+ ansi-escapes "^1.1.0"
+ chalk "^1.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^2.0.1"
+ figures "^2.0.0"
+ lodash "^4.3.0"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rx "^4.1.0"
+ string-width "^2.0.0"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
inquirer@^3.0.6:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
@@ -4173,7 +4346,7 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
-is-stream@^1.0.0, is-stream@^1.1.0:
+is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -4662,6 +4835,10 @@ lodash.escaperegexp@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
+lodash.get@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+
lodash.kebabcase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
@@ -4674,6 +4851,10 @@ lodash.snakecase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
+lodash.startcase@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
+
lodash.upperfirst@4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
@@ -4954,7 +5135,7 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.3, minimist@^1.2.0:
+minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -5098,6 +5279,13 @@ nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
+node-fetch@1.6.3:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
node-forge@0.6.33:
version "0.6.33"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
@@ -5351,10 +5539,28 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
+opencollective@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1"
+ dependencies:
+ babel-polyfill "6.23.0"
+ chalk "1.1.3"
+ inquirer "3.0.6"
+ minimist "1.2.0"
+ node-fetch "1.6.3"
+ opn "4.0.2"
+
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
+opn@4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+ dependencies:
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
opn@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
@@ -5648,7 +5854,7 @@ pluralize@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
-popper.js@^1.14.3:
+popper.js@^1.12.9, popper.js@^1.14.3:
version "1.14.3"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095"
@@ -6056,6 +6262,10 @@ regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
+regenerator-runtime@^0.10.0:
+ version "0.10.5"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
regenerator-runtime@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
@@ -6293,6 +6503,10 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
+rw@1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+
rx-lite-aggregates@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
@@ -6303,6 +6517,10 @@ rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+rx@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
rxjs@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1"
@@ -7457,6 +7675,10 @@ vue-eslint-parser@^2.0.3:
esquery "^1.0.0"
lodash "^4.17.4"
+vue-functional-data-merge@^2.0.5:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-2.0.6.tgz#f08055adfb92458debcf2ad10c3aa712277f7fc2"
+
vue-hot-reload-api@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
@@ -7772,6 +7994,10 @@ xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
+xmlhttprequest@1:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
+
xregexp@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"