summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.gitlab-ci.yml29
-rw-r--r--.gitlab/merge_request_templates/Database changes.md22
-rw-r--r--.gitlab/merge_request_templates/Documentation.md14
-rw-r--r--.rubocop_todo.yml6
-rw-r--r--CONTRIBUTING.md53
-rw-r--r--Dangerfile6
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock30
-rw-r--r--Gemfile.rails5.lock36
-rw-r--r--README.md6
-rwxr-xr-xRakefile4
-rw-r--r--app/assets/javascripts/api.js4
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js5
-rw-r--r--app/assets/javascripts/diffs/components/app.vue26
-rw-r--r--app/assets/javascripts/diffs/components/changed_files.vue45
-rw-r--r--app/assets/javascripts/diffs/components/changed_files_dropdown.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussions.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue56
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue31
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_gutter_content.vue3
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue19
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue20
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue8
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue28
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue29
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue30
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue28
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue44
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue46
-rw-r--r--app/assets/javascripts/diffs/store/actions.js38
-rw-r--r--app/assets/javascripts/diffs/store/getters.js74
-rw-r--r--app/assets/javascripts/diffs/store/index.js11
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js18
-rw-r--r--app/assets/javascripts/diffs/store/modules/index.js24
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js15
-rw-r--r--app/assets/javascripts/due_date_select.js4
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue87
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.vue51
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue873
-rw-r--r--app/assets/javascripts/environments/components/environment_monitoring.vue49
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue94
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue106
-rw-r--r--app/assets/javascripts/environments/components/environment_terminal_button.vue53
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue4
-rw-r--r--app/assets/javascripts/environments/components/stop_environment_modal.vue92
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue8
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js22
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js2
-rw-r--r--app/assets/javascripts/frequent_items/components/app.vue122
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list.vue78
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue117
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_mixin.js23
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue55
-rw-r--r--app/assets/javascripts/frequent_items/constants.js38
-rw-r--r--app/assets/javascripts/frequent_items/event_hub.js (renamed from app/assets/javascripts/projects_dropdown/event_hub.js)0
-rw-r--r--app/assets/javascripts/frequent_items/index.js69
-rw-r--r--app/assets/javascripts/frequent_items/store/actions.js81
-rw-r--r--app/assets/javascripts/frequent_items/store/getters.js4
-rw-r--r--app/assets/javascripts/frequent_items/store/index.js16
-rw-r--r--app/assets/javascripts/frequent_items/store/mutation_types.js9
-rw-r--r--app/assets/javascripts/frequent_items/store/mutations.js71
-rw-r--r--app/assets/javascripts/frequent_items/store/state.js8
-rw-r--r--app/assets/javascripts/frequent_items/utils.js49
-rw-r--r--app/assets/javascripts/ide/components/ide.vue3
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue21
-rw-r--r--app/assets/javascripts/ide/components/ide_tree.vue37
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/info.vue43
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/button.vue51
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue70
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue72
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue124
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue27
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue11
-rw-r--r--app/assets/javascripts/ide/constants.js1
-rw-r--r--app/assets/javascripts/ide/ide_router.js3
-rw-r--r--app/assets/javascripts/ide/lib/themes/gl_theme.js1
-rw-r--r--app/assets/javascripts/ide/services/index.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js13
-rw-r--r--app/assets/javascripts/ide/stores/actions/merge_request.js14
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js5
-rw-r--r--app/assets/javascripts/ide/stores/state.js4
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue112
-rw-r--r--app/assets/javascripts/main.js2
-rw-r--r--app/assets/javascripts/notes.js4
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue6
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue162
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue1
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue8
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue6
-rw-r--r--app/assets/javascripts/notes/index.js3
-rw-r--r--app/assets/javascripts/notes/stores/actions.js11
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js7
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js6
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js12
-rw-r--r--app/assets/javascripts/pages/profiles/keys/index.js16
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue27
-rw-r--r--app/assets/javascripts/performance_bar/index.js33
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue1
-rw-r--r--app/assets/javascripts/preview_markdown.js14
-rw-r--r--app/assets/javascripts/profile/add_ssh_key_validation.js43
-rw-r--r--app/assets/javascripts/projects_dropdown/components/app.vue158
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue57
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_item.vue116
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_search.vue63
-rw-r--r--app/assets/javascripts/projects_dropdown/components/search.vue65
-rw-r--r--app/assets/javascripts/projects_dropdown/constants.js10
-rw-r--r--app/assets/javascripts/projects_dropdown/index.js66
-rw-r--r--app/assets/javascripts/projects_dropdown/service/projects_service.js137
-rw-r--r--app/assets/javascripts/projects_dropdown/store/projects_store.js33
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue98
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue92
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue197
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue104
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue55
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js10
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue22
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue7
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss5
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss69
-rw-r--r--app/assets/stylesheets/framework/filters.scss5
-rw-r--r--app/assets/stylesheets/framework/forms.scss5
-rw-r--r--app/assets/stylesheets/framework/gfm.scss8
-rw-r--r--app/assets/stylesheets/framework/gitlab_theme.scss21
-rw-r--r--app/assets/stylesheets/framework/header.scss24
-rw-r--r--app/assets/stylesheets/framework/icons.scss24
-rw-r--r--app/assets/stylesheets/framework/mixins.scss7
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss7
-rw-r--r--app/assets/stylesheets/framework/typography.scss3
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/boards.scss11
-rw-r--r--app/assets/stylesheets/pages/diff.scss6
-rw-r--r--app/assets/stylesheets/pages/environments.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss1
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss185
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss15
-rw-r--r--app/assets/stylesheets/pages/repo.scss59
-rw-r--r--app/assets/stylesheets/pages/todos.scss16
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb4
-rw-r--r--app/controllers/admin/groups_controller.rb2
-rw-r--r--app/controllers/admin/hooks_controller.rb4
-rw-r--r--app/controllers/admin/identities_controller.rb2
-rw-r--r--app/controllers/admin/impersonations_controller.rb2
-rw-r--r--app/controllers/admin/jobs_controller.rb2
-rw-r--r--app/controllers/admin/runner_projects_controller.rb2
-rw-r--r--app/controllers/admin/runners_controller.rb2
-rw-r--r--app/controllers/admin/services_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/application_controller.rb12
-rw-r--r--app/controllers/concerns/group_tree.rb40
-rw-r--r--app/controllers/concerns/issuable_actions.rb2
-rw-r--r--app/controllers/concerns/lfs_request.rb2
-rw-r--r--app/controllers/concerns/preview_markdown.rb2
-rw-r--r--app/controllers/concerns/todos_actions.rb12
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/runners_controller.rb2
-rw-r--r--app/controllers/import/manifest_controller.rb93
-rw-r--r--app/controllers/jwt_controller.rb4
-rw-r--r--app/controllers/notification_settings_controller.rb4
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb2
-rw-r--r--app/controllers/profiles/avatars_controller.rb2
-rw-r--r--app/controllers/profiles/chat_names_controller.rb2
-rw-r--r--app/controllers/profiles/emails_controller.rb2
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb4
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb2
-rw-r--r--app/controllers/projects/application_controller.rb2
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb2
-rw-r--r--app/controllers/projects/avatars_controller.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/clusters_controller.rb2
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb2
-rw-r--r--app/controllers/projects/environments_controller.rb8
-rw-r--r--app/controllers/projects/git_http_client_controller.rb4
-rw-r--r--app/controllers/projects/group_links_controller.rb4
-rw-r--r--app/controllers/projects/hooks_controller.rb4
-rw-r--r--app/controllers/projects/labels_controller.rb4
-rw-r--r--app/controllers/projects/lfs_api_controller.rb2
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/projects/milestones_controller.rb2
-rw-r--r--app/controllers/projects/mirrors_controller.rb2
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb2
-rw-r--r--app/controllers/projects/releases_controller.rb2
-rw-r--r--app/controllers/projects/repositories_controller.rb2
-rw-r--r--app/controllers/projects/runner_projects_controller.rb2
-rw-r--r--app/controllers/projects/runners_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb2
-rw-r--r--app/controllers/projects/snippets_controller.rb2
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/controllers/projects/templates_controller.rb2
-rw-r--r--app/controllers/projects/todos_controller.rb14
-rw-r--r--app/controllers/projects/triggers_controller.rb2
-rw-r--r--app/controllers/projects/wikis_controller.rb11
-rw-r--r--app/controllers/projects_controller.rb19
-rw-r--r--app/controllers/sessions_controller.rb4
-rw-r--r--app/controllers/sherlock/transactions_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb2
-rw-r--r--app/finders/projects_finder.rb3
-rw-r--r--app/finders/todos_finder.rb52
-rw-r--r--app/helpers/application_settings_helper.rb1
-rw-r--r--app/helpers/clusters_helper.rb1
-rw-r--r--app/helpers/issuables_helper.rb14
-rw-r--r--app/helpers/markup_helper.rb4
-rw-r--r--app/helpers/namespaces_helper.rb11
-rw-r--r--app/helpers/notes_helper.rb1
-rw-r--r--app/helpers/pipeline_schedules_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb14
-rw-r--r--app/helpers/submodule_helper.rb8
-rw-r--r--app/helpers/time_helper.rb8
-rw-r--r--app/helpers/todos_helper.rb10
-rw-r--r--app/mailers/previews/devise_mailer_preview.rb (renamed from spec/mailers/previews/devise_mailer_preview.rb)0
-rw-r--r--app/mailers/previews/email_rejection_mailer_preview.rb (renamed from spec/mailers/previews/email_rejection_mailer_preview.rb)0
-rw-r--r--app/mailers/previews/notify_preview.rb (renamed from spec/mailers/previews/notify_preview.rb)2
-rw-r--r--app/mailers/previews/repository_check_mailer_preview.rb (renamed from spec/mailers/previews/repository_check_mailer_preview.rb)0
-rw-r--r--app/models/application_setting.rb1
-rw-r--r--app/models/ci/build.rb7
-rw-r--r--app/models/ci/build_trace_chunk.rb149
-rw-r--r--app/models/ci/build_trace_chunks/database.rb29
-rw-r--r--app/models/ci/build_trace_chunks/fog.rb59
-rw-r--r--app/models/ci/build_trace_chunks/redis.rb51
-rw-r--r--app/models/concerns/cache_markdown_field.rb22
-rw-r--r--app/models/concerns/cacheable_attributes.rb4
-rw-r--r--app/models/concerns/group_descendant.rb4
-rw-r--r--app/models/concerns/issuable.rb6
-rw-r--r--app/models/concerns/protected_ref.rb2
-rw-r--r--app/models/concerns/protected_ref_access.rb7
-rw-r--r--app/models/concerns/select_for_project_authorization.rb7
-rw-r--r--app/models/deployment.rb4
-rw-r--r--app/models/environment.rb2
-rw-r--r--app/models/group.rb22
-rw-r--r--app/models/import_export_upload.rb13
-rw-r--r--app/models/issue.rb4
-rw-r--r--app/models/member.rb6
-rw-r--r--app/models/members/project_member.rb6
-rw-r--r--app/models/milestone.rb3
-rw-r--r--app/models/network/commit.rb4
-rw-r--r--app/models/note.rb4
-rw-r--r--app/models/notification_setting.rb1
-rw-r--r--app/models/project.rb44
-rw-r--r--app/models/project_group_link.rb3
-rw-r--r--app/models/project_team.rb21
-rw-r--r--app/models/project_wiki.rb14
-rw-r--r--app/models/remote_mirror.rb2
-rw-r--r--app/models/repository.rb20
-rw-r--r--app/models/todo.rb11
-rw-r--r--app/models/user.rb30
-rw-r--r--app/models/wiki_page.rb1
-rw-r--r--app/policies/clusters/cluster_policy.rb2
-rw-r--r--app/policies/deploy_token_policy.rb4
-rw-r--r--app/policies/environment_policy.rb11
-rw-r--r--app/policies/group_policy.rb4
-rw-r--r--app/policies/project_policy.rb8
-rw-r--r--app/serializers/environment_entity.rb12
-rw-r--r--app/serializers/merge_request_widget_entity.rb6
-rw-r--r--app/serializers/note_entity.rb2
-rw-r--r--app/services/access_token_validation_service.rb2
-rw-r--r--app/services/after_branch_delete_service.rb2
-rw-r--r--app/services/akismet_service.rb2
-rw-r--r--app/services/audit_event_service.rb2
-rw-r--r--app/services/badges/update_service.rb2
-rw-r--r--app/services/base_count_service.rb2
-rw-r--r--app/services/base_renderer.rb2
-rw-r--r--app/services/base_service.rb2
-rw-r--r--app/services/ci/stop_environments_service.rb2
-rw-r--r--app/services/cohorts_service.rb2
-rw-r--r--app/services/commits/change_service.rb2
-rw-r--r--app/services/compare_service.rb2
-rw-r--r--app/services/create_branch_service.rb2
-rw-r--r--app/services/create_deployment_service.rb2
-rw-r--r--app/services/create_release_service.rb2
-rw-r--r--app/services/create_snippet_service.rb2
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/delete_merged_branches_service.rb2
-rw-r--r--app/services/event_create_service.rb2
-rw-r--r--app/services/git_push_service.rb2
-rw-r--r--app/services/git_tag_push_service.rb2
-rw-r--r--app/services/gravatar_service.rb2
-rw-r--r--app/services/groups/nested_create_service.rb12
-rw-r--r--app/services/ham_service.rb2
-rw-r--r--app/services/import_export_clean_up_service.rb13
-rw-r--r--app/services/issuable_base_service.rb4
-rw-r--r--app/services/members/update_service.rb2
-rw-r--r--app/services/merge_request_metrics_service.rb2
-rw-r--r--app/services/merge_requests/rebase_service.rb2
-rw-r--r--app/services/metrics_service.rb23
-rw-r--r--app/services/milestones/update_service.rb2
-rw-r--r--app/services/note_summary.rb2
-rw-r--r--app/services/notes/update_service.rb2
-rw-r--r--app/services/notification_recipient_service.rb15
-rw-r--r--app/services/notification_service.rb18
-rw-r--r--app/services/preview_markdown_service.rb9
-rw-r--r--app/services/projects/autocomplete_service.rb34
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb2
-rw-r--r--app/services/projects/fork_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb2
-rw-r--r--app/services/projects/update_service.rb2
-rw-r--r--app/services/protected_branches/access_level_params.rb2
-rw-r--r--app/services/protected_branches/legacy_api_create_service.rb4
-rw-r--r--app/services/protected_branches/legacy_api_update_service.rb4
-rw-r--r--app/services/push_event_payload_service.rb2
-rw-r--r--app/services/repair_ldap_blocked_user_service.rb2
-rw-r--r--app/services/repository_archive_clean_up_service.rb2
-rw-r--r--app/services/reset_project_cache_service.rb2
-rw-r--r--app/services/search_service.rb2
-rw-r--r--app/services/spam_check_service.rb2
-rw-r--r--app/services/spam_service.rb2
-rw-r--r--app/services/submit_usage_ping_service.rb2
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/services/system_note_service.rb35
-rw-r--r--app/services/todo_service.rb32
-rw-r--r--app/services/update_release_service.rb4
-rw-r--r--app/services/update_snippet_service.rb2
-rw-r--r--app/services/upload_service.rb8
-rw-r--r--app/services/user_agent_detail_service.rb2
-rw-r--r--app/services/user_project_access_changed_service.rb2
-rw-r--r--app/services/validate_new_branch_service.rb2
-rw-r--r--app/services/verify_pages_domain_service.rb2
-rw-r--r--app/services/web_hook_service.rb2
-rw-r--r--app/uploaders/file_uploader.rb8
-rw-r--r--app/uploaders/gitlab_uploader.rb22
-rw-r--r--app/uploaders/import_export_uploader.rb15
-rw-r--r--app/uploaders/job_artifact_uploader.rb8
-rw-r--r--app/views/admin/application_settings/_third_party_offers.html.haml13
-rw-r--r--app/views/admin/application_settings/show.html.haml11
-rw-r--r--app/views/admin/groups/_form.html.haml10
-rw-r--r--app/views/admin/groups/_group.html.haml4
-rw-r--r--app/views/admin/groups/edit.html.haml4
-rw-r--r--app/views/admin/groups/index.html.haml4
-rw-r--r--app/views/admin/groups/new.html.haml4
-rw-r--r--app/views/admin/groups/show.html.haml56
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml2
-rw-r--r--app/views/dashboard/_activities.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml48
-rw-r--r--app/views/doorkeeper/applications/_delete_form.html.haml6
-rw-r--r--app/views/doorkeeper/applications/_form.html.haml8
-rw-r--r--app/views/doorkeeper/applications/edit.html.haml4
-rw-r--r--app/views/doorkeeper/applications/index.html.haml35
-rw-r--r--app/views/doorkeeper/applications/new.html.haml4
-rw-r--r--app/views/doorkeeper/applications/show.html.haml14
-rw-r--r--app/views/doorkeeper/authorizations/error.html.haml2
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml28
-rw-r--r--app/views/doorkeeper/authorizations/show.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/_delete_form.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/index.html.haml6
-rw-r--r--app/views/explore/_head.html.haml4
-rw-r--r--app/views/explore/groups/_nav.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml12
-rw-r--r--app/views/explore/projects/_filter.html.haml6
-rw-r--r--app/views/explore/projects/_nav.html.haml6
-rw-r--r--app/views/explore/projects/index.html.haml4
-rw-r--r--app/views/explore/projects/starred.html.haml4
-rw-r--r--app/views/explore/projects/trending.html.haml4
-rw-r--r--app/views/groups/_activities.html.haml2
-rw-r--r--app/views/import/_githubish_status.html.haml24
-rw-r--r--app/views/import/_project_status.html.haml11
-rw-r--r--app/views/import/bitbucket/deploy_key.js.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml37
-rw-r--r--app/views/import/fogbugz/new.html.haml18
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml34
-rw-r--r--app/views/import/fogbugz/status.html.haml27
-rw-r--r--app/views/import/gitea/new.html.haml17
-rw-r--r--app/views/import/gitea/status.html.haml6
-rw-r--r--app/views/import/github/new.html.haml2
-rw-r--r--app/views/import/github/status.html.haml2
-rw-r--r--app/views/import/gitlab/status.html.haml22
-rw-r--r--app/views/import/gitlab_projects/new.html.haml16
-rw-r--r--app/views/import/google_code/new.html.haml42
-rw-r--r--app/views/import/google_code/new_user_map.html.haml36
-rw-r--r--app/views/import/google_code/status.html.haml40
-rw-r--r--app/views/import/manifest/_form.html.haml23
-rw-r--r--app/views/import/manifest/new.html.haml12
-rw-r--r--app/views/import/manifest/status.html.haml42
-rw-r--r--app/views/layouts/header/_default.html.haml4
-rw-r--r--app/views/layouts/header/_new_dropdown.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml16
-rw-r--r--app/views/layouts/nav/groups_dropdown/_show.html.haml12
-rw-r--r--app/views/layouts/nav/projects_dropdown/_show.html.haml6
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml8
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml13
-rw-r--r--app/views/projects/_activity.html.haml3
-rw-r--r--app/views/projects/_export.html.haml2
-rw-r--r--app/views/projects/_import_project_pane.html.haml47
-rw-r--r--app/views/projects/commit/_change.html.haml2
-rw-r--r--app/views/projects/deployments/_actions.haml7
-rw-r--r--app/views/projects/deployments/_rollback.haml7
-rw-r--r--app/views/projects/environments/_external_url.html.haml2
-rw-r--r--app/views/projects/environments/_stop.html.haml5
-rw-r--r--app/views/projects/environments/show.html.haml32
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/issues/_form.html.haml4
-rw-r--r--app/views/projects/merge_requests/_form.html.haml4
-rw-r--r--app/views/projects/merge_requests/_mr_box.html.haml2
-rw-r--r--app/views/projects/milestones/_form.html.haml10
-rw-r--r--app/views/projects/milestones/index.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/new.html.haml13
-rw-r--r--app/views/projects/protected_branches/_update_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_protected_branch.html.haml2
-rw-r--r--app/views/projects/releases/edit.html.haml4
-rw-r--r--app/views/projects/wikis/_form.html.haml4
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml8
-rw-r--r--app/views/shared/_event_filter.html.haml2
-rw-r--r--app/views/shared/_new_project_item_select.html.haml4
-rw-r--r--app/views/shared/hook_logs/_content.html.haml4
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml2
-rw-r--r--app/views/shared/notes/_form.html.haml2
-rw-r--r--app/views/shared/notes/_note.html.haml2
-rw-r--r--app/views/shared/snippets/_form.html.haml4
-rw-r--r--app/workers/all_queues.yml1
-rw-r--r--app/workers/ci/build_trace_chunk_flush_worker.rb2
-rw-r--r--app/workers/email_receiver_worker.rb8
-rw-r--r--app/workers/git_garbage_collect_worker.rb4
-rw-r--r--app/workers/object_storage/migrate_uploads_worker.rb2
-rw-r--r--app/workers/object_storage_upload_worker.rb23
-rw-r--r--app/workers/process_commit_worker.rb5
-rw-r--r--app/workers/repository_check/batch_worker.rb20
-rw-r--r--app/workers/repository_check/dispatch_worker.rb13
-rwxr-xr-xbin/changelog14
-rw-r--r--changelogs/custom_wiki_sidebar.yml5
-rw-r--r--changelogs/unreleased/36234-nav-add-groups-dropdown.yml5
-rw-r--r--changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml5
-rw-r--r--changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml5
-rw-r--r--changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml5
-rw-r--r--changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key.yml5
-rw-r--r--changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml5
-rw-r--r--changelogs/unreleased/48237-toggle-file-comments.yml5
-rw-r--r--changelogs/unreleased/48537-update-avatar-only-via-api.yml5
-rw-r--r--changelogs/unreleased/48578-disable-gcp-free-credit-banner-at-instance-level.yml5
-rw-r--r--changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml5
-rw-r--r--changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml6
-rw-r--r--changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml5
-rw-r--r--changelogs/unreleased/48789-remove-event-listeners-scroll.yml6
-rw-r--r--changelogs/unreleased/48894-fix-rss-button-interaction.yml5
-rw-r--r--changelogs/unreleased/48934.yml5
-rw-r--r--changelogs/unreleased/48951-clean-up.yml5
-rw-r--r--changelogs/unreleased/48976-fix-sti-background-migration.yml6
-rw-r--r--changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml5
-rw-r--r--changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml5
-rw-r--r--changelogs/unreleased/49291-fix-memory-graph-component-typo.yml5
-rw-r--r--changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml5
-rw-r--r--changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml5
-rw-r--r--changelogs/unreleased/an-no-healthcheck-until-brooklyn.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-activerecord-statementinvalid-mysql2-error-expression-1-of-select-list-is-not-in-group-by-clause.yml5
-rw-r--r--changelogs/unreleased/build-chunks-on-object-storage.yml6
-rw-r--r--changelogs/unreleased/bvl-preload-parents-after-pagination.yml5
-rw-r--r--changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml5
-rw-r--r--changelogs/unreleased/dz-manifest-import.yml5
-rw-r--r--changelogs/unreleased/feature-gb-email-delivery-metrics.yml5
-rw-r--r--changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml5
-rw-r--r--changelogs/unreleased/fix-performance-problem-of-tags-query.yml5
-rw-r--r--changelogs/unreleased/fix-project-api-archived.yml5
-rw-r--r--changelogs/unreleased/fix-search-bar.yml5
-rw-r--r--changelogs/unreleased/fl-mr-refactor-performance-improvements.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-services.yml5
-rw-r--r--changelogs/unreleased/gitaly-serverservice-info-timeout.yml5
-rw-r--r--changelogs/unreleased/ide-merge-request-info.yml5
-rw-r--r--changelogs/unreleased/ide-pipeline-icon-open.yml5
-rw-r--r--changelogs/unreleased/ide-row-dropdown-design-update.yml5
-rw-r--r--changelogs/unreleased/improve-metadata-access-performance.yml5
-rw-r--r--changelogs/unreleased/issue_47709.yml5
-rw-r--r--changelogs/unreleased/jprovazn-delete-upload-worker.yml5
-rw-r--r--changelogs/unreleased/jprovazn-upload-symlink.yml5
-rw-r--r--changelogs/unreleased/perf-wiki-pattern-once.yml5
-rw-r--r--changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48977.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml5
-rw-r--r--changelogs/unreleased/rails5-mysql-rename-column.yml5
-rw-r--r--changelogs/unreleased/rails5-update-gemfile-lock.yml5
-rw-r--r--changelogs/unreleased/rosulk-patch-12.yml5
-rw-r--r--changelogs/unreleased/runners-max-timeout-param.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-47797-ce.yml5
-rw-r--r--changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml5
-rw-r--r--changelogs/unreleased/sh-handle-colons-in-url-passwords.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-wiki-empty-check.yml5
-rw-r--r--changelogs/unreleased/tweak-sql-buckets.yml5
-rw-r--r--changelogs/unreleased/update-issue-closing-pattern.yml5
-rw-r--r--changelogs/unreleased/upgrade-hamlit-for-ruby25.yml5
-rw-r--r--changelogs/unreleased/winh-stop-all-environments.yml5
-rw-r--r--changelogs/unreleased/winh-upgrade-grape-path-helpers.yml5
-rw-r--r--config/application.rb4
-rw-r--r--config/environment.rb2
-rw-r--r--config/environments/development.rb2
-rw-r--r--config/gitlab.yml.example5
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/action_mailer_hooks.rb12
-rw-r--r--config/initializers/active_record_locking.rb4
-rw-r--r--config/initializers/active_record_table_definition.rb5
-rw-r--r--config/initializers/additional_headers_interceptor.rb1
-rw-r--r--config/initializers/disable_email_interceptor.rb5
-rw-r--r--config/initializers/email_template_interceptor.rb2
-rw-r--r--config/routes/import.rb6
-rw-r--r--danger/changelog/Dangerfile68
-rw-r--r--danger/changes_size/Dangerfile17
-rw-r--r--danger/database/Dangerfile83
-rw-r--r--danger/gemfile/Dangerfile24
-rw-r--r--danger/metadata/Dangerfile25
-rw-r--r--danger/specs/Dangerfile18
-rw-r--r--db/fixtures/development/12_snippets.rb2
-rw-r--r--db/fixtures/development/19_environments.rb4
-rw-r--r--db/migrate/20160705054938_add_protected_branches_push_access.rb2
-rw-r--r--db/migrate/20160705054952_add_protected_branches_merge_access.rb2
-rw-r--r--db/migrate/20180608091413_add_group_to_todos.rb32
-rw-r--r--db/migrate/20180625113853_create_import_export_uploads.rb16
-rw-r--r--db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb18
-rw-r--r--db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb26
-rw-r--r--db/schema.rb19
-rw-r--r--doc/administration/index.md1
-rw-r--r--doc/administration/job_artifacts.md13
-rw-r--r--doc/administration/job_traces.md10
-rw-r--r--doc/administration/pages/index.md22
-rw-r--r--doc/administration/raketasks/project_import_export.md7
-rw-r--r--doc/api/README.md32
-rw-r--r--doc/api/groups.md25
-rw-r--r--doc/api/merge_requests.md1
-rw-r--r--doc/api/notes.md2
-rw-r--r--doc/api/pipelines.md4
-rw-r--r--doc/api/project_import_export.md28
-rw-r--r--doc/api/projects.md43
-rw-r--r--doc/api/settings.md2
-rw-r--r--doc/api/todos.md1
-rw-r--r--doc/api/users.md30
-rw-r--r--doc/ci/examples/README.md4
-rw-r--r--doc/ci/examples/code_climate.md53
-rw-r--r--doc/ci/examples/code_quality.md49
-rw-r--r--doc/ci/variables/README.md6
-rw-r--r--doc/development/architecture.md2
-rw-r--r--doc/development/emails.md4
-rw-r--r--doc/development/fe_guide/development_process.md6
-rw-r--r--doc/development/fe_guide/img/vue_arch.pngbin9848 -> 0 bytes
-rw-r--r--doc/development/fe_guide/vue.md241
-rw-r--r--doc/development/i18n/externalization.md12
-rw-r--r--doc/development/i18n/proofreader.md1
-rw-r--r--doc/development/licensing.md22
-rw-r--r--doc/development/sql.md42
-rw-r--r--doc/development/testing_guide/frontend_testing.md4
-rw-r--r--doc/development/what_requires_downtime.md4
-rw-r--r--doc/gitlab-basics/create-project.md3
-rw-r--r--doc/gitlab-basics/img/create_new_project_info.pngbin75470 -> 77490 bytes
-rw-r--r--doc/install/installation.md15
-rw-r--r--doc/integration/bitbucket.md35
-rw-r--r--doc/integration/saml.md172
-rw-r--r--doc/public_access/public_access.md6
-rw-r--r--doc/raketasks/user_management.md2
-rw-r--r--doc/ssh/README.md2
-rw-r--r--doc/topics/autodevops/index.md4
-rw-r--r--doc/update/10.6-to-10.7.md2
-rw-r--r--doc/update/11.0-to-11.1.md27
-rw-r--r--doc/user/admin_area/settings/third_party_offers.md6
-rw-r--r--doc/user/markdown.md4
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md2
-rw-r--r--doc/user/project/clusters/index.md14
-rw-r--r--doc/user/project/import/img/manifest_status.pngbin0 -> 34878 bytes
-rw-r--r--doc/user/project/import/img/manifest_upload.pngbin0 -> 12079 bytes
-rw-r--r--doc/user/project/import/index.md1
-rw-r--r--doc/user/project/import/manifest.md49
-rw-r--r--doc/user/project/issue_board.md10
-rw-r--r--doc/user/project/merge_requests/index.md4
-rw-r--r--doc/user/project/wiki/index.md7
-rw-r--r--doc/workflow/todos.md1
-rw-r--r--lib/additional_email_headers_interceptor.rb6
-rw-r--r--lib/api/commits.rb16
-rw-r--r--lib/api/deploy_keys.rb6
-rw-r--r--lib/api/entities.rb34
-rw-r--r--lib/api/environments.rb3
-rw-r--r--lib/api/groups.rb3
-rw-r--r--lib/api/helpers.rb8
-rw-r--r--lib/api/helpers/notes_helpers.rb2
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/project_export.rb10
-rw-r--r--lib/api/project_hooks.rb2
-rw-r--r--lib/api/projects.rb5
-rw-r--r--lib/api/runners.rb2
-rw-r--r--lib/api/services.rb4
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb11
-rw-r--r--lib/banzai/filter/markdown_engines/common_mark.rb2
-rw-r--r--lib/declarative_policy.rb12
-rw-r--r--lib/declarative_policy/base.rb10
-rw-r--r--lib/declarative_policy/delegate_dsl.rb6
-rw-r--r--lib/declarative_policy/policy_dsl.rb14
-rw-r--r--lib/declarative_policy/preferred_scope.rb10
-rw-r--r--lib/declarative_policy/rule.rb4
-rw-r--r--lib/declarative_policy/rule_dsl.rb10
-rw-r--r--lib/declarative_policy/runner.rb2
-rw-r--r--lib/disable_email_interceptor.rb7
-rw-r--r--lib/email_template_interceptor.rb11
-rw-r--r--lib/extracts_path.rb5
-rw-r--r--lib/gitaly/server.rb2
-rw-r--r--lib/gitlab.rb4
-rw-r--r--lib/gitlab/access.rb24
-rw-r--r--lib/gitlab/auth/o_auth/user.rb2
-rw-r--r--lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb1
-rw-r--r--lib/gitlab/background_migration/archive_legacy_traces.rb1
-rw-r--r--lib/gitlab/background_migration/create_fork_network_memberships_range.rb1
-rw-r--r--lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb1
-rw-r--r--lib/gitlab/background_migration/delete_diff_files.rb87
-rw-r--r--lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb1
-rw-r--r--lib/gitlab/background_migration/fill_file_store_job_artifact.rb1
-rw-r--r--lib/gitlab/background_migration/fill_file_store_lfs_object.rb1
-rw-r--r--lib/gitlab/background_migration/fill_store_upload.rb1
-rw-r--r--lib/gitlab/background_migration/fix_cross_project_label_links.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_build_stage.rb1
-rw-r--r--lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb1
-rw-r--r--lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb1
-rw-r--r--lib/gitlab/background_migration/move_personal_snippet_files.rb1
-rw-r--r--lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb1
-rw-r--r--lib/gitlab/background_migration/populate_fork_networks_range.rb2
-rw-r--r--lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb3
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads.rb2
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb4
-rw-r--r--lib/gitlab/background_migration/prepare_untracked_uploads.rb2
-rw-r--r--lib/gitlab/background_migration/schedule_diff_files_deletion.rb44
-rw-r--r--lib/gitlab/ci/ansi2html.rb118
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb19
-rw-r--r--lib/gitlab/ci/config/entry/configurable.rb2
-rw-r--r--lib/gitlab/ci/trace/http_io.rb197
-rw-r--r--lib/gitlab/ci/trace/section_parser.rb12
-rw-r--r--lib/gitlab/current_settings.rb2
-rw-r--r--lib/gitlab/database.rb15
-rw-r--r--lib/gitlab/diff/image_point.rb6
-rw-r--r--lib/gitlab/diff/inline_diff.rb4
-rw-r--r--lib/gitlab/ee_compat_check.rb28
-rw-r--r--lib/gitlab/email/hook/additional_headers_interceptor.rb12
-rw-r--r--lib/gitlab/email/hook/delivery_metrics_observer.rb31
-rw-r--r--lib/gitlab/email/hook/disable_email_interceptor.rb13
-rw-r--r--lib/gitlab/email/hook/email_template_interceptor.rb18
-rw-r--r--lib/gitlab/encoding_helper.rb12
-rw-r--r--lib/gitlab/exclusive_lease_helpers.rb29
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb22
-rw-r--r--lib/gitlab/git/blob.rb108
-rw-r--r--lib/gitlab/git/commit.rb32
-rw-r--r--lib/gitlab/git/conflict/resolver.rb69
-rw-r--r--lib/gitlab/git/diff_collection.rb12
-rw-r--r--lib/gitlab/git/popen.rb18
-rw-r--r--lib/gitlab/git/repository.rb617
-rw-r--r--lib/gitlab/git/rev_list.rb33
-rwxr-xr-xlib/gitlab/git/support/format-git-cat-file-input21
-rw-r--r--lib/gitlab/git_access.rb20
-rw-r--r--lib/gitlab/gitaly_client.rb4
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb4
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb10
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb16
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb49
-rw-r--r--lib/gitlab/gitaly_client/server_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/storage_settings.rb4
-rw-r--r--lib/gitlab/google_code_import/importer.rb22
-rw-r--r--lib/gitlab/gpg/commit.rb2
-rw-r--r--lib/gitlab/http_io.rb193
-rw-r--r--lib/gitlab/import_export.rb4
-rw-r--r--lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb17
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb26
-rw-r--r--lib/gitlab/import_export/avatar_saver.rb11
-rw-r--r--lib/gitlab/import_export/members_mapper.rb2
-rw-r--r--lib/gitlab/import_export/saver.rb31
-rw-r--r--lib/gitlab/import_export/uploads_manager.rb101
-rw-r--r--lib/gitlab/import_export/uploads_restorer.rb21
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb15
-rw-r--r--lib/gitlab/import_sources.rb3
-rw-r--r--lib/gitlab/kubernetes.rb7
-rw-r--r--lib/gitlab/kubernetes/helm/base_command.rb2
-rw-r--r--lib/gitlab/logger.rb8
-rw-r--r--lib/gitlab/mail_room.rb2
-rw-r--r--lib/gitlab/manifest_import/manifest.rb81
-rw-r--r--lib/gitlab/manifest_import/project_creator.rb41
-rw-r--r--lib/gitlab/metrics/influx_db.rb2
-rw-r--r--lib/gitlab/metrics/method_call.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb2
-rw-r--r--lib/gitlab/middleware/multipart.rb2
-rw-r--r--lib/gitlab/middleware/read_only/controller.rb1
-rw-r--r--lib/gitlab/popen.rb9
-rw-r--r--lib/gitlab/project_authorizations/with_nested_groups.rb2
-rw-r--r--lib/gitlab/project_authorizations/without_nested_groups.rb2
-rw-r--r--lib/gitlab/shell.rb59
-rw-r--r--lib/gitlab/url_sanitizer.rb2
-rw-r--r--lib/gitlab/workhorse.rb63
-rw-r--r--lib/rouge/formatters/html_gitlab.rb2
-rw-r--r--lib/tasks/gettext.rake36
-rw-r--r--lib/tasks/gitlab/bulk_add_permission.rake4
-rw-r--r--lib/tasks/gitlab/uploads/migrate.rake2
-rw-r--r--lib/uploaded_file.rb5
-rw-r--r--locale/gitlab.pot659
-rw-r--r--package.json25
-rw-r--r--qa/README.md2
-rw-r--r--qa/qa.rb41
-rw-r--r--qa/qa/factory/repository/project_push.rb2
-rw-r--r--qa/qa/factory/repository/push.rb16
-rw-r--r--qa/qa/factory/resource/branch.rb21
-rw-r--r--qa/qa/factory/resource/file.rb34
-rw-r--r--qa/qa/factory/resource/fork.rb24
-rw-r--r--qa/qa/factory/resource/group.rb2
-rw-r--r--qa/qa/factory/resource/kubernetes_cluster.rb3
-rw-r--r--qa/qa/factory/resource/merge_request.rb11
-rw-r--r--qa/qa/factory/resource/merge_request_from_fork.rb24
-rw-r--r--qa/qa/factory/resource/project.rb19
-rw-r--r--qa/qa/factory/resource/project_imported_from_github.rb37
-rw-r--r--qa/qa/factory/resource/project_milestone.rb36
-rw-r--r--qa/qa/factory/resource/sandbox.rb4
-rw-r--r--qa/qa/factory/resource/user.rb34
-rw-r--r--qa/qa/page/component/select2.rb11
-rw-r--r--qa/qa/page/file/form.rb40
-rw-r--r--qa/qa/page/file/shared/commit_message.rb19
-rw-r--r--qa/qa/page/file/show.rb30
-rw-r--r--qa/qa/page/issuable/sidebar.rb24
-rw-r--r--qa/qa/page/layout/banner.rb17
-rw-r--r--qa/qa/page/main/login.rb19
-rw-r--r--qa/qa/page/main/oauth.rb2
-rw-r--r--qa/qa/page/main/sign_up.rb27
-rw-r--r--qa/qa/page/menu/main.rb14
-rw-r--r--qa/qa/page/menu/side.rb15
-rw-r--r--qa/qa/page/merge_request/new.rb15
-rw-r--r--qa/qa/page/project/fork/new.rb17
-rw-r--r--qa/qa/page/project/import/github.rb66
-rw-r--r--qa/qa/page/project/milestone/index.rb17
-rw-r--r--qa/qa/page/project/milestone/new.rb27
-rw-r--r--qa/qa/page/project/new.rb25
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb23
-rw-r--r--qa/qa/page/project/show.rb27
-rw-r--r--qa/qa/page/view.rb4
-rw-r--r--qa/qa/runtime/browser.rb4
-rw-r--r--qa/qa/runtime/env.rb11
-rw-r--r--qa/qa/runtime/namespace.rb4
-rw-r--r--qa/qa/runtime/release.rb2
-rw-r--r--qa/qa/scenario/test/integration/github.rb18
-rw-r--r--qa/qa/specs/features/login/standard_spec.rb15
-rw-r--r--qa/qa/specs/features/merge_request/create_spec.rb23
-rw-r--r--qa/qa/specs/features/project/file_spec.rb54
-rw-r--r--qa/qa/specs/features/project/fork_project_spec.rb23
-rw-r--r--qa/qa/specs/features/project/import_from_github_spec.rb106
-rw-r--r--qa/qa/specs/features/repository/protected_branches_spec.rb29
-rw-r--r--qa/spec/runtime/env_spec.rb23
-rw-r--r--rubocop/cop/line_break_around_conditional_block.rb2
-rw-r--r--scripts/frontend/prettier.js4
-rw-r--r--spec/bin/changelog_spec.rb14
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb20
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb2
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb2
-rw-r--r--spec/controllers/concerns/group_tree_spec.rb11
-rw-r--r--spec/controllers/dashboard/groups_controller_spec.rb4
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/dashboard_controller_spec.rb2
-rw-r--r--spec/controllers/groups/boards_controller_spec.rb2
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/groups/runners_controller_spec.rb2
-rw-r--r--spec/controllers/groups/settings/ci_cd_controller_spec.rb2
-rw-r--r--spec/controllers/groups/variables_controller_spec.rb2
-rw-r--r--spec/controllers/groups_controller_spec.rb6
-rw-r--r--spec/controllers/metrics_controller_spec.rb49
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/projects/badges_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blame_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb12
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb2
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb2
-rw-r--r--spec/controllers/projects/clusters/applications_controller_spec.rb4
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb36
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb2
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb2
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb2
-rw-r--r--spec/controllers/projects/cycle_analytics_controller_spec.rb2
-rw-r--r--spec/controllers/projects/deploy_keys_controller_spec.rb2
-rw-r--r--spec/controllers/projects/deployments_controller_spec.rb2
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb2
-rw-r--r--spec/controllers/projects/find_file_controller_spec.rb2
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb4
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb10
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb4
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb14
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb2
-rw-r--r--spec/controllers/projects/mattermosts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb4
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pages_domains_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb28
-rw-r--r--spec/controllers/projects/pipelines_settings_controller_spec.rb2
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb20
-rw-r--r--spec/controllers/projects/prometheus/metrics_controller_spec.rb2
-rw-r--r--spec/controllers/projects/protected_branches_controller_spec.rb8
-rw-r--r--spec/controllers/projects/protected_tags_controller_spec.rb2
-rw-r--r--spec/controllers/projects/runners_controller_spec.rb2
-rw-r--r--spec/controllers/projects/services_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb6
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/repository_controller_spec.rb2
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb6
-rw-r--r--spec/controllers/projects/templates_controller_spec.rb2
-rw-r--r--spec/controllers/projects/todos_controller_spec.rb133
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb2
-rw-r--r--spec/controllers/projects/variables_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb62
-rw-r--r--spec/controllers/uploads_controller_spec.rb8
-rw-r--r--spec/factories/ci/build_trace_chunks.rb48
-rw-r--r--spec/factories/group_members.rb2
-rw-r--r--spec/factories/import_export_uploads.rb5
-rw-r--r--spec/factories/issues.rb2
-rw-r--r--spec/factories/merge_requests.rb2
-rw-r--r--spec/factories/project_members.rb4
-rw-r--r--spec/factories/projects.rb18
-rw-r--r--spec/factories/protected_branches.rb8
-rw-r--r--spec/factories/protected_tags.rb6
-rw-r--r--spec/factories/todos.rb4
-rw-r--r--spec/factories/uploads.rb7
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb4
-rw-r--r--spec/features/admin/admin_settings_spec.rb10
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb4
-rw-r--r--spec/features/atom/dashboard_spec.rb2
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb4
-rw-r--r--spec/features/boards/issue_ordering_spec.rb2
-rw-r--r--spec/features/boards/modal_filter_spec.rb2
-rw-r--r--spec/features/boards/new_issue_spec.rb2
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/boards/sub_group_project_spec.rb2
-rw-r--r--spec/features/commits_spec.rb8
-rw-r--r--spec/features/cycle_analytics_spec.rb6
-rw-r--r--spec/features/dashboard/activity_spec.rb2
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb4
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb4
-rw-r--r--spec/features/dashboard/issues_filter_spec.rb2
-rw-r--r--spec/features/dashboard/issues_spec.rb2
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb4
-rw-r--r--spec/features/dashboard/milestone_tabs_spec.rb2
-rw-r--r--spec/features/dashboard/milestones_spec.rb2
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb2
-rw-r--r--spec/features/dashboard/projects_spec.rb6
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb2
-rw-r--r--spec/features/discussion_comments/commit_spec.rb2
-rw-r--r--spec/features/discussion_comments/issue_spec.rb2
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb2
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb2
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/group_variables_spec.rb2
-rw-r--r--spec/features/groups/activity_spec.rb2
-rw-r--r--spec/features/groups/empty_states_spec.rb2
-rw-r--r--spec/features/groups/issues_spec.rb4
-rw-r--r--spec/features/groups/members/filter_members_spec.rb2
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb4
-rw-r--r--spec/features/groups/members/request_access_spec.rb2
-rw-r--r--spec/features/groups/milestone_spec.rb2
-rw-r--r--spec/features/groups/milestones_sorting_spec.rb2
-rw-r--r--spec/features/groups_spec.rb6
-rw-r--r--spec/features/ics/dashboard_issues_spec.rb2
-rw-r--r--spec/features/ide_spec.rb2
-rw-r--r--spec/features/import/manifest_import_spec.rb51
-rw-r--r--spec/features/invites_spec.rb2
-rw-r--r--spec/features/issuables/close_reopen_report_toggle_spec.rb4
-rw-r--r--spec/features/issues/award_emoji_spec.rb2
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb4
-rw-r--r--spec/features/issues/form_spec.rb6
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb2
-rw-r--r--spec/features/issues/spam_issues_spec.rb2
-rw-r--r--spec/features/issues/todo_spec.rb2
-rw-r--r--spec/features/issues/update_issues_spec.rb2
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb6
-rw-r--r--spec/features/issues_spec.rb2
-rw-r--r--spec/features/markdown/gitlab_flavored_markdown_spec.rb2
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb2
-rw-r--r--spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb2
-rw-r--r--spec/features/merge_request/user_customizes_merge_commit_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_immediately_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb (renamed from spec/features/merge_request/user_cherry_picks_spec.rb)24
-rw-r--r--spec/features/merge_request/user_sees_closing_issues_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_deleted_target_branch_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_empty_state_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb6
-rw-r--r--spec/features/merge_request/user_sees_pipelines_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_wip_help_message_spec.rb2
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb2
-rw-r--r--spec/features/merge_request/user_toggles_whitespace_changes_spec.rb2
-rw-r--r--spec/features/merge_request/user_uses_slash_commands_spec.rb4
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb2
-rw-r--r--spec/features/merge_requests/user_squashes_merge_request_spec.rb2
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/milestones/user_edits_milestone_spec.rb22
-rw-r--r--spec/features/profiles/keys_spec.rb14
-rw-r--r--spec/features/profiles/password_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_spec.rb2
-rw-r--r--spec/features/project_variables_spec.rb2
-rw-r--r--spec/features/projects/actve_tabs_spec.rb2
-rw-r--r--spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb2
-rw-r--r--spec/features/projects/badges/list_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb12
-rw-r--r--spec/features/projects/blobs/edit_spec.rb4
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb4
-rw-r--r--spec/features/projects/branches/new_branch_ref_dropdown_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb4
-rw-r--r--spec/features/projects/clusters/applications_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb2
-rw-r--r--spec/features/projects/commit/builds_spec.rb2
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb2
-rw-r--r--spec/features/projects/commit/comments/user_adds_comment_spec.rb2
-rw-r--r--spec/features/projects/commit/diff_notes_spec.rb2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb2
-rw-r--r--spec/features/projects/compare_spec.rb2
-rw-r--r--spec/features/projects/deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_spec.rb17
-rw-r--r--spec/features/projects/environments/environments_spec.rb16
-rw-r--r--spec/features/projects/features_visibility_spec.rb4
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb10
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb6
-rw-r--r--spec/features/projects/files/template_selector_menu_spec.rb2
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb3
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_deletes_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_find_file_spec.rb2
-rw-r--r--spec/features/projects/files/user_reads_pipeline_status_spec.rb2
-rw-r--r--spec/features/projects/files/user_replaces_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_uploads_files_spec.rb2
-rw-r--r--spec/features/projects/fork_spec.rb4
-rw-r--r--spec/features/projects/graph_spec.rb2
-rw-r--r--spec/features/projects/hook_logs/user_reads_log_spec.rb2
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb1
-rw-r--r--spec/features/projects/import_export/namespace_export_file_spec.rb1
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin343136 -> 3368 bytes
-rw-r--r--spec/features/projects/issuable_templates_spec.rb2
-rw-r--r--spec/features/projects/jobs/permissions_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb10
-rw-r--r--spec/features/projects/labels/user_creates_labels_spec.rb4
-rw-r--r--spec/features/projects/labels/user_edits_labels_spec.rb2
-rw-r--r--spec/features/projects/labels/user_removes_labels_spec.rb2
-rw-r--r--spec/features/projects/members/anonymous_user_sees_members_spec.rb2
-rw-r--r--spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb4
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb2
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb8
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb4
-rw-r--r--spec/features/projects/members/share_with_group_spec.rb30
-rw-r--r--spec/features/projects/members/sorting_spec.rb24
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb6
-rw-r--r--spec/features/projects/merge_requests/user_closes_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_commit_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_diff_spec.rb8
-rw-r--r--spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_creates_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_edits_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_manages_subscription_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/projects/milestones/user_interacts_with_labels_spec.rb2
-rw-r--r--spec/features/projects/new_project_spec.rb29
-rw-r--r--spec/features/projects/pages_spec.rb2
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb4
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb4
-rw-r--r--spec/features/projects/remote_mirror_spec.rb6
-rw-r--r--spec/features/projects/services/user_activates_asana_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_assembla_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_emails_on_push_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_flowdock_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_hipchat_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_irker_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_jira_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_packagist_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_pivotaltracker_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_prometheus_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_pushover_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_slack_notifications_spec.rb4
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb2
-rw-r--r--spec/features/projects/services/user_views_services_spec.rb2
-rw-r--r--spec/features/projects/settings/forked_project_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/lfs_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/project_badges_spec.rb2
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb6
-rw-r--r--spec/features/projects/settings/user_archives_project_spec.rb2
-rw-r--r--spec/features/projects/settings/user_changes_avatar_spec.rb2
-rw-r--r--spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb4
-rw-r--r--spec/features/projects/settings/user_manages_group_links_spec.rb4
-rw-r--r--spec/features/projects/settings/user_manages_project_members_spec.rb6
-rw-r--r--spec/features/projects/settings/user_transfers_a_project_spec.rb9
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb8
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb32
-rw-r--r--spec/features/projects/show/user_sees_deletion_failure_message_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb8
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/show_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_comments_on_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_deletes_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_updates_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/user_views_snippets_spec.rb2
-rw-r--r--spec/features/projects/sub_group_issuables_spec.rb2
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb10
-rw-r--r--spec/features/projects/tree/create_file_spec.rb6
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb2
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb6
-rw-r--r--spec/features/projects/user_uses_shortcuts_spec.rb2
-rw-r--r--spec/features/projects/user_views_empty_project_spec.rb4
-rw-r--r--spec/features/projects/view_on_env_spec.rb2
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb9
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb48
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb2
-rw-r--r--spec/features/projects_spec.rb12
-rw-r--r--spec/features/protected_branches_spec.rb4
-rw-r--r--spec/features/reportable_note/commit_spec.rb2
-rw-r--r--spec/features/reportable_note/issue_spec.rb2
-rw-r--r--spec/features/reportable_note/merge_request_spec.rb2
-rw-r--r--spec/features/reportable_note/snippets_spec.rb2
-rw-r--r--spec/features/runners_spec.rb14
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_issues_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_merge_requests_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_milestones_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_wiki_pages_spec.rb2
-rw-r--r--spec/features/security/group/internal_access_spec.rb10
-rw-r--r--spec/features/security/group/private_access_spec.rb10
-rw-r--r--spec/features/security/group/public_access_spec.rb10
-rw-r--r--spec/features/security/project/internal_access_spec.rb68
-rw-r--r--spec/features/security/project/private_access_spec.rb62
-rw-r--r--spec/features/security/project/public_access_spec.rb68
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb12
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb8
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb16
-rw-r--r--spec/features/signed_commits_spec.rb8
-rw-r--r--spec/features/snippets/show_spec.rb20
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb4
-rw-r--r--spec/features/tags/master_deletes_tag_spec.rb4
-rw-r--r--spec/features/tags/master_updates_tag_spec.rb4
-rw-r--r--spec/features/tags/master_views_tags_spec.rb4
-rw-r--r--spec/features/task_lists_spec.rb2
-rw-r--r--spec/features/triggers_spec.rb4
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb25
-rw-r--r--spec/features/users/show_spec.rb24
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb6
-rw-r--r--spec/finders/access_requests_finder_spec.rb4
-rw-r--r--spec/finders/admin/projects_finder_spec.rb2
-rw-r--r--spec/finders/concerns/finder_with_cross_project_access_spec.rb2
-rw-r--r--spec/finders/contributed_projects_finder_spec.rb4
-rw-r--r--spec/finders/environments_finder_spec.rb2
-rw-r--r--spec/finders/group_members_finder_spec.rb24
-rw-r--r--spec/finders/group_projects_finder_spec.rb18
-rw-r--r--spec/finders/issues_finder_spec.rb2
-rw-r--r--spec/finders/joined_groups_finder_spec.rb12
-rw-r--r--spec/finders/members_finder_spec.rb12
-rw-r--r--spec/finders/merge_requests_finder_spec.rb4
-rw-r--r--spec/finders/move_to_project_finder_spec.rb20
-rw-r--r--spec/finders/notes_finder_spec.rb2
-rw-r--r--spec/finders/personal_projects_finder_spec.rb2
-rw-r--r--spec/finders/projects_finder_spec.rb4
-rw-r--r--spec/finders/todos_finder_spec.rb64
-rw-r--r--spec/fixtures/aosp_manifest.xml685
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json2
-rw-r--r--spec/fixtures/project_export.tar.gzbin0 -> 343091 bytes
-rw-r--r--spec/fixtures/trace/sample_trace32
-rw-r--r--spec/helpers/blob_helper_spec.rb4
-rw-r--r--spec/helpers/issuables_helper_spec.rb22
-rw-r--r--spec/helpers/markup_helper_spec.rb23
-rw-r--r--spec/helpers/namespaces_helper_spec.rb10
-rw-r--r--spec/helpers/notes_helper_spec.rb16
-rw-r--r--spec/helpers/projects_helper_spec.rb27
-rw-r--r--spec/helpers/submodule_helper_spec.rb21
-rw-r--r--spec/helpers/time_helper_spec.rb10
-rw-r--r--spec/javascripts/diffs/components/changed_files_spec.js91
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js146
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js6
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js2
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js2
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js44
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js194
-rw-r--r--spec/javascripts/environments/environment_item_spec.js2
-rw-r--r--spec/javascripts/environments/environment_rollback_spec.js4
-rw-r--r--spec/javascripts/environments/environment_stop_spec.js12
-rw-r--r--spec/javascripts/environments/mock_data.js12
-rw-r--r--spec/javascripts/fixtures/commit.rb2
-rw-r--r--spec/javascripts/fixtures/groups.rb2
-rw-r--r--spec/javascripts/fixtures/projects.rb2
-rw-r--r--spec/javascripts/frequent_items/components/app_spec.js251
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js (renamed from spec/javascripts/projects_dropdown/components/projects_list_item_spec.js)34
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_list_spec.js84
-rw-r--r--spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js77
-rw-r--r--spec/javascripts/frequent_items/mock_data.js168
-rw-r--r--spec/javascripts/frequent_items/store/actions_spec.js225
-rw-r--r--spec/javascripts/frequent_items/store/getters_spec.js24
-rw-r--r--spec/javascripts/frequent_items/store/mutations_spec.js117
-rw-r--r--spec/javascripts/frequent_items/utils_spec.js89
-rw-r--r--spec/javascripts/helpers/vuex_action_helper.js114
-rw-r--r--spec/javascripts/helpers/vuex_action_helper_spec.js141
-rw-r--r--spec/javascripts/helpers/wait_for_promises.js1
-rw-r--r--spec/javascripts/ide/components/ide_status_bar_spec.js26
-rw-r--r--spec/javascripts/ide/components/merge_requests/info_spec.js51
-rw-r--r--spec/javascripts/ide/components/new_dropdown/button_spec.js49
-rw-r--r--spec/javascripts/ide/components/new_dropdown/index_spec.js46
-rw-r--r--spec/javascripts/ide/components/new_dropdown/modal_spec.js36
-rw-r--r--spec/javascripts/ide/components/new_dropdown/upload_spec.js3
-rw-r--r--spec/javascripts/ide/components/panes/right_spec.js72
-rw-r--r--spec/javascripts/ide/mock_data.js3
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js5
-rw-r--r--spec/javascripts/ide/stores/actions/merge_request_spec.js4
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js15
-rw-r--r--spec/javascripts/ide/stores/actions/tree_spec.js7
-rw-r--r--spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js25
-rw-r--r--spec/javascripts/ide/stores/modules/pipelines/actions_spec.js6
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js201
-rw-r--r--spec/javascripts/job_spec.js27
-rw-r--r--spec/javascripts/notes/mock_data.js1
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js13
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js14
-rw-r--r--spec/javascripts/performance_bar/components/performance_bar_app_spec.js61
-rw-r--r--spec/javascripts/performance_bar/index_spec.js83
-rw-r--r--spec/javascripts/profile/add_ssh_key_validation_spec.js69
-rw-r--r--spec/javascripts/projects_dropdown/components/app_spec.js349
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js72
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_search_spec.js84
-rw-r--r--spec/javascripts/projects_dropdown/components/search_spec.js100
-rw-r--r--spec/javascripts/projects_dropdown/mock_data.js96
-rw-r--r--spec/javascripts/projects_dropdown/service/projects_service_spec.js179
-rw-r--r--spec/javascripts/projects_dropdown/store/projects_store_spec.js41
-rw-r--r--spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js243
-rw-r--r--spec/javascripts/sidebar/todo_spec.js158
-rw-r--r--spec/javascripts/smart_interval_spec.js201
-rw-r--r--spec/javascripts/test_bundle.js15
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js43
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js2
-rw-r--r--spec/javascripts/vue_shared/components/memory_graph_spec.js2
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb58
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb2
-rw-r--r--spec/lib/extracts_path_spec.rb26
-rw-r--r--spec/lib/gitlab/background_migration/delete_diff_files_spec.rb62
-rw-r--r--spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb43
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/common_spec.rb2
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb18
-rw-r--r--spec/lib/gitlab/cycle_analytics/permissions_spec.rb4
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb29
-rw-r--r--spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb (renamed from spec/lib/additional_email_headers_interceptor_spec.rb)2
-rw-r--r--spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb35
-rw-r--r--spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb (renamed from spec/lib/disable_email_interceptor_spec.rb)2
-rw-r--r--spec/lib/gitlab/exclusive_lease_helpers_spec.rb76
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb171
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb8
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb9
-rw-r--r--spec/lib/gitlab/git/index_spec.rb16
-rw-r--r--spec/lib/gitlab/git/popen_spec.rb30
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb370
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb61
-rw-r--r--spec/lib/gitlab/git_access_spec.rb64
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb41
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb6
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb4
-rw-r--r--spec/lib/gitlab/http_io_spec.rb (renamed from spec/lib/gitlab/ci/trace/http_io_spec.rb)43
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb105
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb31
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb43
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb80
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb10
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb15
-rw-r--r--spec/lib/gitlab/manifest_import/manifest_spec.rb46
-rw-r--r--spec/lib/gitlab/manifest_import/project_creator_spec.rb33
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb27
-rw-r--r--spec/lib/gitlab/middleware/read_only_spec.rb44
-rw-r--r--spec/lib/gitlab/popen_spec.rb13
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb6
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb2
-rw-r--r--spec/lib/gitlab/sanitizers/svg_spec.rb4
-rw-r--r--spec/lib/gitlab/shell_spec.rb105
-rw-r--r--spec/lib/gitlab/slash_commands/issue_move_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_new_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_show_spec.rb2
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb1
-rw-r--r--spec/lib/gitlab/user_access_spec.rb40
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb152
-rw-r--r--spec/mailers/notify_spec.rb33
-rw-r--r--spec/migrations/enqueue_delete_diff_files_workers_spec.rb17
-rw-r--r--spec/migrations/issues_moved_to_id_foreign_key_spec.rb2
-rw-r--r--spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb6
-rw-r--r--spec/models/ci/build_metadata_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb71
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb446
-rw-r--r--spec/models/ci/build_trace_chunks/database_spec.rb105
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb146
-rw-r--r--spec/models/ci/build_trace_chunks/redis_spec.rb132
-rw-r--r--spec/models/ci/job_artifact_spec.rb4
-rw-r--r--spec/models/concerns/batch_destroy_dependent_associations_spec.rb4
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb16
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb2
-rw-r--r--spec/models/concerns/issuable_spec.rb8
-rw-r--r--spec/models/concerns/mentionable_spec.rb6
-rw-r--r--spec/models/concerns/protected_ref_access_spec.rb10
-rw-r--r--spec/models/concerns/resolvable_discussion_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb2
-rw-r--r--spec/models/deployment_spec.rb18
-rw-r--r--spec/models/environment_spec.rb19
-rw-r--r--spec/models/group_spec.rb52
-rw-r--r--spec/models/hooks/system_hook_spec.rb8
-rw-r--r--spec/models/import_export_upload_spec.rb25
-rw-r--r--spec/models/issue_spec.rb2
-rw-r--r--spec/models/lfs_file_lock_spec.rb12
-rw-r--r--spec/models/member_spec.rb86
-rw-r--r--spec/models/members/group_member_spec.rb2
-rw-r--r--spec/models/members/project_member_spec.rb10
-rw-r--r--spec/models/merge_request_spec.rb10
-rw-r--r--spec/models/namespace_spec.rb4
-rw-r--r--spec/models/note_spec.rb6
-rw-r--r--spec/models/notification_setting_spec.rb8
-rw-r--r--spec/models/project_authorization_spec.rb6
-rw-r--r--spec/models/project_feature_spec.rb2
-rw-r--r--spec/models/project_spec.rb52
-rw-r--r--spec/models/project_team_spec.rb108
-rw-r--r--spec/models/project_wiki_spec.rb26
-rw-r--r--spec/models/protected_branch/merge_access_level_spec.rb2
-rw-r--r--spec/models/protected_branch/push_access_level_spec.rb2
-rw-r--r--spec/models/remote_mirror_spec.rb8
-rw-r--r--spec/models/repository_spec.rb51
-rw-r--r--spec/models/route_spec.rb14
-rw-r--r--spec/models/service_spec.rb2
-rw-r--r--spec/models/todo_spec.rb1
-rw-r--r--spec/models/user_spec.rb106
-rw-r--r--spec/policies/ci/build_policy_spec.rb10
-rw-r--r--spec/policies/ci/pipeline_schedule_policy_spec.rb8
-rw-r--r--spec/policies/ci/trigger_policy_spec.rb12
-rw-r--r--spec/policies/clusters/cluster_policy_spec.rb4
-rw-r--r--spec/policies/deploy_key_policy_spec.rb2
-rw-r--r--spec/policies/deploy_token_policy_spec.rb12
-rw-r--r--spec/policies/environment_policy_spec.rb106
-rw-r--r--spec/policies/global_policy_spec.rb4
-rw-r--r--spec/policies/group_policy_spec.rb42
-rw-r--r--spec/policies/project_policy_spec.rb30
-rw-r--r--spec/policies/protected_branch_policy_spec.rb4
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb6
-rw-r--r--spec/presenters/project_presenter_spec.rb8
-rw-r--r--spec/requests/api/access_requests_spec.rb40
-rw-r--r--spec/requests/api/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/badges_spec.rb54
-rw-r--r--spec/requests/api/branches_spec.rb16
-rw-r--r--spec/requests/api/commits_spec.rb64
-rw-r--r--spec/requests/api/deployments_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb6
-rw-r--r--spec/requests/api/group_variables_spec.rb10
-rw-r--r--spec/requests/api/groups_spec.rb35
-rw-r--r--spec/requests/api/helpers_spec.rb2
-rw-r--r--spec/requests/api/issues_spec.rb2
-rw-r--r--spec/requests/api/jobs_spec.rb10
-rw-r--r--spec/requests/api/labels_spec.rb2
-rw-r--r--spec/requests/api/members_spec.rb84
-rw-r--r--spec/requests/api/merge_request_diffs_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb8
-rw-r--r--spec/requests/api/namespaces_spec.rb12
-rw-r--r--spec/requests/api/notes_spec.rb6
-rw-r--r--spec/requests/api/pages_domains_spec.rb28
-rw-r--r--spec/requests/api/pipeline_schedules_spec.rb24
-rw-r--r--spec/requests/api/pipelines_spec.rb2
-rw-r--r--spec/requests/api/project_export_spec.rb59
-rw-r--r--spec/requests/api/project_hooks_spec.rb4
-rw-r--r--spec/requests/api/project_import_spec.rb6
-rw-r--r--spec/requests/api/projects_spec.rb67
-rw-r--r--spec/requests/api/protected_branches_spec.rb34
-rw-r--r--spec/requests/api/repositories_spec.rb2
-rw-r--r--spec/requests/api/runners_spec.rb71
-rw-r--r--spec/requests/api/tags_spec.rb20
-rw-r--r--spec/requests/api/triggers_spec.rb2
-rw-r--r--spec/requests/api/variables_spec.rb2
-rw-r--r--spec/requests/api/wikis_spec.rb62
-rw-r--r--spec/requests/git_http_spec.rb12
-rw-r--r--spec/requests/lfs_http_spec.rb14
-rw-r--r--spec/requests/lfs_locks_api_spec.rb6
-rw-r--r--spec/serializers/deploy_key_entity_spec.rb4
-rw-r--r--spec/serializers/environment_entity_spec.rb3
-rw-r--r--spec/serializers/environment_serializer_spec.rb10
-rw-r--r--spec/serializers/group_child_entity_spec.rb2
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb15
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb8
-rw-r--r--spec/services/ci/retry_build_service_spec.rb14
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb4
-rw-r--r--spec/services/ci/stop_environments_service_spec.rb2
-rw-r--r--spec/services/discussions/resolve_service_spec.rb2
-rw-r--r--spec/services/files/create_service_spec.rb2
-rw-r--r--spec/services/files/delete_service_spec.rb2
-rw-r--r--spec/services/files/multi_service_spec.rb2
-rw-r--r--spec/services/files/update_service_spec.rb2
-rw-r--r--spec/services/git_push_service_spec.rb12
-rw-r--r--spec/services/groups/update_service_spec.rb8
-rw-r--r--spec/services/import_export_clean_up_service_spec.rb19
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/issues/create_service_spec.rb16
-rw-r--r--spec/services/issues/reopen_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb8
-rw-r--r--spec/services/lfs/unlock_file_service_spec.rb6
-rw-r--r--spec/services/members/approve_access_request_service_spec.rb6
-rw-r--r--spec/services/members/create_service_spec.rb2
-rw-r--r--spec/services/members/destroy_service_spec.rb10
-rw-r--r--spec/services/members/update_service_spec.rb6
-rw-r--r--spec/services/merge_requests/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/conflicts/list_service_spec.rb24
-rw-r--r--spec/services/merge_requests/conflicts/resolve_service_spec.rb11
-rw-r--r--spec/services/merge_requests/create_service_spec.rb20
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb4
-rw-r--r--spec/services/merge_requests/post_merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb2
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb2
-rw-r--r--spec/services/merge_requests/update_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/milestones/create_service_spec.rb2
-rw-r--r--spec/services/milestones/destroy_service_spec.rb2
-rw-r--r--spec/services/milestones/promote_service_spec.rb2
-rw-r--r--spec/services/notes/create_service_spec.rb2
-rw-r--r--spec/services/notes/post_process_service_spec.rb2
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb20
-rw-r--r--spec/services/notes/update_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb108
-rw-r--r--spec/services/preview_markdown_service_spec.rb12
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb54
-rw-r--r--spec/services/projects/create_service_spec.rb4
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/services/projects/move_access_service_spec.rb12
-rw-r--r--spec/services/projects/move_project_authorizations_service_spec.rb8
-rw-r--r--spec/services/projects/move_project_group_links_service_spec.rb8
-rw-r--r--spec/services/projects/move_project_members_service_spec.rb8
-rw-r--r--spec/services/projects/overwrite_project_service_spec.rb8
-rw-r--r--spec/services/projects/transfer_service_spec.rb2
-rw-r--r--spec/services/projects/update_pages_service_spec.rb16
-rw-r--r--spec/services/protected_branches/create_service_spec.rb8
-rw-r--r--spec/services/protected_tags/create_service_spec.rb4
-rw-r--r--spec/services/reset_project_cache_service_spec.rb2
-rw-r--r--spec/services/search/global_service_spec.rb2
-rw-r--r--spec/services/search_service_spec.rb2
-rw-r--r--spec/services/system_hooks_service_spec.rb2
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb30
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/api/repositories_shared_context.rb2
-rw-r--r--spec/support/api/time_tracking_shared_examples.rb8
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb32
-rwxr-xr-xspec/support/generate-seed-repo-rb2
-rw-r--r--spec/support/helpers/jira_service_helper.rb2
-rw-r--r--spec/support/helpers/key_generator_helper.rb2
-rw-r--r--spec/support/helpers/markdown_feature.rb2
-rw-r--r--spec/support/helpers/stub_object_storage.rb5
-rw-r--r--spec/support/helpers/test_env.rb4
-rw-r--r--spec/support/http_io/http_io_helpers.rb60
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/matchers/access_matchers_for_controller.rb2
-rw-r--r--spec/support/matchers/disallow_request_matchers.rb15
-rw-r--r--spec/support/services/issuable_create_service_slash_commands_shared_examples.rb4
-rw-r--r--spec/support/shared_contexts/merge_requests_allowing_collaboration.rb15
-rw-r--r--spec/support/shared_examples/controllers/todos_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb14
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb2
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb2
-rw-r--r--spec/support/shared_examples/requests/api/notes.rb1
-rw-r--r--spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb18
-rw-r--r--spec/uploaders/file_uploader_spec.rb9
-rw-r--r--spec/uploaders/gitlab_uploader_spec.rb62
-rw-r--r--spec/uploaders/import_export_uploader_spec.rb20
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb37
-rw-r--r--spec/views/projects/imports/new.html.haml_spec.rb4
-rw-r--r--spec/views/projects/merge_requests/show.html.haml_spec.rb2
-rw-r--r--spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb6
-rw-r--r--spec/views/shared/notes/_form.html.haml_spec.rb2
-rw-r--r--spec/workers/concerns/waitable_worker_spec.rb4
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb13
-rw-r--r--spec/workers/merge_worker_spec.rb2
-rw-r--r--spec/workers/object_storage_upload_worker_spec.rb108
-rw-r--r--spec/workers/pipeline_schedule_worker_spec.rb2
-rw-r--r--spec/workers/process_commit_worker_spec.rb40
-rw-r--r--spec/workers/repository_check/batch_worker_spec.rb8
-rw-r--r--spec/workers/repository_check/dispatch_worker_spec.rb8
-rw-r--r--spec/workers/repository_import_worker_spec.rb4
-rw-r--r--spec/workers/repository_update_remote_mirror_worker_spec.rb10
-rw-r--r--spec/workers/stuck_import_jobs_worker_spec.rb4
-rw-r--r--vendor/gitignore/Autotools.gitignore3
-rw-r--r--vendor/gitignore/CraftCMS.gitignore5
-rw-r--r--vendor/gitignore/Delphi.gitignore2
-rw-r--r--vendor/gitignore/Eagle.gitignore1
-rw-r--r--vendor/gitignore/GWT.gitignore3
-rw-r--r--vendor/gitignore/Global/Backup.gitignore5
-rw-r--r--vendor/gitignore/Global/CodeKit.gitignore1
-rw-r--r--vendor/gitignore/Global/Eclipse.gitignore5
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore12
-rw-r--r--vendor/gitignore/Global/Matlab.gitignore15
-rw-r--r--vendor/gitignore/Global/Patch.gitignore2
-rw-r--r--vendor/gitignore/Global/SynopsysVCS.gitignore8
-rw-r--r--vendor/gitignore/Global/Vim.gitignore3
-rw-r--r--vendor/gitignore/LabVIEW.gitignore1
-rw-r--r--vendor/gitignore/Maven.gitignore4
-rw-r--r--vendor/gitignore/Node.gitignore6
-rw-r--r--vendor/gitignore/Objective-C.gitignore3
-rw-r--r--vendor/gitignore/Perl6.gitignore7
-rw-r--r--vendor/gitignore/Swift.gitignore3
-rw-r--r--vendor/gitignore/TeX.gitignore6
-rw-r--r--vendor/gitignore/Typo3.gitignore5
-rw-r--r--vendor/gitignore/Umbraco.gitignore2
-rw-r--r--vendor/gitignore/UnrealEngine.gitignore3
-rw-r--r--vendor/gitignore/VisualStudio.gitignore6
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml12
-rw-r--r--vendor/licenses.csv84
-rw-r--r--yarn.lock1360
1469 files changed, 17657 insertions, 11633 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000000..f1c41c9bb76
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+Dangerfile gitlab-language=ruby
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 49c4b059dbc..afe9da08495 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
.dedicated-runner: &dedicated-runner
retry: 1
@@ -86,7 +86,9 @@ stages:
.rails5: &rails5
allow_failure: true
only:
- - /rails5/
+ variables:
+ - $CI_COMMIT_REF_NAME =~ /rails5/
+ - $RAILS5_ENABLED
variables:
BUNDLE_GEMFILE: "Gemfile.rails5"
RAILS5: "true"
@@ -327,7 +329,7 @@ cloud-native-image:
cache: {}
script:
- gem install gitlab --no-ri --no-rdoc
- - ./trigger-build cng
+ - BUILD_TRIGGER_TOKEN=$CI_JOB_TOKEN scripts/trigger-build cng
only:
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
@@ -436,6 +438,27 @@ setup-test-env:
- config/secrets.yml
- vendor/gitaly-ruby
+danger-review:
+ image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest
+ stage: test
+ allow_failure: true
+ before_script:
+ - source scripts/utils.sh
+ - retry gem install danger --no-ri --no-rdoc
+ cache: {}
+ only:
+ refs:
+ - branches@gitlab-org/gitlab-ce
+ - branches@gitlab-org/gitlab-ee
+ except:
+ refs:
+ - master
+ variables:
+ - $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
+ script:
+ - git version
+ - danger --fail-on-errors=true
+
rspec-pg 0 30: *rspec-metadata-pg
rspec-pg 1 30: *rspec-metadata-pg
rspec-pg 2 30: *rspec-metadata-pg
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
index d14d52e1b6b..e636ec313df 100644
--- a/.gitlab/merge_request_templates/Database changes.md
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -34,17 +34,17 @@ When removing columns, tables, indexes or other structures:
## General checklist
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
-- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html)
-- [ ] API support added
-- [ ] Tests added for this feature/bug
-- Conform by the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
- - [ ] Has been reviewed by a Backend maintainer
- - [ ] Has been reviewed by a Database specialist
-- [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
-- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
+- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
+- [ ] [API support added](https://docs.gitlab.com/ee/development/api_styleguide.html)
+- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
+- Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
+ - [ ] Has been reviewed by a Backend [maintainer](https://about.gitlab.com/handbook/engineering/#maintainer)
+ - [ ] Has been reviewed by a Database [specialist](https://about.gitlab.com/team/structure/#specialist)
+- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
+- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
-- [ ] Internationalization required/considered
-- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan
-- [ ] End-to-end tests pass (`package-and-qa` manual pipeline job)
+- [ ] [Internationalization required/considered](https://docs.gitlab.com/ee/development/i18n/index.html)
+- [ ] For a paid feature, have we considered GitLab.com plans, how it works for groups, and is there a design for promoting it to users who aren't on the correct plan?
+- [ ] [End-to-end tests](https://docs.gitlab.com/ee/development/testing_guide/end_to_end_tests.html#testing-code-in-merge-requests) pass (`package-and-qa` manual pipeline job)
/label ~database
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index da38a703c3c..531035b3766 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,4 +1,4 @@
-<!--See the general Documentation guidelines https://docs.gitlab.com/ce/development/writing_documentation.html -->
+<!--See the general Documentation guidelines https://docs.gitlab.com/ee/development/documentation/index.html -->
## What does this MR do?
@@ -13,17 +13,17 @@ Closes
## Moving docs to a new location?
Read the guidelines:
-https://docs.gitlab.com/ce/development/writing_documentation.html#changing-document-location
+https://docs.gitlab.com/ee/development/documentation/#changing-document-location
- [ ] Make sure the old link is not removed and has its contents replaced with
a link to the new location.
- [ ] Make sure internal links pointing to the document in question are not broken.
-- [ ] Search and replace any links referring to old docs in GitLab Rails app,
- specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
-- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments)
+- [ ] Search and replace any links referring to the old docs in the GitLab Rails app,
+ specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
+- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
to the new document if there are any Disqus comments on the old document thread.
-- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
- with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+- [ ] If working on CE and the `ee-compat-check` jobs fails, [submit an MR to EE
+ with the changes](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee) as well.
- [ ] Ping one of the technical writers for review.
/label ~Documentation
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 3e1713f845e..8a1ca6747a8 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -199,12 +199,6 @@ Naming/HeredocDelimiterCase:
Naming/HeredocDelimiterNaming:
Enabled: false
-# Offense count: 27
-# Cop supports --auto-correct.
-# Configuration parameters: AutoCorrect.
-Performance/HashEachMethods:
- Enabled: false
-
# Offense count: 1
Performance/UnfreezeString:
Exclude:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fd4e769ecee..4a1fa39b41d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -50,6 +50,7 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
- [Definition of done](#definition-of-done)
- [Style guides](#style-guides)
- [Code of conduct](#code-of-conduct)
+- [Contribution Flow](#contribution-flow)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -225,24 +226,24 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable
or ~"Stretch". Any open issue for a previous milestone should be labeled
~"Next Patch Release", or otherwise rescheduled to a different milestone.
-### Bug Priority labels
+### Priority labels
-Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
+Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
-| Label | Meaning | Estimate time to fix | Guidance |
-|-------|-----------------|------------------------------------------------------------------|----------|
-| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com | |
-| ~P2 | High Priority | The next release | |
-| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | |
-| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented |
+| Label | Meaning | Estimate time to fix |
+|-------|-----------------|------------------------------------------------------------------|
+| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com |
+| ~P2 | High Priority | The next release |
+| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) |
+| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) |
-### Bug Severity labels
+### Severity labels
Severity labels help us clearly communicate the impact of a ~bug on users.
-| Label | Meaning | Impact of the defect | Example |
+| Label | Meaning | Impact on Functionality | Example |
|-------|-------------------|-------------------------------------------------------|---------|
| ~S1 | Blocker | Outage, broken feature with no workaround | Unable to create an issue. Data corruption/loss. Security breach. |
| ~S2 | Critical Severity | Broken Feature, workaround too complex & unacceptable | Can push commits, but only via the command line. |
@@ -251,12 +252,14 @@ Severity labels help us clearly communicate the impact of a ~bug on users.
#### Severity impact guidance
-| Label | Security Impact | Availability / Performance Impact |
-|-------|---------------------------------------------------------------------|--------------------------------------------------------------|
-| ~S1 | >50% users impacted (possible company extinction level event) | |
-| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
-| ~S3 | A few users or a single paid customer impacted | The issue is likely to occur in the near future |
-| ~S4 | No paid users/customer impacted, or expected impact within 30 days | The issue _may_ occur but it's not likely |
+Severity levels can be applied further depending on the facet of the impact; e.g. Affected customers, GitLab.com availability, performance and etc. The below is a guideline.
+
+| Severity | Affected Customers/Users | GitLab.com Availability | Performance Degradation |
+|----------|---------------------------------------------------------------------|----------------------------------------------------|------------------------------|
+| ~S1 | >50% users affected (possible company extinction level event) | Significant impact on all of GitLab.com | |
+| ~S2 | Many users or multiple paid customers affected (but not apocalyptic)| Significant impact on large portions of GitLab.com | Degradation is guaranteed to occur in the near future |
+| ~S3 | A few users or a single paid customer affected | Limited impact on important portions of GitLab.com | Degradation is likely to occur in the near future |
+| ~S4 | No paid users/customer affected, or expected to in the near future | Minor impact on on GitLab.com | Degradation _may_ occur but it's not likely |
### Label for community contributors
@@ -729,6 +732,24 @@ reported by emailing `contact@gitlab.com`.
This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+## Contribution Flow
+
+When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
+
+When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
+
+When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
+
+Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
+
+GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
+
+GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
+
+When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
+
+When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
+
[core team]: https://about.gitlab.com/core-team/
[team]: https://about.gitlab.com/team/
[getting-help]: https://about.gitlab.com/getting-help/
diff --git a/Dangerfile b/Dangerfile
new file mode 100644
index 00000000000..84b72673c50
--- /dev/null
+++ b/Dangerfile
@@ -0,0 +1,6 @@
+danger.import_dangerfile(path: 'danger/metadata')
+danger.import_dangerfile(path: 'danger/changes_size')
+danger.import_dangerfile(path: 'danger/changelog')
+danger.import_dangerfile(path: 'danger/specs')
+danger.import_dangerfile(path: 'danger/gemfile')
+danger.import_dangerfile(path: 'danger/database')
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 068bced3785..e23e3fd2982 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.110.0
+0.112.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index f374f6662e9..3eefcb9dd5b 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-0.9.1
+1.0.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index b7f8ee41e69..69adf3456f8 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-7.1.4
+7.1.5
diff --git a/Gemfile b/Gemfile
index 052d0d7e0d4..d575568adaa 100644
--- a/Gemfile
+++ b/Gemfile
@@ -104,7 +104,7 @@ gem 'hashie-forbidden_attributes'
gem 'kaminari', '~> 1.0'
# HAML
-gem 'hamlit', '~> 2.6.1'
+gem 'hamlit', '~> 2.8.8'
# Files attachments
gem 'carrierwave', '~> 1.2'
@@ -351,9 +351,9 @@ group :development, :test do
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 2.3', require: false
+ gem 'gitlab-styles', '~> 2.4', require: false
# Pin these dependencies, otherwise a new rule could break the CI pipelines
- gem 'rubocop', '~> 0.52.1'
+ gem 'rubocop', '~> 0.54.0'
gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false
@@ -418,7 +418,7 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.103.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.105.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
diff --git a/Gemfile.lock b/Gemfile.lock
index 79682559522..7f9207d9dfe 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -282,7 +282,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.103.0)
+ gitaly-proto (0.105.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -312,8 +312,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
- gitlab-styles (2.3.2)
- rubocop (~> 0.51)
+ gitlab-styles (2.4.1)
+ rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
gitlab_omniauth-ldap (2.0.4)
@@ -359,7 +359,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)
@@ -381,8 +381,8 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
- hamlit (2.6.1)
- temple (~> 0.7.6)
+ hamlit (2.8.8)
+ temple (>= 0.8.0)
thor
tilt
hashdiff (0.3.4)
@@ -776,16 +776,16 @@ GEM
pg
rails
sqlite3
- rubocop (0.52.1)
+ rubocop (0.54.0)
parallel (~> 1.10)
- parser (>= 2.4.0.2, < 3.0)
+ parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-rspec (1.22.1)
+ rubocop-rspec (1.22.2)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@@ -889,7 +889,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
- temple (0.7.7)
+ temple (0.8.0)
test-prof (0.2.5)
test_after_commit (1.1.0)
activerecord (>= 3.2)
@@ -900,7 +900,7 @@ 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)
@@ -1037,13 +1037,13 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.103.0)
+ gitaly-proto (~> 0.105.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.4)
- gitlab-styles (~> 2.3)
+ gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
google-api-client (~> 0.19.8)
@@ -1057,7 +1057,7 @@ DEPENDENCIES
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
- hamlit (~> 2.6.1)
+ hamlit (~> 2.8.8)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
@@ -1143,7 +1143,7 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.52.1)
+ rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 0d153a526e7..766f2479ea5 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -79,7 +79,7 @@ GEM
babosa (1.0.2)
base32 (0.3.2)
batch-loader (1.2.1)
- bcrypt (3.1.11)
+ bcrypt (3.1.12)
bcrypt_pbkdf (1.0.0)
benchmark-ips (2.3.0)
better_errors (2.1.1)
@@ -111,7 +111,7 @@ GEM
capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
- carrierwave (1.2.1)
+ carrierwave (1.2.3)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
@@ -285,7 +285,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.103.0)
+ gitaly-proto (0.105.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -315,8 +315,8 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.4)
- gitlab-styles (2.3.2)
- rubocop (~> 0.51)
+ gitlab-styles (2.4.1)
+ rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
gitlab_omniauth-ldap (2.0.4)
@@ -384,8 +384,8 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
- hamlit (2.6.1)
- temple (~> 0.7.6)
+ hamlit (2.8.8)
+ temple (>= 0.8.0)
thor
tilt
hashdiff (0.3.4)
@@ -514,7 +514,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
- nokogiri (1.8.2)
+ nokogiri (1.8.3)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -785,16 +785,16 @@ GEM
pg
rails
sqlite3
- rubocop (0.52.1)
+ rubocop (0.54.0)
parallel (~> 1.10)
- parser (>= 2.4.0.2, < 3.0)
+ parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-rspec (1.22.1)
+ rubocop-rspec (1.22.2)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@@ -811,7 +811,7 @@ GEM
rubyzip (1.2.1)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.1)
+ rugged (0.27.2)
safe_yaml (1.0.4)
sanitize (4.6.5)
crass (~> 1.0.2)
@@ -877,7 +877,7 @@ GEM
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
- sprockets (3.7.1)
+ sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
@@ -898,7 +898,7 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
- temple (0.7.7)
+ temple (0.8.0)
test-prof (0.2.5)
text (1.3.1)
thin (1.7.0)
@@ -1047,13 +1047,13 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.103.0)
+ gitaly-proto (~> 0.105.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.4)
- gitlab-styles (~> 2.3)
+ gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
google-api-client (~> 0.19.8)
@@ -1067,7 +1067,7 @@ DEPENDENCIES
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
- hamlit (~> 2.6.1)
+ hamlit (~> 2.8.8)
hashie-forbidden_attributes
health_check (~> 2.6.0)
hipchat (~> 1.5.0)
@@ -1154,7 +1154,7 @@ DEPENDENCIES
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.52.1)
+ rubocop (~> 0.54.0)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.17.0)
diff --git a/README.md b/README.md
index 77f03b791f2..b6e1cc9a432 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,5 @@
# GitLab
-[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
-[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ce/pipelines)
-[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
-[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
-[![Gitter](https://badges.gitter.im/gitlabhq/gitlabhq.svg)](https://gitter.im/gitlabhq/gitlabhq?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
diff --git a/Rakefile b/Rakefile
index 85fff2d51eb..de0d6695c7b 100755
--- a/Rakefile
+++ b/Rakefile
@@ -2,9 +2,9 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-require File.expand_path('../config/application', __FILE__)
+require File.expand_path('config/application', __dir__)
-relative_url_conf = File.expand_path('../config/initializers/relative_url', __FILE__)
+relative_url_conf = File.expand_path('config/initializers/relative_url', __dir__)
require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 0ca0e8f35dd..422becb7db8 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -100,12 +100,12 @@ const Api = {
},
// Return Merge Request for project
- mergeRequest(projectPath, mergeRequestId) {
+ mergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.mergeRequestPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId);
- return axios.get(url);
+ return axios.get(url, { params });
},
mergeRequests(params = {}) {
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 1638e09132b..b0c85c2572e 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -2,13 +2,16 @@ import $ from 'jquery';
import { rstrip } from './lib/utils/common_utils';
function openConfirmDangerModal($form, text) {
+ const $input = $('.js-confirm-danger-input');
+ $input.val('');
+
$('.js-confirm-text').text(text || '');
- $('.js-confirm-danger-input').val('');
$('#modal-confirm-danger').modal('show');
const confirmTextMatch = $('.js-confirm-danger-match').text();
const $submit = $('.js-confirm-danger-submit');
$submit.disable();
+ $input.focus();
$('.js-confirm-danger-input').off('input').on('input', function handleInput() {
const confirmText = rstrip($(this).val());
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index eb0985e5603..7cc4e6a2c3a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -41,11 +41,6 @@ export default {
required: true,
},
},
- data() {
- return {
- activeFile: '',
- };
- },
computed: {
...mapState({
isLoading: state => state.diffs.isLoading,
@@ -63,7 +58,8 @@ export default {
plainDiffPath: state => state.diffs.plainDiffPath,
emailPatchPath: state => state.diffs.emailPatchPath,
}),
- ...mapGetters(['isParallelView', 'isNotesFetched']),
+ ...mapGetters('diffs', ['isParallelView']),
+ ...mapGetters(['isNotesFetched']),
targetBranch() {
return {
branchName: this.targetBranchName,
@@ -89,6 +85,9 @@ export default {
}
return __('Show latest version');
},
+ canCurrentUserFork() {
+ return this.currentUser.canFork === true && this.currentUser.canCreateMergeRequest;
+ },
},
watch: {
diffViewType() {
@@ -115,7 +114,7 @@ export default {
this.adjustView();
},
methods: {
- ...mapActions(['setBaseConfig', 'fetchDiffFiles']),
+ ...mapActions('diffs', ['setBaseConfig', 'fetchDiffFiles']),
fetchData() {
this.fetchDiffFiles().catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
@@ -125,14 +124,6 @@ export default {
eventHub.$emit('fetchNotesData');
}
},
- setActive(filePath) {
- this.activeFile = filePath;
- },
- unsetActive(filePath) {
- if (this.activeFile === filePath) {
- this.activeFile = '';
- }
- },
adjustView() {
if (this.shouldShow && this.isParallelView) {
window.mrTabs.expandViewContainer();
@@ -194,7 +185,6 @@ export default {
<changed-files
:diff-files="diffFiles"
- :active-file="activeFile"
/>
<div
@@ -205,9 +195,7 @@ export default {
v-for="file in diffFiles"
:key="file.newPath"
:file="file"
- :current-user="currentUser"
- @setActive="setActive(file.filePath)"
- @unsetActive="unsetActive(file.filePath)"
+ :can-current-user-fork="canCurrentUserFork"
/>
</div>
<no-changes v-else />
diff --git a/app/assets/javascripts/diffs/components/changed_files.vue b/app/assets/javascripts/diffs/components/changed_files.vue
index c5ef9fefc2f..97751db1254 100644
--- a/app/assets/javascripts/diffs/components/changed_files.vue
+++ b/app/assets/javascripts/diffs/components/changed_files.vue
@@ -16,13 +16,6 @@ export default {
ClipboardButton,
},
mixins: [changedFilesMixin],
- props: {
- activeFile: {
- type: String,
- required: false,
- default: '',
- },
- },
data() {
return {
isStuck: false,
@@ -31,7 +24,7 @@ export default {
};
},
computed: {
- ...mapGetters(['isInlineView', 'isParallelView', 'areAllFilesCollapsed']),
+ ...mapGetters('diffs', ['isInlineView', 'isParallelView', 'areAllFilesCollapsed']),
sumAddedLines() {
return this.sumValues('addedLines');
},
@@ -66,11 +59,11 @@ export default {
document.removeEventListener('scroll', this.handleScroll);
},
methods: {
- ...mapActions(['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']),
+ ...mapActions('diffs', ['setInlineDiffViewType', 'setParallelDiffViewType', 'expandAllFiles']),
pluralize,
handleScroll() {
if (!this.updating) {
- requestAnimationFrame(this.updateIsStuck);
+ this.$nextTick(this.updateIsStuck);
this.updating = true;
}
},
@@ -148,25 +141,8 @@ export default {
/>
<span
- v-show="activeFile"
- class="prepend-left-5"
- >
- <strong class="prepend-right-5">
- {{ truncatedDiffPath(activeFile) }}
- </strong>
- <clipboard-button
- :text="activeFile"
- :title="s__('Copy file name to clipboard')"
- tooltip-placement="bottom"
- tooltip-container="body"
- class="btn btn-default btn-transparent btn-clipboard"
- />
- </span>
-
- <span
- v-show="!isStuck"
- id="diff-stats"
- class="diff-stats-additions-deletions-expanded"
+ class="js-diff-stats-additions-deletions-expanded
+ diff-stats-additions-deletions-expanded"
>
with
<strong class="cgreen">
@@ -177,6 +153,17 @@ export default {
{{ pluralize(`${sumRemovedLines} deletion`, sumRemovedLines) }}
</strong>
</span>
+ <div
+ class="js-diff-stats-additions-deletions-collapsed
+ diff-stats-additions-deletions-collapsed float-right d-sm-none"
+ >
+ <strong class="cgreen">
+ +{{ sumAddedLines }}
+ </strong>
+ <strong class="cred">
+ -{{ sumRemovedLines }}
+ </strong>
+ </div>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue b/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
index b38d217fbe3..045688a32bf 100644
--- a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
+++ b/app/assets/javascripts/diffs/components/changed_files_dropdown.vue
@@ -40,7 +40,7 @@ export default {
{{ n__('%d changed file', '%d changed files', diffFiles.length) }}
</span>
<icon
- :size="8"
+ class="caret-icon"
name="chevron-down"
/>
</button>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index b6af49c7e2e..02d5be1821b 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -22,7 +22,7 @@ export default {
projectPath: state => state.diffs.projectPath,
endpoint: state => state.diffs.endpoint,
}),
- ...mapGetters(['isInlineView', 'isParallelView']),
+ ...mapGetters('diffs', ['isInlineView', 'isParallelView']),
diffMode() {
const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]);
return diffModes[diffModeKey] || diffModes.replaced;
@@ -39,12 +39,12 @@ export default {
<div class="diff-viewer">
<template v-if="isTextFile">
<inline-diff-view
- v-show="isInlineView"
+ v-if="isInlineView"
:diff-file="diffFile"
:diff-lines="diffFile.highlightedDiffLines || []"
/>
<parallel-diff-view
- v-show="isParallelView"
+ v-if="isParallelView"
:diff-file="diffFile"
:diff-lines="diffFile.parallelDiffLines || []"
/>
diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue
index 39d535036f6..20483161033 100644
--- a/app/assets/javascripts/diffs/components/diff_discussions.vue
+++ b/app/assets/javascripts/diffs/components/diff_discussions.vue
@@ -15,9 +15,7 @@ export default {
</script>
<template>
- <div
- v-if="discussions.length"
- >
+ <div>
<div
v-for="discussion in discussions"
:key="discussion.id"
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 108eefdac5f..7e7058d8d08 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -18,22 +18,18 @@ export default {
type: Object,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
},
data() {
return {
- isActive: false,
isLoadingCollapsedDiff: false,
forkMessageVisible: false,
};
},
computed: {
- isDiscussionsExpanded() {
- return true; // TODO: @fatihacet - Fix this.
- },
isCollapsed() {
return this.file.collapsed || false;
},
@@ -47,15 +43,12 @@ export default {
false,
);
},
- },
- mounted() {
- document.addEventListener('scroll', this.handleScroll);
- },
- beforeDestroy() {
- document.removeEventListener('scroll', this.handleScroll);
+ showExpandMessage() {
+ return this.isCollapsed && !this.isLoadingCollapsedDiff && !this.file.tooLarge;
+ },
},
methods: {
- ...mapActions(['loadCollapsedDiff']),
+ ...mapActions('diffs', ['loadCollapsedDiff']),
handleToggle() {
const { collapsed, highlightedDiffLines, parallelDiffLines } = this.file;
@@ -65,36 +58,6 @@ export default {
this.file.collapsed = !this.file.collapsed;
}
},
- handleScroll() {
- if (!this.updating) {
- requestAnimationFrame(this.scrollUpdate.bind(this));
- this.updating = true;
- }
- },
- scrollUpdate() {
- const header = document.querySelector('.js-diff-files-changed');
- if (!header) {
- this.updating = false;
- return;
- }
-
- const { top, bottom } = this.$el.getBoundingClientRect();
- const { top: topOfFixedHeader, bottom: bottomOfFixedHeader } = header.getBoundingClientRect();
-
- const headerOverlapsContent = top < topOfFixedHeader && bottom > bottomOfFixedHeader;
- const fullyAboveHeader = bottom < bottomOfFixedHeader;
- const fullyBelowHeader = top > topOfFixedHeader;
-
- if (headerOverlapsContent && !this.isActive) {
- this.$emit('setActive');
- this.isActive = true;
- } else if (this.isActive && (fullyAboveHeader || fullyBelowHeader)) {
- this.$emit('unsetActive');
- this.isActive = false;
- }
-
- this.updating = false;
- },
handleLoadCollapsedDiff() {
this.isLoadingCollapsedDiff = true;
@@ -124,11 +87,10 @@ export default {
class="diff-file file-holder"
>
<diff-file-header
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
:diff-file="file"
:collapsible="true"
:expanded="!isCollapsed"
- :discussions-expanded="isDiscussionsExpanded"
:add-merge-request-buttons="true"
class="js-file-title file-title"
@toggleFile="handleToggle"
@@ -159,7 +121,7 @@ export default {
</div>
<diff-content
- v-show="!isCollapsed"
+ v-if="!isCollapsed"
:class="{ hidden: isCollapsed || file.tooLarge }"
:diff-file="file"
/>
@@ -168,7 +130,7 @@ export default {
class="diff-content loading"
/>
<div
- v-show="isCollapsed && !isLoadingCollapsedDiff && !file.tooLarge"
+ v-if="showExpandMessage"
class="nothing-here-block diff-collapsed"
>
{{ __('This diff is collapsed.') }}
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index a8e8732053b..c494d3bcd3e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -1,5 +1,6 @@
<script>
import _ from 'underscore';
+import { mapActions, mapGetters } from 'vuex';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Icon from '~/vue_shared/components/icon.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
@@ -38,13 +39,8 @@ export default {
required: false,
default: true,
},
- discussionsExpanded: {
+ canCurrentUserFork: {
type: Boolean,
- required: false,
- default: true,
- },
- currentUser: {
- type: Object,
required: true,
},
},
@@ -54,6 +50,10 @@ export default {
};
},
computed: {
+ ...mapGetters('diffs', ['diffHasExpandedDiscussions']),
+ hasExpandedDiscussions() {
+ return this.diffHasExpandedDiscussions(this.diffFile);
+ },
icon() {
if (this.diffFile.submodule) {
return 'archive';
@@ -88,9 +88,6 @@ export default {
collapseIcon() {
return this.expanded ? 'chevron-down' : 'chevron-right';
},
- isDiscussionsExpanded() {
- return this.discussionsExpanded && this.expanded;
- },
viewFileButtonText() {
const truncatedContentSha = _.escape(truncateSha(this.diffFile.contentSha));
return sprintf(
@@ -113,7 +110,8 @@ export default {
},
},
methods: {
- handleToggle(e, checkTarget) {
+ ...mapActions('diffs', ['toggleFileDiscussions']),
+ handleToggleFile(e, checkTarget) {
if (
!checkTarget ||
e.target === this.$refs.header ||
@@ -125,6 +123,9 @@ export default {
showForkMessage() {
this.$emit('showForkMessage');
},
+ handleToggleDiscussions() {
+ this.toggleFileDiscussions(this.diffFile);
+ },
},
};
</script>
@@ -133,7 +134,7 @@ export default {
<div
ref="header"
class="js-file-title file-title file-title-flex-parent"
- @click="handleToggle($event, true)"
+ @click="handleToggleFile($event, true)"
>
<div class="file-header-content">
<icon
@@ -145,6 +146,7 @@ export default {
@click.stop="handleToggle"
/>
<a
+ v-once
ref="titleWrapper"
:href="titleLink"
class="append-right-4"
@@ -215,17 +217,18 @@ export default {
v-if="diffFile.blob && diffFile.blob.readableText"
>
<button
- :class="{ active: isDiscussionsExpanded }"
+ :class="{ active: hasExpandedDiscussions }"
:title="s__('MergeRequests|Toggle comments for this file')"
- class="btn js-toggle-diff-comments"
+ class="js-btn-vue-toggle-comments btn"
type="button"
+ @click="handleToggleDiscussions"
>
<icon name="comment" />
</button>
<edit-button
v-if="!diffFile.deletedFile"
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
:edit-path="diffFile.editPath"
:can-modify-blob="diffFile.canModifyBlob"
@showForkMessage="showForkMessage"
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index a74ea4bfaaf..ad838a32518 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -108,7 +108,7 @@ export default {
},
},
methods: {
- ...mapActions(['loadMoreLines', 'showCommentForm']),
+ ...mapActions('diffs', ['loadMoreLines', 'showCommentForm']),
handleCommentButton() {
this.showCommentForm({ lineCode: this.lineCode });
},
@@ -189,6 +189,7 @@ export default {
</button>
<a
v-if="lineNumber"
+ v-once
:data-linenumber="lineNumber"
:href="lineHref"
>
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index 6943b462e86..32f9516d332 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -13,12 +13,8 @@ export default {
noteForm,
},
props: {
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
line: {
@@ -40,6 +36,7 @@ export default {
noteableData: state => state.notes.noteableData,
diffViewType: state => state.diffs.diffViewType,
}),
+ ...mapGetters('diffs', ['getDiffFileByHash']),
...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
},
mounted() {
@@ -59,7 +56,8 @@ export default {
}
},
methods: {
- ...mapActions(['cancelCommentForm', 'saveNote', 'fetchDiscussions']),
+ ...mapActions('diffs', ['cancelCommentForm']),
+ ...mapActions(['saveNote', 'refetchDiscussionById']),
handleCancelCommentForm() {
this.autosave.reset();
this.cancelCommentForm({
@@ -67,21 +65,22 @@ export default {
});
},
handleSaveNote(note) {
+ const selectedDiffFile = this.getDiffFileByHash(this.diffFileHash);
const postData = getNoteFormData({
note,
noteableData: this.noteableData,
noteableType: this.noteableType,
noteTargetLine: this.noteTargetLine,
diffViewType: this.diffViewType,
- diffFile: this.diffFile,
+ diffFile: selectedDiffFile,
linePosition: this.position,
});
this.saveNote(postData)
- .then(() => {
+ .then(result => {
const endpoint = this.getNotesDataByProp('discussionsPath');
- this.fetchDiscussions(endpoint)
+ this.refetchDiscussionById({ path: endpoint, discussionId: result.discussion_id })
.then(() => {
this.handleCancelCommentForm();
})
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index 5b08b161114..5962f30d9bb 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -24,8 +24,12 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
diffViewType: {
@@ -117,25 +121,17 @@ export default {
<template>
<td
- v-if="isContentLine"
- :class="lineType"
- class="line_content"
- v-html="normalizedLine.richText"
- >
- </td>
- <td
- v-else
:class="classNameMap"
>
<diff-line-gutter-content
- :file-hash="diffFile.fileHash"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line-type="normalizedLine.type"
:line-code="normalizedLine.lineCode"
:line-position="linePosition"
:line-number="lineNumber"
:meta-data="normalizedLine.metaData"
:show-comment-button="showCommentButton"
- :context-lines-path="diffFile.contextLinesPath"
:is-bottom="isBottom"
:is-match-line="isMatchLine"
:is-context-line="isContentLine"
diff --git a/app/assets/javascripts/diffs/components/edit_button.vue b/app/assets/javascripts/diffs/components/edit_button.vue
index ebf90631d76..2fb85ca2f07 100644
--- a/app/assets/javascripts/diffs/components/edit_button.vue
+++ b/app/assets/javascripts/diffs/components/edit_button.vue
@@ -5,8 +5,8 @@ export default {
type: String,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
canModifyBlob: {
@@ -17,12 +17,12 @@ export default {
},
methods: {
handleEditClick(evt) {
- if (!this.currentUser || this.canModifyBlob) {
+ if (!this.canCurrentUserFork || this.canModifyBlob) {
// if we can Edit, do default Edit button behavior
return;
}
- if (this.currentUser.canFork && this.currentUser.canCreateMergeRequest) {
+ if (this.canCurrentUserFork) {
evt.preventDefault();
this.$emit('showForkMessage');
}
diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
index 0e935f1d68e..ca265dd892c 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -13,12 +13,8 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
lineIndex: {
@@ -31,22 +27,9 @@ export default {
diffLineCommentForms: state => state.diffs.diffLineCommentForms,
}),
...mapGetters(['discussionsByLineCode']),
- isDiscussionExpanded() {
- if (!this.discussions.length) {
- return false;
- }
-
- return this.discussions.every(discussion => discussion.expanded);
- },
- hasCommentForm() {
- return this.diffLineCommentForms[this.line.lineCode];
- },
discussions() {
return this.discussionsByLineCode[this.line.lineCode] || [];
},
- shouldRender() {
- return this.isDiscussionExpanded || this.hasCommentForm;
- },
className() {
return this.discussions.length ? '' : 'js-temp-notes-holder';
},
@@ -56,7 +39,6 @@ export default {
<template>
<tr
- v-if="shouldRender"
:class="className"
class="notes_holder"
>
@@ -67,14 +49,14 @@ export default {
<td class="notes_content">
<div class="content">
<diff-discussions
+ v-if="discussions.length"
:discussions="discussions"
/>
<diff-line-note-form
v-if="diffLineCommentForms[line.lineCode]"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line"
- :note-target-line="diffLines[lineIndex]"
+ :note-target-line="line"
/>
</div>
</td>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index a2470843ca6..0197a510ef1 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -16,8 +16,12 @@ export default {
DiffTableCell,
},
props: {
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
line: {
@@ -36,7 +40,7 @@ export default {
};
},
computed: {
- ...mapGetters(['isInlineView']),
+ ...mapGetters('diffs', ['isInlineView']),
isContextLine() {
return this.line.type === CONTEXT_LINE_TYPE;
},
@@ -50,7 +54,7 @@ export default {
inlineRowId() {
const { lineCode, oldLine, newLine } = this.line;
- return lineCode || `${this.diffFile.fileHash}_${oldLine}_${newLine}`;
+ return lineCode || `${this.fileHash}_${oldLine}_${newLine}`;
},
},
created() {
@@ -78,7 +82,8 @@ export default {
@mouseout="handleMouseMove"
>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="oldLineType"
:is-bottom="isBottom"
@@ -87,18 +92,20 @@ export default {
class="diff-line-num old_line"
/>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="newLineType"
:is-bottom="isBottom"
:is-hover="isHover"
class="diff-line-num new_line"
/>
- <diff-table-cell
+ <td
+ v-once
:class="line.type"
- :diff-file="diffFile"
- :line="line"
- :is-content-line="true"
- />
+ class="line_content"
+ v-html="line.richText"
+ >
+ </td>
</tr>
</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index b884230fb63..9fd19b74cd7 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -1,5 +1,5 @@
<script>
-import { mapGetters } from 'vuex';
+import { mapGetters, mapState } from 'vuex';
import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue';
import { trimFirstCharOfLineContent } from '../store/utils';
@@ -20,20 +20,33 @@ export default {
},
},
computed: {
- ...mapGetters(['commit']),
+ ...mapGetters('diffs', ['commitId']),
+ ...mapGetters(['discussionsByLineCode']),
+ ...mapState({
+ diffLineCommentForms: state => state.diffs.diffLineCommentForms,
+ }),
normalizedDiffLines() {
return this.diffLines.map(line => (line.richText ? trimFirstCharOfLineContent(line) : line));
},
diffLinesLength() {
return this.normalizedDiffLines.length;
},
- commitId() {
- return this.commit && this.commit.id;
- },
userColorScheme() {
return window.gon.user_color_scheme;
},
},
+ methods: {
+ shouldRenderCommentRow(line) {
+ if (this.diffLineCommentForms[line.lineCode]) return true;
+
+ const lineDiscussions = this.discussionsByLineCode[line.lineCode];
+ if (lineDiscussions === undefined) {
+ return false;
+ }
+
+ return lineDiscussions.every(discussion => discussion.expanded);
+ },
+ },
};
</script>
@@ -47,14 +60,15 @@ export default {
v-for="(line, index) in normalizedDiffLines"
>
<inline-diff-table-row
- :diff-file="diffFile"
+ :file-hash="diffFile.fileHash"
+ :context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="line.lineCode"
/>
<inline-diff-comment-row
- :diff-file="diffFile"
- :diff-lines="normalizedDiffLines"
+ v-if="shouldRenderCommentRow(line)"
+ :diff-file-hash="diffFile.fileHash"
:line="line"
:line-index="index"
:key="index"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index 5f33ec7a3c2..cc5248c25d9 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -13,12 +13,8 @@ export default {
type: Object,
required: true,
},
- diffFile: {
- type: Object,
- required: true,
- },
- diffLines: {
- type: Array,
+ diffFileHash: {
+ type: String,
required: true,
},
lineIndex: {
@@ -55,13 +51,6 @@ export default {
hasAnyExpandedDiscussion() {
return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
},
- shouldRenderDiscussionsRow() {
- const hasDiscussion = this.hasDiscussion && this.hasAnyExpandedDiscussion;
- const hasCommentFormOnLeft = this.diffLineCommentForms[this.leftLineCode];
- const hasCommentFormOnRight = this.diffLineCommentForms[this.rightLineCode];
-
- return hasDiscussion || hasCommentFormOnLeft || hasCommentFormOnRight;
- },
shouldRenderDiscussionsOnLeft() {
return this.discussionsByLineCode[this.leftLineCode] && this.hasExpandedDiscussionOnLeft;
},
@@ -81,7 +70,6 @@ export default {
<template>
<tr
- v-if="shouldRenderDiscussionsRow"
:class="className"
class="notes_holder"
>
@@ -92,16 +80,16 @@ export default {
class="content"
>
<diff-discussions
+ v-if="discussionsByLineCode[leftLineCode].length"
:discussions="discussionsByLineCode[leftLineCode]"
/>
</div>
<diff-line-note-form
v-if="diffLineCommentForms[leftLineCode] &&
diffLineCommentForms[leftLineCode]"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line.left"
- :note-target-line="diffLines[lineIndex].left"
+ :note-target-line="line.left"
position="left"
/>
</td>
@@ -112,16 +100,16 @@ export default {
class="content"
>
<diff-discussions
+ v-if="discussionsByLineCode[rightLineCode].length"
:discussions="discussionsByLineCode[rightLineCode]"
/>
</div>
<diff-line-note-form
v-if="diffLineCommentForms[rightLineCode] &&
diffLineCommentForms[rightLineCode] && line.right.type"
- :diff-file="diffFile"
- :diff-lines="diffLines"
+ :diff-file-hash="diffFileHash"
:line="line.right"
- :note-target-line="diffLines[lineIndex].right"
+ :note-target-line="line.right"
position="right"
/>
</td>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index eb769584d74..ee5bb4d8d05 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -19,8 +19,12 @@ export default {
DiffTableCell,
},
props: {
- diffFile: {
- type: Object,
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
required: true,
},
line: {
@@ -40,7 +44,7 @@ export default {
};
},
computed: {
- ...mapGetters(['isParallelView']),
+ ...mapGetters('diffs', ['isParallelView']),
isContextLine() {
return this.line.left.type === CONTEXT_LINE_TYPE;
},
@@ -103,7 +107,8 @@ export default {
@mouseout="handleMouseMove"
>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="oldLineType"
:line-position="linePositionLeft"
@@ -113,19 +118,18 @@ export default {
:diff-view-type="parallelDiffViewType"
class="diff-line-num old_line"
/>
- <diff-table-cell
+ <td
+ v-once
:id="line.left.lineCode"
- :diff-file="diffFile"
- :line="line"
- :is-content-line="true"
- :line-position="linePositionLeft"
- :line-type="parallelViewLeftLineType"
- :diff-view-type="parallelDiffViewType"
+ :class="parallelViewLeftLineType"
class="line_content parallel left-side"
@mousedown.native="handleParallelLineMouseDown"
- />
+ v-html="line.left.richText"
+ >
+ </td>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="newLineType"
:line-position="linePositionRight"
@@ -135,16 +139,14 @@ export default {
:diff-view-type="parallelDiffViewType"
class="diff-line-num new_line"
/>
- <diff-table-cell
+ <td
+ v-once
:id="line.right.lineCode"
- :diff-file="diffFile"
- :line="line"
- :is-content-line="true"
- :line-position="linePositionRight"
- :line-type="line.right.type"
- :diff-view-type="parallelDiffViewType"
+ :class="line.right.type"
class="line_content parallel right-side"
@mousedown.native="handleParallelLineMouseDown"
- />
+ v-html="line.right.richText"
+ >
+ </td>
</tr>
</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 52561e197e6..32528c9e7ab 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -1,5 +1,5 @@
<script>
-import { mapGetters } from 'vuex';
+import { mapState, mapGetters } from 'vuex';
import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
import { EMPTY_CELL_TYPE } from '../constants';
@@ -21,7 +21,11 @@ export default {
},
},
computed: {
- ...mapGetters(['commit']),
+ ...mapGetters('diffs', ['commitId']),
+ ...mapGetters(['discussionsByLineCode']),
+ ...mapState({
+ diffLineCommentForms: state => state.diffs.diffLineCommentForms,
+ }),
parallelDiffLines() {
return this.diffLines.map(line => {
const parallelLine = Object.assign({}, line);
@@ -44,13 +48,36 @@ export default {
diffLinesLength() {
return this.parallelDiffLines.length;
},
- commitId() {
- return this.commit && this.commit.id;
- },
userColorScheme() {
return window.gon.user_color_scheme;
},
},
+ methods: {
+ shouldRenderCommentRow(line) {
+ const leftLineCode = line.left.lineCode;
+ const rightLineCode = line.right.lineCode;
+ const discussions = this.discussionsByLineCode;
+ const leftDiscussions = discussions[leftLineCode];
+ const rightDiscussions = discussions[rightLineCode];
+ const hasDiscussion = leftDiscussions || rightDiscussions;
+
+ const hasExpandedDiscussionOnLeft = leftDiscussions
+ ? leftDiscussions.every(discussion => discussion.expanded)
+ : false;
+ const hasExpandedDiscussionOnRight = rightDiscussions
+ ? rightDiscussions.every(discussion => discussion.expanded)
+ : false;
+
+ if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
+ return true;
+ }
+
+ const hasCommentFormOnLeft = this.diffLineCommentForms[leftLineCode];
+ const hasCommentFormOnRight = this.diffLineCommentForms[rightLineCode];
+
+ return hasCommentFormOnLeft || hasCommentFormOnRight;
+ },
+ },
};
</script>
@@ -66,16 +93,17 @@ export default {
v-for="(line, index) in parallelDiffLines"
>
<parallel-diff-table-row
- :diff-file="diffFile"
+ :file-hash="diffFile.fileHash"
+ :context-lines-path="diffFile.contextLinesPath"
:line="line"
:is-bottom="index + 1 === diffLinesLength"
:key="index"
/>
<parallel-diff-comment-row
- :key="line.left.lineCode || line.right.lineCode"
+ v-if="shouldRenderCommentRow(line)"
+ :key="`dcr-${index}`"
:line="line"
- :diff-file="diffFile"
- :diff-lines="parallelDiffLines"
+ :diff-file-hash="diffFile.fileHash"
:line-index="index"
/>
</template>
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 5e0fd5109bb..27001142257 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -82,14 +82,32 @@ export const expandAllFiles = ({ commit }) => {
commit(types.EXPAND_ALL_FILES);
};
-export default {
- setBaseConfig,
- fetchDiffFiles,
- setInlineDiffViewType,
- setParallelDiffViewType,
- showCommentForm,
- cancelCommentForm,
- loadMoreLines,
- loadCollapsedDiff,
- expandAllFiles,
+/**
+ * Toggles the file discussions after user clicked on the toggle discussions button.
+ *
+ * Gets the discussions for the provided diff.
+ *
+ * If all discussions are expanded, it will collapse all of them
+ * If all discussions are collapsed, it will expand all of them
+ * If some discussions are open and others closed, it will expand the closed ones.
+ *
+ * @param {Object} diff
+ */
+export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+ const shouldCloseAll = getters.diffHasAllExpandedDiscussions(diff);
+ const shouldExpandAll = getters.diffHasAllCollpasedDiscussions(diff);
+
+ discussions.forEach(discussion => {
+ const data = { discussionId: discussion.id };
+
+ if (shouldCloseAll) {
+ dispatch('collapseDiscussion', data, { root: true });
+ } else if (shouldExpandAll || (!shouldCloseAll && !shouldExpandAll && !discussion.expanded)) {
+ dispatch('expandDiscussion', data, { root: true });
+ }
+ });
};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index 66d0f47d102..855de79adf8 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -1,16 +1,64 @@
+import _ from 'underscore';
import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '../constants';
-export default {
- isParallelView(state) {
- return state.diffViewType === PARALLEL_DIFF_VIEW_TYPE;
- },
- isInlineView(state) {
- return state.diffViewType === INLINE_DIFF_VIEW_TYPE;
- },
- areAllFilesCollapsed(state) {
- return state.diffFiles.every(file => file.collapsed);
- },
- commit(state) {
- return state.commit;
- },
+export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW_TYPE;
+
+export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE;
+
+export const areAllFilesCollapsed = state => state.diffFiles.every(file => file.collapsed);
+
+export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null);
+
+/**
+ * Checks if the diff has all discussions expanded
+ * @param {Object} diff
+ * @returns {Boolean}
+ */
+export const diffHasAllExpandedDiscussions = (state, getters) => diff => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+
+ return (discussions.length && discussions.every(discussion => discussion.expanded)) || false;
+};
+
+/**
+ * Checks if the diff has all discussions collpased
+ * @param {Object} diff
+ * @returns {Boolean}
+ */
+export const diffHasAllCollpasedDiscussions = (state, getters) => diff => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+
+ return (discussions.length && discussions.every(discussion => !discussion.expanded)) || false;
+};
+
+/**
+ * Checks if the diff has any open discussions
+ * @param {Object} diff
+ * @returns {Boolean}
+ */
+export const diffHasExpandedDiscussions = (state, getters) => diff => {
+ const discussions = getters.getDiffFileDiscussions(diff);
+
+ return (
+ (discussions.length && discussions.find(discussion => discussion.expanded) !== undefined) ||
+ false
+ );
};
+
+/**
+ * Returns an array with the discussions of the given diff
+ * @param {Object} diff
+ * @returns {Array}
+ */
+export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) => diff =>
+ rootGetters.discussions.filter(
+ discussion =>
+ discussion.diff_discussion && _.isEqual(discussion.diff_file.file_hash, diff.fileHash),
+ ) || [];
+
+// prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
+export const getDiffFileByHash = state => fileHash =>
+ state.diffFiles.find(file => file.fileHash === fileHash);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/diffs/store/index.js b/app/assets/javascripts/diffs/store/index.js
deleted file mode 100644
index e6aa8f5b12a..00000000000
--- a/app/assets/javascripts/diffs/store/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Vue from 'vue';
-import Vuex from 'vuex';
-import diffsModule from './modules';
-
-Vue.use(Vuex);
-
-export default new Vuex.Store({
- modules: {
- diffs: diffsModule,
- },
-});
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
new file mode 100644
index 00000000000..39d90a64aab
--- /dev/null
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -0,0 +1,18 @@
+import Cookies from 'js-cookie';
+import { getParameterValues } from '~/lib/utils/url_utility';
+import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants';
+
+const viewTypeFromQueryString = getParameterValues('view')[0];
+const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME);
+const defaultViewType = INLINE_DIFF_VIEW_TYPE;
+
+export default () => ({
+ isLoading: true,
+ endpoint: '',
+ basePath: '',
+ commit: null,
+ diffFiles: [],
+ mergeRequestDiffs: [],
+ diffLineCommentForms: {},
+ diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
+});
diff --git a/app/assets/javascripts/diffs/store/modules/index.js b/app/assets/javascripts/diffs/store/modules/index.js
index 94caa131506..20d1ebbe049 100644
--- a/app/assets/javascripts/diffs/store/modules/index.js
+++ b/app/assets/javascripts/diffs/store/modules/index.js
@@ -1,25 +1,11 @@
-import Cookies from 'js-cookie';
-import { getParameterValues } from '~/lib/utils/url_utility';
-import actions from '../actions';
-import getters from '../getters';
+import * as actions from '../actions';
+import * as getters from '../getters';
import mutations from '../mutations';
-import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants';
-
-const viewTypeFromQueryString = getParameterValues('view')[0];
-const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME);
-const defaultViewType = INLINE_DIFF_VIEW_TYPE;
+import createState from './diff_state';
export default {
- state: {
- isLoading: true,
- endpoint: '',
- basePath: '',
- commit: null,
- diffFiles: [],
- mergeRequestDiffs: [],
- diffLineCommentForms: {},
- diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
- },
+ namespaced: true,
+ state: createState(),
getters,
actions,
mutations,
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 8aa8a114c6f..a98b2be89a3 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -66,15 +66,10 @@ export default {
},
[types.EXPAND_ALL_FILES](state) {
- const diffFiles = [];
-
- state.diffFiles.forEach(file => {
- diffFiles.push({
- ...file,
- collapsed: false,
- });
- });
-
- Object.assign(state, { diffFiles });
+ // eslint-disable-next-line no-param-reassign
+ state.diffFiles = state.diffFiles.map(file => ({
+ ...file,
+ collapsed: false,
+ }));
},
};
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index 17ea3bdb179..8abd8bc581a 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -171,6 +171,8 @@ export default class DueDateSelectors {
initMilestoneDatePicker() {
$('.datepicker').each(function initPikadayMilestone() {
const $datePicker = $(this);
+ const datePickerVal = $datePicker.val();
+
const calendar = new Pikaday({
field: $datePicker.get(0),
theme: 'gitlab-theme animate-picker',
@@ -183,7 +185,7 @@ export default class DueDateSelectors {
},
});
- calendar.setDate(parsePikadayDate($datePicker.val()));
+ calendar.setDate(parsePikadayDate(datePickerVal));
$datePicker.data('pikaday', calendar);
});
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index e3652fe739e..63d83e307ee 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -1,50 +1,50 @@
<script>
- import Icon from '~/vue_shared/components/icon.vue';
- import eventHub from '../event_hub';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+import Icon from '~/vue_shared/components/icon.vue';
+import eventHub from '../event_hub';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ loadingIcon,
+ Icon,
+ },
+ props: {
+ actions: {
+ type: Array,
+ required: false,
+ default: () => [],
},
- components: {
- loadingIcon,
- Icon,
+ },
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
+ computed: {
+ title() {
+ return 'Deploy to...';
},
- props: {
- actions: {
- type: Array,
- required: false,
- default: () => [],
- },
- },
- data() {
- return {
- isLoading: false,
- };
- },
- computed: {
- title() {
- return 'Deploy to...';
- },
- },
- methods: {
- onClickAction(endpoint) {
- this.isLoading = true;
+ },
+ methods: {
+ onClickAction(endpoint) {
+ this.isLoading = true;
- eventHub.$emit('postAction', endpoint);
- },
+ eventHub.$emit('postAction', { endpoint });
+ },
- isActionDisabled(action) {
- if (action.playable === undefined) {
- return false;
- }
+ isActionDisabled(action) {
+ if (action.playable === undefined) {
+ return false;
+ }
- return !action.playable;
- },
+ return !action.playable;
},
- };
+ },
+};
</script>
<template>
<div
@@ -61,10 +61,7 @@
data-toggle="dropdown"
>
<span>
- <icon
- :size="12"
- name="play"
- />
+ <icon name="play" />
<i
class="fa fa-caret-down"
aria-hidden="true"
@@ -85,10 +82,6 @@
class="js-manual-action-link no-btn btn"
@click="onClickAction(action.play_path)"
>
- <icon
- :size="12"
- name="play"
- />
<span>
{{ action.name }}
</span>
diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue
index 68195225d50..7446196de13 100644
--- a/app/assets/javascripts/environments/components/environment_external_url.vue
+++ b/app/assets/javascripts/environments/components/environment_external_url.vue
@@ -1,30 +1,30 @@
<script>
- import Icon from '~/vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
- import { s__ } from '../../locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
+import { s__ } from '../../locale';
- /**
- * Renders the external url link in environments table.
- */
- export default {
- components: {
- Icon,
+/**
+ * Renders the external url link in environments table.
+ */
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ externalUrl: {
+ type: String,
+ required: true,
},
- directives: {
- tooltip,
+ },
+ computed: {
+ title() {
+ return s__('Environments|Open live environment');
},
- props: {
- externalUrl: {
- type: String,
- required: true,
- },
- },
- computed: {
- title() {
- return s__('Environments|Open');
- },
- },
- };
+ },
+};
</script>
<template>
<a
@@ -37,9 +37,6 @@
target="_blank"
rel="noopener noreferrer nofollow"
>
- <icon
- :size="12"
- name="external-link"
- />
+ <icon name="external-link" />
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 5ecdccf63ad..39f3790a286 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -1,429 +1,450 @@
<script>
- import Timeago from 'timeago.js';
- import _ from 'underscore';
- import tooltip from '~/vue_shared/directives/tooltip';
- import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
- import { humanize } from '~/lib/utils/text_utility';
- import ActionsComponent from './environment_actions.vue';
- import ExternalUrlComponent from './environment_external_url.vue';
- import StopComponent from './environment_stop.vue';
- import RollbackComponent from './environment_rollback.vue';
- import TerminalButtonComponent from './environment_terminal_button.vue';
- import MonitoringButtonComponent from './environment_monitoring.vue';
- import CommitComponent from '../../vue_shared/components/commit.vue';
- import eventHub from '../event_hub';
-
- /**
- * Envrionment Item Component
- *
- * Renders a table row for each environment.
- */
- const timeagoInstance = new Timeago();
-
- export default {
- components: {
- UserAvatarLink,
- CommitComponent,
- ActionsComponent,
- ExternalUrlComponent,
- StopComponent,
- RollbackComponent,
- TerminalButtonComponent,
- MonitoringButtonComponent,
+import Timeago from 'timeago.js';
+import _ from 'underscore';
+import tooltip from '~/vue_shared/directives/tooltip';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import { humanize } from '~/lib/utils/text_utility';
+import ActionsComponent from './environment_actions.vue';
+import ExternalUrlComponent from './environment_external_url.vue';
+import StopComponent from './environment_stop.vue';
+import RollbackComponent from './environment_rollback.vue';
+import TerminalButtonComponent from './environment_terminal_button.vue';
+import MonitoringButtonComponent from './environment_monitoring.vue';
+import CommitComponent from '../../vue_shared/components/commit.vue';
+import eventHub from '../event_hub';
+
+/**
+ * Envrionment Item Component
+ *
+ * Renders a table row for each environment.
+ */
+const timeagoInstance = new Timeago();
+
+export default {
+ components: {
+ UserAvatarLink,
+ CommitComponent,
+ ActionsComponent,
+ ExternalUrlComponent,
+ StopComponent,
+ RollbackComponent,
+ TerminalButtonComponent,
+ MonitoringButtonComponent,
+ },
+
+ directives: {
+ tooltip,
+ },
+
+ props: {
+ model: {
+ type: Object,
+ required: true,
+ default: () => ({}),
},
- directives: {
- tooltip,
+ canCreateDeployment: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- props: {
- model: {
- type: Object,
- required: true,
- default: () => ({}),
- },
-
- canCreateDeployment: {
- type: Boolean,
- required: false,
- default: false,
- },
-
- canReadEnvironment: {
- type: Boolean,
- required: false,
- default: false,
- },
+ canReadEnvironment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ computed: {
+ /**
+ * Verifies if `last_deployment` key exists in the current Envrionment.
+ * This key is required to render most of the html - this method works has
+ * an helper.
+ *
+ * @returns {Boolean}
+ */
+ hasLastDeploymentKey() {
+ if (this.model && this.model.last_deployment && !_.isEmpty(this.model.last_deployment)) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Verifies is the given environment has manual actions.
+ * Used to verify if we should render them or nor.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ hasManualActions() {
+ return (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.manual_actions &&
+ this.model.last_deployment.manual_actions.length > 0
+ );
+ },
+
+ /**
+ * Returns whether the environment can be stopped.
+ *
+ * @returns {Boolean}
+ */
+ canStopEnvironment() {
+ return this.model && this.model.can_stop;
+ },
+
+ /**
+ * Verifies if the `deployable` key is present in `last_deployment` key.
+ * Used to verify whether we should or not render the rollback partial.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ canRetry() {
+ return (
+ this.model &&
+ this.hasLastDeploymentKey &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable
+ );
+ },
+
+ /**
+ * Verifies if the date to be shown is present.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ canShowDate() {
+ return (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable !== undefined
+ );
+ },
+
+ /**
+ * Human readable date.
+ *
+ * @returns {String}
+ */
+ createdDate() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.created_at
+ ) {
+ return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
+ }
+ return '';
+ },
+
+ /**
+ * Returns the manual actions with the name parsed.
+ *
+ * @returns {Array.<Object>|Undefined}
+ */
+ manualActions() {
+ if (this.hasManualActions) {
+ return this.model.last_deployment.manual_actions.map(action => {
+ const parsedAction = {
+ name: humanize(action.name),
+ play_path: action.play_path,
+ playable: action.playable,
+ };
+ return parsedAction;
+ });
+ }
+ return [];
+ },
+
+ /**
+ * Builds the string used in the user image alt attribute.
+ *
+ * @returns {String}
+ */
+ userImageAltDescription() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.user &&
+ this.model.last_deployment.user.username
+ ) {
+ return `${this.model.last_deployment.user.username}'s avatar'`;
+ }
+ return '';
+ },
+
+ /**
+ * If provided, returns the commit tag.
+ *
+ * @returns {String|Undefined}
+ */
+ commitTag() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.tag) {
+ return this.model.last_deployment.tag;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit ref.
+ *
+ * @returns {Object|Undefined}
+ */
+ commitRef() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.ref) {
+ return this.model.last_deployment.ref;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit url.
+ *
+ * @returns {String|Undefined}
+ */
+ commitUrl() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.commit_path
+ ) {
+ return this.model.last_deployment.commit.commit_path;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit short sha.
+ *
+ * @returns {String|Undefined}
+ */
+ commitShortSha() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.short_id
+ ) {
+ return this.model.last_deployment.commit.short_id;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit title.
+ *
+ * @returns {String|Undefined}
+ */
+ commitTitle() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.title
+ ) {
+ return this.model.last_deployment.commit.title;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit tag.
+ *
+ * @returns {Object|Undefined}
+ */
+ commitAuthor() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.author
+ ) {
+ return this.model.last_deployment.commit.author;
+ }
+
+ return undefined;
+ },
+
+ /**
+ * Verifies if the `retry_path` key is present and returns its value.
+ *
+ * @returns {String|Undefined}
+ */
+ retryUrl() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.retry_path
+ ) {
+ return this.model.last_deployment.deployable.retry_path;
+ }
+ return undefined;
+ },
+
+ /**
+ * Verifies if the `last?` key is present and returns its value.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ isLastDeployment() {
+ return this.model && this.model.last_deployment && this.model.last_deployment['last?'];
+ },
+
+ /**
+ * Builds the name of the builds needed to display both the name and the id.
+ *
+ * @returns {String}
+ */
+ buildName() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.deployable) {
+ const { deployable } = this.model.last_deployment;
+ return `${deployable.name} #${deployable.id}`;
+ }
+ return '';
+ },
+
+ /**
+ * Builds the needed string to show the internal id.
+ *
+ * @returns {String}
+ */
+ deploymentInternalId() {
+ if (this.model && this.model.last_deployment && this.model.last_deployment.iid) {
+ return `#${this.model.last_deployment.iid}`;
+ }
+ return '';
},
- computed: {
- /**
- * Verifies if `last_deployment` key exists in the current Envrionment.
- * This key is required to render most of the html - this method works has
- * an helper.
- *
- * @returns {Boolean}
- */
- hasLastDeploymentKey() {
- if (this.model &&
- this.model.last_deployment &&
- !_.isEmpty(this.model.last_deployment)) {
- return true;
- }
- return false;
- },
-
- /**
- * Verifies is the given environment has manual actions.
- * Used to verify if we should render them or nor.
- *
- * @returns {Boolean|Undefined}
- */
- hasManualActions() {
- return this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.manual_actions &&
- this.model.last_deployment.manual_actions.length > 0;
- },
-
- /**
- * Returns the value of the `stop_action?` key provided in the response.
- *
- * @returns {Boolean}
- */
- hasStopAction() {
- return this.model && this.model['stop_action?'];
- },
-
- /**
- * Verifies if the `deployable` key is present in `last_deployment` key.
- * Used to verify whether we should or not render the rollback partial.
- *
- * @returns {Boolean|Undefined}
- */
- canRetry() {
- return this.model &&
- this.hasLastDeploymentKey &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable;
- },
-
- /**
- * Verifies if the date to be shown is present.
- *
- * @returns {Boolean|Undefined}
- */
- canShowDate() {
- return this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable !== undefined;
- },
-
- /**
- * Human readable date.
- *
- * @returns {String}
- */
- createdDate() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable.created_at) {
- return timeagoInstance.format(this.model.last_deployment.deployable.created_at);
- }
- return '';
- },
-
- /**
- * Returns the manual actions with the name parsed.
- *
- * @returns {Array.<Object>|Undefined}
- */
- manualActions() {
- if (this.hasManualActions) {
- return this.model.last_deployment.manual_actions.map((action) => {
- const parsedAction = {
- name: humanize(action.name),
- play_path: action.play_path,
- playable: action.playable,
- };
- return parsedAction;
- });
- }
- return [];
- },
-
- /**
- * Builds the string used in the user image alt attribute.
- *
- * @returns {String}
- */
- userImageAltDescription() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.user &&
- this.model.last_deployment.user.username) {
- return `${this.model.last_deployment.user.username}'s avatar'`;
- }
- return '';
- },
-
- /**
- * If provided, returns the commit tag.
- *
- * @returns {String|Undefined}
- */
- commitTag() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.tag) {
- return this.model.last_deployment.tag;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit ref.
- *
- * @returns {Object|Undefined}
- */
- commitRef() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.ref) {
- return this.model.last_deployment.ref;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit url.
- *
- * @returns {String|Undefined}
- */
- commitUrl() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.commit_path) {
- return this.model.last_deployment.commit.commit_path;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit short sha.
- *
- * @returns {String|Undefined}
- */
- commitShortSha() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.short_id) {
- return this.model.last_deployment.commit.short_id;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit title.
- *
- * @returns {String|Undefined}
- */
- commitTitle() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.title) {
- return this.model.last_deployment.commit.title;
- }
- return undefined;
- },
-
- /**
- * If provided, returns the commit tag.
- *
- * @returns {Object|Undefined}
- */
- commitAuthor() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.commit &&
- this.model.last_deployment.commit.author) {
- return this.model.last_deployment.commit.author;
- }
-
- return undefined;
- },
-
- /**
- * Verifies if the `retry_path` key is present and returns its value.
- *
- * @returns {String|Undefined}
- */
- retryUrl() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable.retry_path) {
- return this.model.last_deployment.deployable.retry_path;
- }
- return undefined;
- },
-
- /**
- * Verifies if the `last?` key is present and returns its value.
- *
- * @returns {Boolean|Undefined}
- */
- isLastDeployment() {
- return this.model && this.model.last_deployment &&
- this.model.last_deployment['last?'];
- },
-
- /**
- * Builds the name of the builds needed to display both the name and the id.
- *
- * @returns {String}
- */
- buildName() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable) {
- const { deployable } = this.model.last_deployment;
- return `${deployable.name} #${deployable.id}`;
- }
- return '';
- },
-
- /**
- * Builds the needed string to show the internal id.
- *
- * @returns {String}
- */
- deploymentInternalId() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.iid) {
- return `#${this.model.last_deployment.iid}`;
- }
- return '';
- },
-
- /**
- * Verifies if the user object is present under last_deployment object.
- *
- * @returns {Boolean}
- */
- deploymentHasUser() {
- return this.model &&
- !_.isEmpty(this.model.last_deployment) &&
- !_.isEmpty(this.model.last_deployment.user);
- },
-
- /**
- * Returns the user object nested with the last_deployment object.
- * Used to render the template.
- *
- * @returns {Object}
- */
- deploymentUser() {
- if (this.model &&
- !_.isEmpty(this.model.last_deployment) &&
- !_.isEmpty(this.model.last_deployment.user)) {
- return this.model.last_deployment.user;
- }
- return {};
- },
-
- /**
- * Verifies if the build name column should be rendered by verifing
- * if all the information needed is present
- * and if the environment is not a folder.
- *
- * @returns {Boolean}
- */
- shouldRenderBuildName() {
- return !this.model.isFolder &&
- !_.isEmpty(this.model.last_deployment) &&
- !_.isEmpty(this.model.last_deployment.deployable);
- },
-
- /**
- * Verifies the presence of all the keys needed to render the buil_path.
- *
- * @return {String}
- */
- buildPath() {
- if (this.model &&
- this.model.last_deployment &&
- this.model.last_deployment.deployable &&
- this.model.last_deployment.deployable.build_path) {
- return this.model.last_deployment.deployable.build_path;
- }
-
- return '';
- },
-
- /**
- * Verifies the presence of all the keys needed to render the external_url.
- *
- * @return {String}
- */
- externalURL() {
- if (this.model && this.model.external_url) {
- return this.model.external_url;
- }
-
- return '';
- },
-
- /**
- * Verifies if deplyment internal ID should be rendered by verifing
- * if all the information needed is present
- * and if the environment is not a folder.
- *
- * @returns {Boolean}
- */
- shouldRenderDeploymentID() {
- return !this.model.isFolder &&
- !_.isEmpty(this.model.last_deployment) &&
- this.model.last_deployment.iid !== undefined;
- },
-
- environmentPath() {
- if (this.model && this.model.environment_path) {
- return this.model.environment_path;
- }
-
- return '';
- },
-
- monitoringUrl() {
- if (this.model && this.model.metrics_path) {
- return this.model.metrics_path;
- }
-
- return '';
- },
-
- displayEnvironmentActions() {
- return this.hasManualActions ||
- this.externalURL ||
- this.monitoringUrl ||
- this.hasStopAction ||
- this.canRetry;
- },
+ /**
+ * Verifies if the user object is present under last_deployment object.
+ *
+ * @returns {Boolean}
+ */
+ deploymentHasUser() {
+ return (
+ this.model &&
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.user)
+ );
},
- methods: {
- onClickFolder() {
- eventHub.$emit('toggleFolder', this.model);
- },
+ /**
+ * Returns the user object nested with the last_deployment object.
+ * Used to render the template.
+ *
+ * @returns {Object}
+ */
+ deploymentUser() {
+ if (
+ this.model &&
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.user)
+ ) {
+ return this.model.last_deployment.user;
+ }
+ return {};
},
- };
+
+ /**
+ * Verifies if the build name column should be rendered by verifing
+ * if all the information needed is present
+ * and if the environment is not a folder.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderBuildName() {
+ return (
+ !this.model.isFolder &&
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.deployable)
+ );
+ },
+
+ /**
+ * Verifies the presence of all the keys needed to render the buil_path.
+ *
+ * @return {String}
+ */
+ buildPath() {
+ if (
+ this.model &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.build_path
+ ) {
+ return this.model.last_deployment.deployable.build_path;
+ }
+
+ return '';
+ },
+
+ /**
+ * Verifies the presence of all the keys needed to render the external_url.
+ *
+ * @return {String}
+ */
+ externalURL() {
+ if (this.model && this.model.external_url) {
+ return this.model.external_url;
+ }
+
+ return '';
+ },
+
+ /**
+ * Verifies if deplyment internal ID should be rendered by verifing
+ * if all the information needed is present
+ * and if the environment is not a folder.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderDeploymentID() {
+ return (
+ !this.model.isFolder &&
+ !_.isEmpty(this.model.last_deployment) &&
+ this.model.last_deployment.iid !== undefined
+ );
+ },
+
+ environmentPath() {
+ if (this.model && this.model.environment_path) {
+ return this.model.environment_path;
+ }
+
+ return '';
+ },
+
+ monitoringUrl() {
+ if (this.model && this.model.metrics_path) {
+ return this.model.metrics_path;
+ }
+
+ return '';
+ },
+
+ displayEnvironmentActions() {
+ return (
+ this.hasManualActions ||
+ this.externalURL ||
+ this.monitoringUrl ||
+ this.canStopEnvironment ||
+ this.canRetry
+ );
+ },
+ },
+
+ methods: {
+ onClickFolder() {
+ eventHub.$emit('toggleFolder', this.model);
+ },
+ },
+};
</script>
<template>
<div
@@ -580,11 +601,6 @@
class="btn-group table-action-buttons"
role="group">
- <actions-component
- v-if="hasManualActions && canCreateDeployment"
- :actions="manualActions"
- />
-
<external-url-component
v-if="externalURL && canReadEnvironment"
:external-url="externalURL"
@@ -595,21 +611,26 @@
:monitoring-url="monitoringUrl"
/>
+ <actions-component
+ v-if="hasManualActions && canCreateDeployment"
+ :actions="manualActions"
+ />
+
<terminal-button-component
v-if="model && model.terminal_path"
:terminal-path="model.terminal_path"
/>
- <stop-component
- v-if="hasStopAction && canCreateDeployment"
- :stop-url="model.stop_path"
- />
-
<rollback-component
v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl"
/>
+
+ <stop-component
+ v-if="canStopEnvironment"
+ :environment="model"
+ />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue
index 947e8c901e9..ccc8419ca6d 100644
--- a/app/assets/javascripts/environments/components/environment_monitoring.vue
+++ b/app/assets/javascripts/environments/components/environment_monitoring.vue
@@ -1,29 +1,29 @@
<script>
- /**
- * Renders the Monitoring (Metrics) link in environments table.
- */
- import Icon from '~/vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+/**
+ * Renders the Monitoring (Metrics) link in environments table.
+ */
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- components: {
- Icon,
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ monitoringUrl: {
+ type: String,
+ required: true,
},
- directives: {
- tooltip,
+ },
+ computed: {
+ title() {
+ return 'Monitoring';
},
- props: {
- monitoringUrl: {
- type: String,
- required: true,
- },
- },
- computed: {
- title() {
- return 'Monitoring';
- },
- },
- };
+ },
+};
</script>
<template>
<a
@@ -35,9 +35,6 @@
data-container="body"
rel="noopener noreferrer nofollow"
>
- <icon
- :size="12"
- name="chart"
- />
+ <icon name="chart" />
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
index 310835c5ea9..4deeef4beb9 100644
--- a/app/assets/javascripts/environments/components/environment_rollback.vue
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -1,56 +1,74 @@
<script>
- /**
- * Renders Rollback or Re deploy button in environments table depending
- * of the provided property `isLastDeployment`.
- *
- * Makes a post request when the button is clicked.
- */
- import eventHub from '../event_hub';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
- export default {
- components: {
- loadingIcon,
+/**
+ * Renders Rollback or Re deploy button in environments table depending
+ * of the provided property `isLastDeployment`.
+ *
+ * Makes a post request when the button is clicked.
+ */
+import { s__ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import eventHub from '../event_hub';
+import LoadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+export default {
+ components: {
+ Icon,
+ LoadingIcon,
+ },
+
+ directives: {
+ tooltip,
+ },
+
+ props: {
+ retryUrl: {
+ type: String,
+ default: '',
},
- props: {
- retryUrl: {
- type: String,
- default: '',
- },
-
- isLastDeployment: {
- type: Boolean,
- default: true,
- },
+
+ isLastDeployment: {
+ type: Boolean,
+ default: true,
},
- data() {
- return {
- isLoading: false,
- };
+ },
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
+
+ computed: {
+ title() {
+ return this.isLastDeployment ? s__('Environments|Re-deploy to environment') : s__('Environments|Rollback environment');
},
- methods: {
- onClick() {
- this.isLoading = true;
+ },
+
+ methods: {
+ onClick() {
+ this.isLoading = true;
- eventHub.$emit('postAction', this.retryUrl);
- },
+ eventHub.$emit('postAction', { endpoint: this.retryUrl });
},
- };
+ },
+};
</script>
<template>
<button
+ v-tooltip
:disabled="isLoading"
+ :title="title"
type="button"
class="btn d-none d-sm-none d-md-block"
@click="onClick"
>
- <span v-if="isLastDeployment">
- {{ s__("Environments|Re-deploy") }}
- </span>
- <span v-else>
- {{ s__("Environments|Rollback") }}
- </span>
+ <icon
+ v-if="isLastDeployment"
+ name="repeat" />
+ <icon
+ v-else
+ name="redo"/>
<loading-icon v-if="isLoading" />
</button>
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
index eba58bedd6d..a814b3405f5 100644
--- a/app/assets/javascripts/environments/components/environment_stop.vue
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -1,72 +1,78 @@
<script>
- /**
- * Renders the stop "button" that allows stop an environment.
- * Used in environments table.
- */
+/**
+ * Renders the stop "button" that allows stop an environment.
+ * Used in environments table.
+ */
- import $ from 'jquery';
- import eventHub from '../event_hub';
- import loadingIcon from '../../vue_shared/components/loading_icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+import { s__ } from '~/locale';
+import eventHub from '../event_hub';
+import LoadingButton from '../../vue_shared/components/loading_button.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- components: {
- loadingIcon,
- },
+export default {
+ components: {
+ Icon,
+ LoadingButton,
+ },
- directives: {
- tooltip,
- },
+ directives: {
+ tooltip,
+ },
- props: {
- stopUrl: {
- type: String,
- default: '',
- },
+ props: {
+ environment: {
+ type: Object,
+ required: true,
},
+ },
- data() {
- return {
- isLoading: false,
- };
- },
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
- computed: {
- title() {
- return 'Stop';
- },
+ computed: {
+ title() {
+ return s__('Environments|Stop environment');
},
+ },
- methods: {
- onClick() {
- // eslint-disable-next-line no-alert
- if (window.confirm('Are you sure you want to stop this environment?')) {
- this.isLoading = true;
+ mounted() {
+ eventHub.$on('stopEnvironment', this.onStopEnvironment);
+ },
- $(this.$el).tooltip('dispose');
+ beforeDestroy() {
+ eventHub.$off('stopEnvironment', this.onStopEnvironment);
+ },
- eventHub.$emit('postAction', this.stopUrl);
- }
- },
+ methods: {
+ onClick() {
+ $(this.$el).tooltip('dispose');
+ eventHub.$emit('requestStopEnvironment', this.environment);
+ },
+ onStopEnvironment(environment) {
+ if (this.environment.id === environment.id) {
+ this.isLoading = true;
+ }
},
- };
+ },
+};
</script>
<template>
- <button
+ <loading-button
v-tooltip
- :disabled="isLoading"
+ :loading="isLoading"
:title="title"
:aria-label="title"
- type="button"
- class="btn stop-env-link d-none d-sm-none d-md-block"
+ container-class="btn btn-danger d-none d-sm-none d-md-block"
data-container="body"
+ data-toggle="modal"
+ data-target="#stop-environment-modal"
@click="onClick"
>
- <i
- class="fa fa-stop stop-env-icon"
- aria-hidden="true"
- >
- </i>
- <loading-icon v-if="isLoading" />
- </button>
+ <icon name="stop"/>
+ </loading-button>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue
index f8e3165f8cd..350417e5ad0 100644
--- a/app/assets/javascripts/environments/components/environment_terminal_button.vue
+++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue
@@ -1,31 +1,31 @@
<script>
- /**
- * Renders a terminal button to open a web terminal.
- * Used in environments table.
- */
- import Icon from '~/vue_shared/components/icon.vue';
- import tooltip from '../../vue_shared/directives/tooltip';
+/**
+ * Renders a terminal button to open a web terminal.
+ * Used in environments table.
+ */
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '../../vue_shared/directives/tooltip';
- export default {
- components: {
- Icon,
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ props: {
+ terminalPath: {
+ type: String,
+ required: false,
+ default: '',
},
- directives: {
- tooltip,
+ },
+ computed: {
+ title() {
+ return 'Terminal';
},
- props: {
- terminalPath: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- title() {
- return 'Terminal';
- },
- },
- };
+ },
+};
</script>
<template>
<a
@@ -36,9 +36,6 @@
class="btn terminal-button d-none d-sm-none d-md-block"
data-container="body"
>
- <icon
- :size="12"
- name="terminal"
- />
+ <icon name="terminal" />
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index b18f02343d6..8efdfb8abe0 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -5,10 +5,12 @@
import eventHub from '../event_hub';
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+ import StopEnvironmentModal from './stop_environment_modal.vue';
export default {
components: {
emptyState,
+ StopEnvironmentModal,
},
mixins: [
@@ -90,6 +92,8 @@
</script>
<template>
<div :class="cssContainerClass">
+ <stop-environment-modal :environment="environmentInStopModal" />
+
<div class="top-area">
<tabs
:tabs="tabs"
diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue
new file mode 100644
index 00000000000..657cc8cd1aa
--- /dev/null
+++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue
@@ -0,0 +1,92 @@
+<script>
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { s__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import eventHub from '../event_hub';
+
+export default {
+ id: 'stop-environment-modal',
+ name: 'StopEnvironmentModal',
+
+ components: {
+ GlModal,
+ LoadingButton,
+ },
+
+ directives: {
+ tooltip,
+ },
+
+ props: {
+ environment: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ computed: {
+ noStopActionMessage() {
+ return sprintf(
+ s__(
+ `Environments|Note that this action will stop the environment,
+ but it will %{emphasisStart}not%{emphasisEnd} have an effect on any existing deployment
+ due to no “stop environment action” being defined
+ in the %{ciConfigLinkStart}.gitlab-ci.yml%{ciConfigLinkEnd} file.`,
+ ),
+ {
+ emphasisStart: '<strong>',
+ emphasisEnd: '</strong>',
+ ciConfigLinkStart:
+ '<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">',
+ ciConfigLinkEnd: '</a>',
+ },
+ false,
+ );
+ },
+ },
+
+ methods: {
+ onSubmit() {
+ eventHub.$emit('stopEnvironment', this.environment);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ :id="$options.id"
+ :footer-primary-button-text="s__('Environments|Stop environment')"
+ footer-primary-button-variant="danger"
+ @submit="onSubmit"
+ >
+ <template slot="header">
+ <h4
+ class="modal-title d-flex mw-100"
+ >
+ Stopping
+ <span
+ v-tooltip
+ :title="environment.name"
+ class="text-truncate ml-1 mr-1 flex-fill"
+ >{{ environment.name }}</span>
+ ?
+ </h4>
+ </template>
+
+ <p>{{ s__('Environments|Are you sure you want to stop this environment?') }}</p>
+
+ <div
+ v-if="!environment.has_stop_action"
+ class="warning_message"
+ >
+ <p v-html="noStopActionMessage"></p>
+ <a
+ href="https://docs.gitlab.com/ee/ci/environments.html#stopping-an-environment"
+ target="_blank"
+ rel="noopener noreferrer"
+ >{{ s__('Environments|Learn more about stopping environments') }}</a>
+ </div>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 5f72a39c5cb..e69bfa0b2cc 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -1,12 +1,18 @@
<script>
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
+ import StopEnvironmentModal from '../components/stop_environment_modal.vue';
export default {
+ components: {
+ StopEnvironmentModal,
+ },
+
mixins: [
environmentsMixin,
CIPaginationMixin,
],
+
props: {
endpoint: {
type: String,
@@ -38,6 +44,8 @@
</script>
<template>
<div :class="cssContainerClass">
+ <stop-environment-modal :environment="environmentInStopModal" />
+
<div
v-if="!isLoading"
class="top-area"
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index a7a79dbca70..d88624f7f8d 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -40,6 +40,7 @@ export default {
scope: getParameterByName('scope') || 'available',
page: getParameterByName('page') || '1',
requestData: {},
+ environmentInStopModal: {},
};
},
@@ -85,7 +86,7 @@ export default {
Flash(s__('Environments|An error occurred while fetching the environments.'));
},
- postAction(endpoint) {
+ postAction({ endpoint, errorMessage }) {
if (!this.isMakingRequest) {
this.isLoading = true;
@@ -93,7 +94,7 @@ export default {
.then(() => this.fetchEnvironments())
.catch(() => {
this.isLoading = false;
- Flash(s__('Environments|An error occurred while making the request.'));
+ Flash(errorMessage || s__('Environments|An error occurred while making the request.'));
});
}
},
@@ -106,6 +107,15 @@ export default {
.catch(this.errorCallback);
},
+ updateStopModal(environment) {
+ this.environmentInStopModal = environment;
+ },
+
+ stopEnvironment(environment) {
+ const endpoint = environment.stop_path;
+ const errorMessage = s__('Environments|An error occurred while stopping the environment, please try again');
+ this.postAction({ endpoint, errorMessage });
+ },
},
computed: {
@@ -162,9 +172,13 @@ export default {
});
eventHub.$on('postAction', this.postAction);
+ eventHub.$on('requestStopEnvironment', this.updateStopModal);
+ eventHub.$on('stopEnvironment', this.stopEnvironment);
},
- beforeDestroyed() {
- eventHub.$off('postAction');
+ beforeDestroy() {
+ eventHub.$off('postAction', this.postAction);
+ eventHub.$off('requestStopEnvironment', this.updateStopModal);
+ eventHub.$off('stopEnvironment', this.stopEnvironment);
},
};
diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js
index 3b121551aca..4e07ccba91a 100644
--- a/app/assets/javascripts/environments/services/environments_service.js
+++ b/app/assets/javascripts/environments/services/environments_service.js
@@ -13,7 +13,7 @@ export default class EnvironmentsService {
// eslint-disable-next-line class-methods-use-this
postAction(endpoint) {
- return axios.post(endpoint, {}, { emulateJSON: true });
+ return axios.post(endpoint, {});
}
getFolderContent(folderUrl) {
diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue
new file mode 100644
index 00000000000..2f030de8967
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/app.vue
@@ -0,0 +1,122 @@
+<script>
+import { mapState, mapActions, mapGetters } from 'vuex';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import AccessorUtilities from '~/lib/utils/accessor';
+import eventHub from '../event_hub';
+import store from '../store/';
+import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
+import { isMobile, updateExistingFrequentItem } from '../utils';
+import FrequentItemsSearchInput from './frequent_items_search_input.vue';
+import FrequentItemsList from './frequent_items_list.vue';
+import frequentItemsMixin from './frequent_items_mixin';
+
+export default {
+ store,
+ components: {
+ LoadingIcon,
+ FrequentItemsSearchInput,
+ FrequentItemsList,
+ },
+ mixins: [frequentItemsMixin],
+ props: {
+ currentUserName: {
+ type: String,
+ required: true,
+ },
+ currentItem: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['searchQuery', 'isLoadingItems', 'isFetchFailed', 'items']),
+ ...mapGetters(['hasSearchQuery']),
+ translations() {
+ return this.getTranslations(['loadingMessage', 'header']);
+ },
+ },
+ created() {
+ const { namespace, currentUserName, currentItem } = this;
+ const storageKey = `${currentUserName}/${STORAGE_KEY[namespace]}`;
+
+ this.setNamespace(namespace);
+ this.setStorageKey(storageKey);
+
+ if (currentItem.id) {
+ this.logItemAccess(storageKey, currentItem);
+ }
+
+ eventHub.$on(`${this.namespace}-dropdownOpen`, this.dropdownOpenHandler);
+ },
+ beforeDestroy() {
+ eventHub.$off(`${this.namespace}-dropdownOpen`, this.dropdownOpenHandler);
+ },
+ methods: {
+ ...mapActions(['setNamespace', 'setStorageKey', 'fetchFrequentItems']),
+ dropdownOpenHandler() {
+ if (this.searchQuery === '' || isMobile()) {
+ this.fetchFrequentItems();
+ }
+ },
+ logItemAccess(storageKey, item) {
+ if (!AccessorUtilities.isLocalStorageAccessSafe()) {
+ return false;
+ }
+
+ // Check if there's any frequent items list set
+ const storedRawItems = localStorage.getItem(storageKey);
+ const storedFrequentItems = storedRawItems
+ ? JSON.parse(storedRawItems)
+ : [{ ...item, frequency: 1 }]; // No frequent items list set, set one up.
+
+ // Check if item already exists in list
+ const itemMatchIndex = storedFrequentItems.findIndex(
+ frequentItem => frequentItem.id === item.id,
+ );
+
+ if (itemMatchIndex > -1) {
+ storedFrequentItems[itemMatchIndex] = updateExistingFrequentItem(
+ storedFrequentItems[itemMatchIndex],
+ item,
+ );
+ } else {
+ if (storedFrequentItems.length === FREQUENT_ITEMS.MAX_COUNT) {
+ storedFrequentItems.shift();
+ }
+
+ storedFrequentItems.push({ ...item, frequency: 1 });
+ }
+
+ return localStorage.setItem(storageKey, JSON.stringify(storedFrequentItems));
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <frequent-items-search-input
+ :namespace="namespace"
+ />
+ <loading-icon
+ v-if="isLoadingItems"
+ :label="translations.loadingMessage"
+ class="loading-animation prepend-top-20"
+ size="2"
+ />
+ <div
+ v-if="!isLoadingItems && !hasSearchQuery"
+ class="section-header"
+ >
+ {{ translations.header }}
+ </div>
+ <frequent-items-list
+ v-if="!isLoadingItems"
+ :items="items"
+ :namespace="namespace"
+ :has-search-query="hasSearchQuery"
+ :is-fetch-failed="isFetchFailed"
+ :matcher="searchQuery"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
new file mode 100644
index 00000000000..8e511aa2a36
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
@@ -0,0 +1,78 @@
+<script>
+import FrequentItemsListItem from './frequent_items_list_item.vue';
+import frequentItemsMixin from './frequent_items_mixin';
+
+export default {
+ components: {
+ FrequentItemsListItem,
+ },
+ mixins: [frequentItemsMixin],
+ props: {
+ items: {
+ type: Array,
+ required: true,
+ },
+ hasSearchQuery: {
+ type: Boolean,
+ required: true,
+ },
+ isFetchFailed: {
+ type: Boolean,
+ required: true,
+ },
+ matcher: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ translations() {
+ return this.getTranslations([
+ 'itemListEmptyMessage',
+ 'itemListErrorMessage',
+ 'searchListEmptyMessage',
+ 'searchListErrorMessage',
+ ]);
+ },
+ isListEmpty() {
+ return this.items.length === 0;
+ },
+ listEmptyMessage() {
+ if (this.hasSearchQuery) {
+ return this.isFetchFailed
+ ? this.translations.searchListErrorMessage
+ : this.translations.searchListEmptyMessage;
+ }
+
+ return this.isFetchFailed
+ ? this.translations.itemListErrorMessage
+ : this.translations.itemListEmptyMessage;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="frequent-items-list-container">
+ <ul class="list-unstyled">
+ <li
+ v-if="isListEmpty"
+ :class="{ 'section-failure': isFetchFailed }"
+ class="section-empty"
+ >
+ {{ listEmptyMessage }}
+ </li>
+ <frequent-items-list-item
+ v-for="item in items"
+ v-else
+ :key="item.id"
+ :item-id="item.id"
+ :item-name="item.name"
+ :namespace="item.namespace"
+ :web-url="item.webUrl"
+ :avatar-url="item.avatarUrl"
+ :matcher="matcher"
+ />
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
new file mode 100644
index 00000000000..1f1665ff7fe
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -0,0 +1,117 @@
+<script>
+/* eslint-disable vue/require-default-prop, vue/require-prop-types */
+import Identicon from '../../vue_shared/components/identicon.vue';
+
+export default {
+ components: {
+ Identicon,
+ },
+ props: {
+ matcher: {
+ type: String,
+ required: false,
+ },
+ itemId: {
+ type: Number,
+ required: true,
+ },
+ itemName: {
+ type: String,
+ required: true,
+ },
+ namespace: {
+ type: String,
+ required: false,
+ },
+ webUrl: {
+ type: String,
+ required: true,
+ },
+ avatarUrl: {
+ required: true,
+ validator(value) {
+ return value === null || typeof value === 'string';
+ },
+ },
+ },
+ computed: {
+ hasAvatar() {
+ return this.avatarUrl !== null;
+ },
+ highlightedItemName() {
+ if (this.matcher) {
+ const matcherRegEx = new RegExp(this.matcher, 'gi');
+ const matches = this.itemName.match(matcherRegEx);
+
+ if (matches && matches.length > 0) {
+ return this.itemName.replace(matches[0], `<b>${matches[0]}</b>`);
+ }
+ }
+ return this.itemName;
+ },
+ /**
+ * Smartly truncates item namespace by doing two things;
+ * 1. Only include Group names in path by removing item name
+ * 2. Only include first and last group names in the path
+ * when namespace has more than 2 groups present
+ *
+ * First part (removal of item name from namespace) can be
+ * done from backend but doing so involves migration of
+ * existing item namespaces which is not wise thing to do.
+ */
+ truncatedNamespace() {
+ if (!this.namespace) {
+ return null;
+ }
+ const namespaceArr = this.namespace.split(' / ');
+
+ namespaceArr.splice(-1, 1);
+ let namespace = namespaceArr.join(' / ');
+
+ if (namespaceArr.length > 2) {
+ namespace = `${namespaceArr[0]} / ... / ${namespaceArr.pop()}`;
+ }
+
+ return namespace;
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="frequent-items-list-item-container">
+ <a
+ :href="webUrl"
+ class="clearfix"
+ >
+ <div class="frequent-items-item-avatar-container">
+ <img
+ v-if="hasAvatar"
+ :src="avatarUrl"
+ class="avatar s32"
+ />
+ <identicon
+ v-else
+ :entity-id="itemId"
+ :entity-name="itemName"
+ size-class="s32"
+ />
+ </div>
+ <div class="frequent-items-item-metadata-container">
+ <div
+ :title="itemName"
+ class="frequent-items-item-title"
+ v-html="highlightedItemName"
+ >
+ </div>
+ <div
+ v-if="truncatedNamespace"
+ :title="namespace"
+ class="frequent-items-item-namespace"
+ >
+ {{ truncatedNamespace }}
+ </div>
+ </div>
+ </a>
+ </li>
+</template>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_mixin.js b/app/assets/javascripts/frequent_items/components/frequent_items_mixin.js
new file mode 100644
index 00000000000..704dc83ca8e
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_mixin.js
@@ -0,0 +1,23 @@
+import { TRANSLATION_KEYS } from '../constants';
+
+export default {
+ props: {
+ namespace: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ getTranslations(keys) {
+ const translationStrings = keys.reduce(
+ (acc, key) => ({
+ ...acc,
+ [key]: TRANSLATION_KEYS[this.namespace][key],
+ }),
+ {},
+ );
+
+ return translationStrings;
+ },
+ },
+};
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
new file mode 100644
index 00000000000..a6a265eb3fd
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
@@ -0,0 +1,55 @@
+<script>
+import _ from 'underscore';
+import { mapActions } from 'vuex';
+import eventHub from '../event_hub';
+import frequentItemsMixin from './frequent_items_mixin';
+
+export default {
+ mixins: [frequentItemsMixin],
+ data() {
+ return {
+ searchQuery: '',
+ };
+ },
+ computed: {
+ translations() {
+ return this.getTranslations(['searchInputPlaceholder']);
+ },
+ },
+ watch: {
+ searchQuery: _.debounce(function debounceSearchQuery() {
+ this.setSearchQuery(this.searchQuery);
+ }, 500),
+ },
+ mounted() {
+ eventHub.$on(`${this.namespace}-dropdownOpen`, this.setFocus);
+ },
+ beforeDestroy() {
+ eventHub.$off(`${this.namespace}-dropdownOpen`, this.setFocus);
+ },
+ methods: {
+ ...mapActions(['setSearchQuery']),
+ setFocus() {
+ this.$refs.search.focus();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="search-input-container d-none d-sm-block">
+ <input
+ ref="search"
+ v-model="searchQuery"
+ :placeholder="translations.searchInputPlaceholder"
+ type="search"
+ class="form-control"
+ />
+ <i
+ v-if="!searchQuery"
+ class="search-icon fa fa-fw fa-search"
+ aria-hidden="true"
+ >
+ </i>
+ </div>
+</template>
diff --git a/app/assets/javascripts/frequent_items/constants.js b/app/assets/javascripts/frequent_items/constants.js
new file mode 100644
index 00000000000..9bc17f5ef4f
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/constants.js
@@ -0,0 +1,38 @@
+import { s__ } from '~/locale';
+
+export const FREQUENT_ITEMS = {
+ MAX_COUNT: 20,
+ LIST_COUNT_DESKTOP: 5,
+ LIST_COUNT_MOBILE: 3,
+ ELIGIBLE_FREQUENCY: 3,
+};
+
+export const HOUR_IN_MS = 3600000;
+
+export const STORAGE_KEY = {
+ projects: 'frequent-projects',
+ groups: 'frequent-groups',
+};
+
+export const TRANSLATION_KEYS = {
+ projects: {
+ loadingMessage: s__('ProjectsDropdown|Loading projects'),
+ header: s__('ProjectsDropdown|Frequently visited'),
+ itemListErrorMessage: s__(
+ 'ProjectsDropdown|This feature requires browser localStorage support',
+ ),
+ itemListEmptyMessage: s__('ProjectsDropdown|Projects you visit often will appear here'),
+ searchListErrorMessage: s__('ProjectsDropdown|Something went wrong on our end.'),
+ searchListEmptyMessage: s__('ProjectsDropdown|Sorry, no projects matched your search'),
+ searchInputPlaceholder: s__('ProjectsDropdown|Search your projects'),
+ },
+ groups: {
+ loadingMessage: s__('GroupsDropdown|Loading groups'),
+ header: s__('GroupsDropdown|Frequently visited'),
+ itemListErrorMessage: s__('GroupsDropdown|This feature requires browser localStorage support'),
+ itemListEmptyMessage: s__('GroupsDropdown|Groups you visit often will appear here'),
+ searchListErrorMessage: s__('GroupsDropdown|Something went wrong on our end.'),
+ searchListEmptyMessage: s__('GroupsDropdown|Sorry, no groups matched your search'),
+ searchInputPlaceholder: s__('GroupsDropdown|Search your groups'),
+ },
+};
diff --git a/app/assets/javascripts/projects_dropdown/event_hub.js b/app/assets/javascripts/frequent_items/event_hub.js
index 0948c2e5352..0948c2e5352 100644
--- a/app/assets/javascripts/projects_dropdown/event_hub.js
+++ b/app/assets/javascripts/frequent_items/event_hub.js
diff --git a/app/assets/javascripts/frequent_items/index.js b/app/assets/javascripts/frequent_items/index.js
new file mode 100644
index 00000000000..5157ff211dc
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/index.js
@@ -0,0 +1,69 @@
+import $ from 'jquery';
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import eventHub from '~/frequent_items/event_hub';
+import frequentItems from './components/app.vue';
+
+Vue.use(Translate);
+
+const frequentItemDropdowns = [
+ {
+ namespace: 'projects',
+ key: 'project',
+ },
+ {
+ namespace: 'groups',
+ key: 'group',
+ },
+];
+
+document.addEventListener('DOMContentLoaded', () => {
+ frequentItemDropdowns.forEach(dropdown => {
+ const { namespace, key } = dropdown;
+ const el = document.getElementById(`js-${namespace}-dropdown`);
+ const navEl = document.getElementById(`nav-${namespace}-dropdown`);
+
+ // Don't do anything if element doesn't exist (No groups dropdown)
+ // This is for when the user accesses GitLab without logging in
+ if (!el || !navEl) {
+ return;
+ }
+
+ $(navEl).on('shown.bs.dropdown', () => {
+ eventHub.$emit(`${namespace}-dropdownOpen`);
+ });
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ components: {
+ frequentItems,
+ },
+ data() {
+ const { dataset } = this.$options.el;
+ const item = {
+ id: Number(dataset[`${key}Id`]),
+ name: dataset[`${key}Name`],
+ namespace: dataset[`${key}Namespace`],
+ webUrl: dataset[`${key}WebUrl`],
+ avatarUrl: dataset[`${key}AvatarUrl`] || null,
+ lastAccessedOn: Date.now(),
+ };
+
+ return {
+ currentUserName: dataset.userName,
+ currentItem: item,
+ };
+ },
+ render(createElement) {
+ return createElement('frequent-items', {
+ props: {
+ namespace,
+ currentUserName: this.currentUserName,
+ currentItem: this.currentItem,
+ },
+ });
+ },
+ });
+ });
+});
diff --git a/app/assets/javascripts/frequent_items/store/actions.js b/app/assets/javascripts/frequent_items/store/actions.js
new file mode 100644
index 00000000000..3dd89a82a42
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/actions.js
@@ -0,0 +1,81 @@
+import Api from '~/api';
+import AccessorUtilities from '~/lib/utils/accessor';
+import * as types from './mutation_types';
+import { getTopFrequentItems } from '../utils';
+
+export const setNamespace = ({ commit }, namespace) => {
+ commit(types.SET_NAMESPACE, namespace);
+};
+
+export const setStorageKey = ({ commit }, key) => {
+ commit(types.SET_STORAGE_KEY, key);
+};
+
+export const requestFrequentItems = ({ commit }) => {
+ commit(types.REQUEST_FREQUENT_ITEMS);
+};
+export const receiveFrequentItemsSuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_FREQUENT_ITEMS_SUCCESS, data);
+};
+export const receiveFrequentItemsError = ({ commit }) => {
+ commit(types.RECEIVE_FREQUENT_ITEMS_ERROR);
+};
+
+export const fetchFrequentItems = ({ state, dispatch }) => {
+ dispatch('requestFrequentItems');
+
+ if (AccessorUtilities.isLocalStorageAccessSafe()) {
+ const storedFrequentItems = JSON.parse(localStorage.getItem(state.storageKey));
+
+ dispatch(
+ 'receiveFrequentItemsSuccess',
+ !storedFrequentItems ? [] : getTopFrequentItems(storedFrequentItems),
+ );
+ } else {
+ dispatch('receiveFrequentItemsError');
+ }
+};
+
+export const requestSearchedItems = ({ commit }) => {
+ commit(types.REQUEST_SEARCHED_ITEMS);
+};
+export const receiveSearchedItemsSuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_SEARCHED_ITEMS_SUCCESS, data);
+};
+export const receiveSearchedItemsError = ({ commit }) => {
+ commit(types.RECEIVE_SEARCHED_ITEMS_ERROR);
+};
+export const fetchSearchedItems = ({ state, dispatch }, searchQuery) => {
+ dispatch('requestSearchedItems');
+
+ const params = {
+ simple: true,
+ per_page: 20,
+ membership: !!gon.current_user_id,
+ };
+
+ if (state.namespace === 'projects') {
+ params.order_by = 'last_activity_at';
+ }
+
+ return Api[state.namespace](searchQuery, params)
+ .then(results => {
+ dispatch('receiveSearchedItemsSuccess', results);
+ })
+ .catch(() => {
+ dispatch('receiveSearchedItemsError');
+ });
+};
+
+export const setSearchQuery = ({ commit, dispatch }, query) => {
+ commit(types.SET_SEARCH_QUERY, query);
+
+ if (query) {
+ dispatch('fetchSearchedItems', query);
+ } else {
+ dispatch('fetchFrequentItems');
+ }
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/frequent_items/store/getters.js b/app/assets/javascripts/frequent_items/store/getters.js
new file mode 100644
index 00000000000..00165db6684
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/getters.js
@@ -0,0 +1,4 @@
+export const hasSearchQuery = state => state.searchQuery !== '';
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/frequent_items/store/index.js b/app/assets/javascripts/frequent_items/store/index.js
new file mode 100644
index 00000000000..ece9e6419dd
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/index.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as actions from './actions';
+import * as getters from './getters';
+import mutations from './mutations';
+import state from './state';
+
+Vue.use(Vuex);
+
+export default () =>
+ new Vuex.Store({
+ actions,
+ getters,
+ mutations,
+ state: state(),
+ });
diff --git a/app/assets/javascripts/frequent_items/store/mutation_types.js b/app/assets/javascripts/frequent_items/store/mutation_types.js
new file mode 100644
index 00000000000..cbe2c9401ad
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/mutation_types.js
@@ -0,0 +1,9 @@
+export const SET_NAMESPACE = 'SET_NAMESPACE';
+export const SET_STORAGE_KEY = 'SET_STORAGE_KEY';
+export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
+export const REQUEST_FREQUENT_ITEMS = 'REQUEST_FREQUENT_ITEMS';
+export const RECEIVE_FREQUENT_ITEMS_SUCCESS = 'RECEIVE_FREQUENT_ITEMS_SUCCESS';
+export const RECEIVE_FREQUENT_ITEMS_ERROR = 'RECEIVE_FREQUENT_ITEMS_ERROR';
+export const REQUEST_SEARCHED_ITEMS = 'REQUEST_SEARCHED_ITEMS';
+export const RECEIVE_SEARCHED_ITEMS_SUCCESS = 'RECEIVE_SEARCHED_ITEMS_SUCCESS';
+export const RECEIVE_SEARCHED_ITEMS_ERROR = 'RECEIVE_SEARCHED_ITEMS_ERROR';
diff --git a/app/assets/javascripts/frequent_items/store/mutations.js b/app/assets/javascripts/frequent_items/store/mutations.js
new file mode 100644
index 00000000000..41b660a243f
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/mutations.js
@@ -0,0 +1,71 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.SET_NAMESPACE](state, namespace) {
+ Object.assign(state, {
+ namespace,
+ });
+ },
+ [types.SET_STORAGE_KEY](state, storageKey) {
+ Object.assign(state, {
+ storageKey,
+ });
+ },
+ [types.SET_SEARCH_QUERY](state, searchQuery) {
+ const hasSearchQuery = searchQuery !== '';
+
+ Object.assign(state, {
+ searchQuery,
+ isLoadingItems: true,
+ hasSearchQuery,
+ });
+ },
+ [types.REQUEST_FREQUENT_ITEMS](state) {
+ Object.assign(state, {
+ isLoadingItems: true,
+ hasSearchQuery: false,
+ });
+ },
+ [types.RECEIVE_FREQUENT_ITEMS_SUCCESS](state, rawItems) {
+ Object.assign(state, {
+ items: rawItems,
+ isLoadingItems: false,
+ hasSearchQuery: false,
+ isFetchFailed: false,
+ });
+ },
+ [types.RECEIVE_FREQUENT_ITEMS_ERROR](state) {
+ Object.assign(state, {
+ isLoadingItems: false,
+ hasSearchQuery: false,
+ isFetchFailed: true,
+ });
+ },
+ [types.REQUEST_SEARCHED_ITEMS](state) {
+ Object.assign(state, {
+ isLoadingItems: true,
+ hasSearchQuery: true,
+ });
+ },
+ [types.RECEIVE_SEARCHED_ITEMS_SUCCESS](state, rawItems) {
+ Object.assign(state, {
+ items: rawItems.map(rawItem => ({
+ id: rawItem.id,
+ name: rawItem.name,
+ namespace: rawItem.name_with_namespace || rawItem.full_name,
+ webUrl: rawItem.web_url,
+ avatarUrl: rawItem.avatar_url,
+ })),
+ isLoadingItems: false,
+ hasSearchQuery: true,
+ isFetchFailed: false,
+ });
+ },
+ [types.RECEIVE_SEARCHED_ITEMS_ERROR](state) {
+ Object.assign(state, {
+ isLoadingItems: false,
+ hasSearchQuery: true,
+ isFetchFailed: true,
+ });
+ },
+};
diff --git a/app/assets/javascripts/frequent_items/store/state.js b/app/assets/javascripts/frequent_items/store/state.js
new file mode 100644
index 00000000000..75b04febee4
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/store/state.js
@@ -0,0 +1,8 @@
+export default () => ({
+ namespace: '',
+ storageKey: '',
+ searchQuery: '',
+ isLoadingItems: false,
+ isFetchFailed: false,
+ items: [],
+});
diff --git a/app/assets/javascripts/frequent_items/utils.js b/app/assets/javascripts/frequent_items/utils.js
new file mode 100644
index 00000000000..aba692e4b99
--- /dev/null
+++ b/app/assets/javascripts/frequent_items/utils.js
@@ -0,0 +1,49 @@
+import _ from 'underscore';
+import bp from '~/breakpoints';
+import { FREQUENT_ITEMS, HOUR_IN_MS } from './constants';
+
+export const isMobile = () => {
+ const screenSize = bp.getBreakpointSize();
+
+ return screenSize === 'sm' || screenSize === 'xs';
+};
+
+export const getTopFrequentItems = items => {
+ if (!items) {
+ return [];
+ }
+ const frequentItemsCount = isMobile()
+ ? FREQUENT_ITEMS.LIST_COUNT_MOBILE
+ : FREQUENT_ITEMS.LIST_COUNT_DESKTOP;
+
+ const frequentItems = items.filter(item => item.frequency >= FREQUENT_ITEMS.ELIGIBLE_FREQUENCY);
+
+ if (!frequentItems || frequentItems.length === 0) {
+ return [];
+ }
+
+ frequentItems.sort((itemA, itemB) => {
+ // Sort all frequent items in decending order of frequency
+ // and then by lastAccessedOn with recent most first
+ if (itemA.frequency !== itemB.frequency) {
+ return itemB.frequency - itemA.frequency;
+ } else if (itemA.lastAccessedOn !== itemB.lastAccessedOn) {
+ return itemB.lastAccessedOn - itemA.lastAccessedOn;
+ }
+
+ return 0;
+ });
+
+ return _.first(frequentItems, frequentItemsCount);
+};
+
+export const updateExistingFrequentItem = (frequentItem, item) => {
+ const accessedOverHourAgo =
+ Math.abs(item.lastAccessedOn - frequentItem.lastAccessedOn) / HOUR_IN_MS > 1;
+
+ return {
+ ...item,
+ frequency: accessedOverHourAgo ? frequentItem.frequency + 1 : frequentItem.frequency,
+ lastAccessedOn: accessedOverHourAgo ? Date.now() : frequentItem.lastAccessedOn,
+ };
+};
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 9f016e0338f..257a7432c20 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,6 +1,7 @@
<script>
import Mousetrap from 'mousetrap';
import { mapActions, mapState, mapGetters } from 'vuex';
+import NewModal from './new_dropdown/modal.vue';
import IdeSidebar from './ide_side_bar.vue';
import RepoTabs from './repo_tabs.vue';
import IdeStatusBar from './ide_status_bar.vue';
@@ -13,6 +14,7 @@ const originalStopCallback = Mousetrap.stopCallback;
export default {
components: {
+ NewModal,
IdeSidebar,
RepoTabs,
IdeStatusBar,
@@ -137,5 +139,6 @@ export default {
/>
</div>
<ide-status-bar :file="activeFile"/>
+ <new-modal />
</article>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index 0582ad32e92..715dc1bfb42 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -5,6 +5,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
import CiIcon from '../../vue_shared/components/ci_icon.vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
+import { rightSidebarViews } from '../constants';
export default {
components: {
@@ -49,6 +50,7 @@ export default {
this.stopPipelinePolling();
},
methods: {
+ ...mapActions(['setRightPane']),
...mapActions('pipelines', ['fetchLatestPipeline', 'stopPipelinePolling']),
startTimer() {
this.intervalId = setInterval(() => {
@@ -69,24 +71,31 @@ export default {
return `${this.currentProject.web_url}/commit/${shortSha}`;
},
},
+ rightSidebarViews,
};
</script>
<template>
<footer class="ide-status-bar">
<div
- v-if="lastCommit && lastCommitFormatedAge"
+ v-if="lastCommit"
class="ide-status-branch"
>
<span
v-if="latestPipeline && latestPipeline.details"
class="ide-status-pipeline"
>
- <ci-icon
- v-tooltip
- :status="latestPipeline.details.status"
- :title="latestPipeline.details.status.text"
- />
+ <button
+ type="button"
+ class="p-0 border-0 h-50"
+ @click="setRightPane($options.rightSidebarViews.pipelines)"
+ >
+ <ci-icon
+ v-tooltip
+ :status="latestPipeline.details.status"
+ :title="latestPipeline.details.status.text"
+ />
+ </button>
Pipeline
<a
:href="latestPipeline.details.status.details_path"
diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue
index 8fc4ebe6ca6..0a95c0bb30d 100644
--- a/app/assets/javascripts/ide/components/ide_tree.vue
+++ b/app/assets/javascripts/ide/components/ide_tree.vue
@@ -1,12 +1,16 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
-import NewDropdown from './new_dropdown/index.vue';
+import Icon from '~/vue_shared/components/icon.vue';
import IdeTreeList from './ide_tree_list.vue';
+import Upload from './new_dropdown/upload.vue';
+import NewEntryButton from './new_dropdown/button.vue';
export default {
components: {
- NewDropdown,
+ Icon,
+ Upload,
IdeTreeList,
+ NewEntryButton,
},
computed: {
...mapState(['currentBranchId']),
@@ -20,23 +24,42 @@ export default {
}
},
methods: {
- ...mapActions(['updateViewer']),
+ ...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry']),
},
};
</script>
<template>
<ide-tree-list
+ header-class="d-flex w-100"
viewer-type="editor"
>
<template
slot="header"
>
{{ __('Edit') }}
- <new-dropdown
- :project-id="currentProject.name_with_namespace"
- :branch="currentBranchId"
- />
+ <div class="ml-auto d-flex">
+ <new-entry-button
+ :label="__('New file')"
+ :show-label="false"
+ class="d-flex border-0 p-0 mr-3"
+ icon="doc-new"
+ @click="openNewEntryModal({ type: 'blob' })"
+ />
+ <upload
+ :show-label="false"
+ class="d-flex mr-3"
+ button-css-classes="border-0 p-0"
+ @create="createTempEntry"
+ />
+ <new-entry-button
+ :label="__('New directory')"
+ :show-label="false"
+ class="d-flex border-0 p-0"
+ icon="folder-new"
+ @click="openNewEntryModal({ type: 'tree' })"
+ />
+ </div>
</template>
</ide-tree-list>
</template>
diff --git a/app/assets/javascripts/ide/components/merge_requests/info.vue b/app/assets/javascripts/ide/components/merge_requests/info.vue
new file mode 100644
index 00000000000..199d2e74971
--- /dev/null
+++ b/app/assets/javascripts/ide/components/merge_requests/info.vue
@@ -0,0 +1,43 @@
+<script>
+import { mapGetters } from 'vuex';
+import Icon from '../../../vue_shared/components/icon.vue';
+import TitleComponent from '../../../issue_show/components/title.vue';
+import DescriptionComponent from '../../../issue_show/components/description.vue';
+
+export default {
+ components: {
+ Icon,
+ TitleComponent,
+ DescriptionComponent,
+ },
+ computed: {
+ ...mapGetters(['currentMergeRequest']),
+ },
+};
+</script>
+
+<template>
+ <div class="ide-merge-request-info h-100 d-flex flex-column">
+ <div class="detail-page-header">
+ <icon
+ name="git-merge"
+ class="align-self-center append-right-8"
+ />
+ <strong>
+ !{{ currentMergeRequest.iid }}
+ </strong>
+ </div>
+ <div class="issuable-details">
+ <title-component
+ :issuable-ref="currentMergeRequest.iid"
+ :title-html="currentMergeRequest.title_html"
+ :title-text="currentMergeRequest.title"
+ />
+ <description-component
+ :description-html="currentMergeRequest.description_html"
+ :description-text="currentMergeRequest.description"
+ :can-update="false"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/button.vue b/app/assets/javascripts/ide/components/new_dropdown/button.vue
new file mode 100644
index 00000000000..7682b34ce4d
--- /dev/null
+++ b/app/assets/javascripts/ide/components/new_dropdown/button.vue
@@ -0,0 +1,51 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ Icon,
+ },
+ props: {
+ label: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ icon: {
+ type: String,
+ required: true,
+ },
+ iconClasses: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ showLabel: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ },
+ methods: {
+ clicked() {
+ this.$emit('click');
+ },
+ },
+};
+</script>
+
+<template>
+ <button
+ :aria-label="label"
+ type="button"
+ @click.stop.prevent="clicked"
+ >
+ <icon
+ :name="icon"
+ :css-classes="iconClasses"
+ />
+ <template v-if="showLabel">
+ {{ label }}
+ </template>
+ </button>
+</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 1e398d7e1aa..c29e49ba766 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -3,12 +3,14 @@ import { mapActions } from 'vuex';
import icon from '~/vue_shared/components/icon.vue';
import newModal from './modal.vue';
import upload from './upload.vue';
+import ItemButton from './button.vue';
export default {
components: {
icon,
newModal,
upload,
+ ItemButton,
},
props: {
branch: {
@@ -20,11 +22,13 @@ export default {
required: false,
default: '',
},
+ mouseOver: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return {
- openModal: false,
- modalType: '',
dropdownOpen: false,
};
},
@@ -34,17 +38,18 @@ export default {
this.$refs.dropdownMenu.scrollIntoView();
});
},
+ mouseOver() {
+ if (!this.mouseOver) {
+ this.dropdownOpen = false;
+ }
+ },
},
methods: {
- ...mapActions(['createTempEntry']),
+ ...mapActions(['createTempEntry', 'openNewEntryModal']),
createNewItem(type) {
- this.modalType = type;
- this.openModal = true;
+ this.openNewEntryModal({ type, path: this.path });
this.dropdownOpen = false;
},
- hideModal() {
- this.openModal = false;
- },
openDropdown() {
this.dropdownOpen = !this.dropdownOpen;
},
@@ -58,23 +63,19 @@ export default {
:class="{
show: dropdownOpen,
}"
- class="dropdown"
+ class="dropdown d-flex"
>
<button
+ :aria-label="__('Create new file or directory')"
type="button"
- class="btn btn-sm btn-default dropdown-toggle add-to-tree"
- aria-label="Create new file or directory"
+ class="rounded border-0 d-flex ide-entry-dropdown-toggle"
@click.stop="openDropdown()"
>
<icon
- :size="12"
- name="plus"
- css-classes="float-left"
+ name="hamburger"
/>
<icon
- :size="12"
name="arrow-down"
- css-classes="float-left"
/>
</button>
<ul
@@ -82,39 +83,30 @@ export default {
class="dropdown-menu dropdown-menu-right"
>
<li>
- <a
- href="#"
- role="button"
- @click.stop.prevent="createNewItem('blob')"
- >
- {{ __('New file') }}
- </a>
+ <item-button
+ :label="__('New file')"
+ class="d-flex"
+ icon="doc-new"
+ icon-classes="mr-2"
+ @click="createNewItem('blob')"
+ />
</li>
<li>
<upload
- :branch-id="branch"
:path="path"
@create="createTempEntry"
/>
</li>
<li>
- <a
- href="#"
- role="button"
- @click.stop.prevent="createNewItem('tree')"
- >
- {{ __('New directory') }}
- </a>
+ <item-button
+ :label="__('New directory')"
+ class="d-flex"
+ icon="folder-new"
+ icon-classes="mr-2"
+ @click="createNewItem('tree')"
+ />
</li>
</ul>
</div>
- <new-modal
- v-if="openModal"
- :type="modalType"
- :branch-id="branch"
- :path="path"
- @hide="hideModal"
- @create="createTempEntry"
- />
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index 1e9668d5154..1867b7980d2 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -1,78 +1,70 @@
<script>
import { __ } from '~/locale';
-import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
+import { mapActions, mapState } from 'vuex';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
export default {
components: {
- DeprecatedModal,
- },
- props: {
- branchId: {
- type: String,
- required: true,
- },
- type: {
- type: String,
- required: true,
- },
- path: {
- type: String,
- required: true,
- },
+ GlModal,
},
data() {
return {
- entryName: this.path !== '' ? `${this.path}/` : '',
+ name: '',
};
},
computed: {
+ ...mapState(['newEntryModal']),
+ entryName: {
+ get() {
+ return this.name || (this.newEntryModal.path !== '' ? `${this.newEntryModal.path}/` : '');
+ },
+ set(val) {
+ this.name = val;
+ },
+ },
modalTitle() {
- if (this.type === 'tree') {
+ if (this.newEntryModal.type === 'tree') {
return __('Create new directory');
}
return __('Create new file');
},
buttonLabel() {
- if (this.type === 'tree') {
+ if (this.newEntryModal.type === 'tree') {
return __('Create directory');
}
return __('Create file');
},
},
- mounted() {
- this.$refs.fieldName.focus();
- },
methods: {
+ ...mapActions(['createTempEntry']),
createEntryInStore() {
- this.$emit('create', {
- branchId: this.branchId,
- name: this.entryName,
- type: this.type,
+ this.createTempEntry({
+ name: this.name,
+ type: this.newEntryModal.type,
});
-
- this.hideModal();
},
- hideModal() {
- this.$emit('hide');
+ focusInput() {
+ setTimeout(() => {
+ this.$refs.fieldName.focus();
+ });
},
},
};
</script>
<template>
- <deprecated-modal
- :title="modalTitle"
- :primary-button-label="buttonLabel"
- kind="success"
- @cancel="hideModal"
+ <gl-modal
+ id="ide-new-entry"
+ :header-title-text="modalTitle"
+ :footer-primary-button-text="buttonLabel"
+ footer-primary-button-variant="success"
@submit="createEntryInStore"
+ @open="focusInput"
>
- <form
- slot="body"
+ <div
class="form-group row"
- @submit.prevent="createEntryInStore"
>
<label class="label-light col-form-label col-sm-3">
{{ __('Name') }}
@@ -85,6 +77,6 @@ export default {
class="form-control"
/>
</div>
- </form>
- </deprecated-modal>
+ </div>
+ </gl-modal>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index 677b282bd61..5b1743bb30e 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -1,71 +1,85 @@
<script>
- export default {
- props: {
- branchId: {
- type: String,
- required: true,
- },
- path: {
- type: String,
- required: false,
- default: '',
- },
+import Icon from '~/vue_shared/components/icon.vue';
+import ItemButton from './button.vue';
+
+export default {
+ components: {
+ Icon,
+ ItemButton,
+ },
+ props: {
+ path: {
+ type: String,
+ required: false,
+ default: '',
},
- mounted() {
- this.$refs.fileUpload.addEventListener('change', this.openFile);
+ showLabel: {
+ type: Boolean,
+ required: false,
+ default: true,
},
- beforeDestroy() {
- this.$refs.fileUpload.removeEventListener('change', this.openFile);
+ buttonCssClasses: {
+ type: String,
+ required: false,
+ default: null,
},
- methods: {
- createFile(target, file, isText) {
- const { name } = file;
- let { result } = target;
+ },
+ mounted() {
+ this.$refs.fileUpload.addEventListener('change', this.openFile);
+ },
+ beforeDestroy() {
+ this.$refs.fileUpload.removeEventListener('change', this.openFile);
+ },
+ methods: {
+ createFile(target, file, isText) {
+ const { name } = file;
+ let { result } = target;
- if (!isText) {
- // eslint-disable-next-line prefer-destructuring
- result = result.split('base64,')[1];
- }
+ if (!isText) {
+ // eslint-disable-next-line prefer-destructuring
+ result = result.split('base64,')[1];
+ }
- this.$emit('create', {
- name: `${(this.path ? `${this.path}/` : '')}${name}`,
- branchId: this.branchId,
- type: 'blob',
- content: result,
- base64: !isText,
- });
- },
- readFile(file) {
- const reader = new FileReader();
- const isText = file.type.match(/text.*/) !== null;
+ this.$emit('create', {
+ name: `${this.path ? `${this.path}/` : ''}${name}`,
+ type: 'blob',
+ content: result,
+ base64: !isText,
+ });
+ },
+ readFile(file) {
+ const reader = new FileReader();
+ const isText = file.type.match(/text.*/) !== null;
- reader.addEventListener('load', e => this.createFile(e.target, file, isText), { once: true });
+ reader.addEventListener('load', e => this.createFile(e.target, file, isText), { once: true });
- if (isText) {
- reader.readAsText(file);
- } else {
- reader.readAsDataURL(file);
- }
- },
- openFile() {
- Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file));
- },
- startFileUpload() {
- this.$refs.fileUpload.click();
- },
+ if (isText) {
+ reader.readAsText(file);
+ } else {
+ reader.readAsDataURL(file);
+ }
+ },
+ openFile() {
+ Array.from(this.$refs.fileUpload.files).forEach(file => this.readFile(file));
},
- };
+ startFileUpload() {
+ this.$refs.fileUpload.click();
+ },
+ },
+};
</script>
<template>
<div>
- <a
- href="#"
- role="button"
- @click.stop.prevent="startFileUpload"
- >
- {{ __('Upload file') }}
- </a>
+ <item-button
+ :class="buttonCssClasses"
+ :show-label="showLabel"
+ :icon-classes="showLabel ? 'mr-2' : ''"
+ :label="__('Upload file')"
+ class="d-flex"
+ icon="upload"
+ @click="startFileUpload"
+ />
<input
id="file-upload"
ref="fileUpload"
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index 5cd2c9ce188..e4a5fcc67c4 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -5,6 +5,7 @@ import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
+import MergeRequestInfo from '../merge_requests/info.vue';
import ResizablePanel from '../resizable_panel.vue';
export default {
@@ -16,9 +17,10 @@ export default {
PipelinesList,
JobsDetail,
ResizablePanel,
+ MergeRequestInfo,
},
computed: {
- ...mapState(['rightPane']),
+ ...mapState(['rightPane', 'currentMergeRequestId']),
pipelinesActive() {
return (
this.rightPane === rightSidebarViews.pipelines ||
@@ -54,10 +56,33 @@ export default {
</resizable-panel>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
+ <li
+ v-if="currentMergeRequestId"
+ >
+ <button
+ v-tooltip
+ :title="__('Merge Request')"
+ :aria-label="__('Merge Request')"
+ :class="{
+ active: rightPane === $options.rightSidebarViews.mergeRequestInfo
+ }"
+ data-container="body"
+ data-placement="left"
+ class="ide-sidebar-link is-right"
+ type="button"
+ @click="clickTab($event, $options.rightSidebarViews.mergeRequestInfo)"
+ >
+ <icon
+ :size="16"
+ name="text-description"
+ />
+ </button>
+ </li>
<li>
<button
v-tooltip
:title="__('Pipelines')"
+ :aria-label="__('Pipelines')"
:class="{
active: pipelinesActive
}"
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index f490a3a2a39..3b4dd5ae9aa 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -40,6 +40,11 @@ export default {
default: false,
},
},
+ data() {
+ return {
+ mouseOver: false,
+ };
+ },
computed: {
...mapGetters([
'getChangesInFolder',
@@ -142,6 +147,9 @@ export default {
hasUrlAtCurrentRoute() {
return this.$router.currentRoute.path === `/project${this.file.url}`;
},
+ toggleHover(over) {
+ this.mouseOver = over;
+ },
},
};
</script>
@@ -153,6 +161,8 @@ export default {
class="file"
role="button"
@click="clickFile"
+ @mouseover="toggleHover(true)"
+ @mouseout="toggleHover(false)"
>
<div
class="file-name"
@@ -206,6 +216,7 @@ export default {
:project-id="file.projectId"
:branch="file.branchId"
:path="file.path"
+ :mouse-over="mouseOver"
class="float-right prepend-left-8"
/>
</div>
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 12e0c3aeef0..45d36f6f42c 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -31,6 +31,7 @@ export const diffModes = {
export const rightSidebarViews = {
pipelines: 'pipelines-list',
jobsDetail: 'jobs-detail',
+ mergeRequestInfo: 'merge-request-info',
};
export const stageKeys = {
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index cc8dbb942d8..44c35e9a5a5 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -101,6 +101,7 @@ router.beforeEach((to, from, next) => {
store
.dispatch('getMergeRequestData', {
projectId: fullProjectId,
+ targetProjectId: to.query.target_project,
mergeRequestId: to.params.mrid,
})
.then(mr => {
@@ -119,12 +120,14 @@ router.beforeEach((to, from, next) => {
.then(() =>
store.dispatch('getMergeRequestVersions', {
projectId: fullProjectId,
+ targetProjectId: to.query.target_project,
mergeRequestId: to.params.mrid,
}),
)
.then(() =>
store.dispatch('getMergeRequestChanges', {
projectId: fullProjectId,
+ targetProjectId: to.query.target_project,
mergeRequestId: to.params.mrid,
}),
)
diff --git a/app/assets/javascripts/ide/lib/themes/gl_theme.js b/app/assets/javascripts/ide/lib/themes/gl_theme.js
index 2fc96250c7d..439ae50448a 100644
--- a/app/assets/javascripts/ide/lib/themes/gl_theme.js
+++ b/app/assets/javascripts/ide/lib/themes/gl_theme.js
@@ -9,6 +9,7 @@ export default {
'diffEditor.insertedTextBackground': '#ddfbe6',
'diffEditor.removedTextBackground': '#f9d7dc',
'editor.selectionBackground': '#aad6f8',
+ 'editorIndentGuide.activeBackground': '#cccccc',
},
},
};
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 3e939f0c1a3..49a481f25d5 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -40,8 +40,8 @@ export default {
getProjectData(namespace, project) {
return Api.project(`${namespace}/${project}`);
},
- getProjectMergeRequestData(projectId, mergeRequestId) {
- return Api.mergeRequest(projectId, mergeRequestId);
+ getProjectMergeRequestData(projectId, mergeRequestId, params = {}) {
+ return Api.mergeRequest(projectId, mergeRequestId, params);
},
getProjectMergeRequestChanges(projectId, mergeRequestId) {
return Api.mergeRequestChanges(projectId, mergeRequestId);
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 5e91fa915ff..b5bd6f5a6bb 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -52,7 +52,7 @@ export const setResizingStatus = ({ commit }, resizing) => {
export const createTempEntry = (
{ state, commit, dispatch },
- { branchId, name, type, content = '', base64 = false },
+ { name, type, content = '', base64 = false },
) =>
new Promise(resolve => {
const worker = new FilesDecoratorWorker();
@@ -81,7 +81,7 @@ export const createTempEntry = (
commit(types.CREATE_TMP_ENTRY, {
data,
projectId: state.currentProjectId,
- branchId,
+ branchId: state.currentBranchId,
});
if (type === 'blob') {
@@ -100,7 +100,7 @@ export const createTempEntry = (
worker.postMessage({
data: [fullName],
projectId: state.currentProjectId,
- branchId,
+ branchId: state.currentBranchId,
type,
tempFile: true,
base64,
@@ -178,6 +178,13 @@ export const setLinks = ({ commit }, links) => commit(types.SET_LINKS, links);
export const setErrorMessage = ({ commit }, errorMessage) =>
commit(types.SET_ERROR_MESSAGE, errorMessage);
+export const openNewEntryModal = ({ commit }, { type, path = '' }) => {
+ commit(types.OPEN_NEW_ENTRY_MODAL, { type, path });
+
+ // open the modal manually so we don't mess around with dropdown/rows
+ $('#ide-new-entry').modal('show');
+};
+
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js
index 4aa151abcb7..1887b77b00b 100644
--- a/app/assets/javascripts/ide/stores/actions/merge_request.js
+++ b/app/assets/javascripts/ide/stores/actions/merge_request.js
@@ -4,12 +4,14 @@ import * as types from '../mutation_types';
export const getMergeRequestData = (
{ commit, dispatch, state },
- { projectId, mergeRequestId, force = false } = {},
+ { projectId, mergeRequestId, targetProjectId = null, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId] || force) {
service
- .getProjectMergeRequestData(projectId, mergeRequestId)
+ .getProjectMergeRequestData(targetProjectId || projectId, mergeRequestId, {
+ render_html: true,
+ })
.then(({ data }) => {
commit(types.SET_MERGE_REQUEST, {
projectPath: projectId,
@@ -38,12 +40,12 @@ export const getMergeRequestData = (
export const getMergeRequestChanges = (
{ commit, dispatch, state },
- { projectId, mergeRequestId, force = false } = {},
+ { projectId, mergeRequestId, targetProjectId = null, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId].changes.length || force) {
service
- .getProjectMergeRequestChanges(projectId, mergeRequestId)
+ .getProjectMergeRequestChanges(targetProjectId || projectId, mergeRequestId)
.then(({ data }) => {
commit(types.SET_MERGE_REQUEST_CHANGES, {
projectPath: projectId,
@@ -71,12 +73,12 @@ export const getMergeRequestChanges = (
export const getMergeRequestVersions = (
{ commit, dispatch, state },
- { projectId, mergeRequestId, force = false } = {},
+ { projectId, mergeRequestId, targetProjectId = null, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (!state.projects[projectId].mergeRequests[mergeRequestId].versions.length || force) {
service
- .getProjectMergeRequestVersions(projectId, mergeRequestId)
+ .getProjectMergeRequestVersions(targetProjectId || projectId, mergeRequestId)
.then(res => res.data)
.then(data => {
commit(types.SET_MERGE_REQUEST_VERSIONS, {
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index cdd8076952f..6ef938b0ae2 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -31,7 +31,7 @@ export const fetchMergeRequests = ({ dispatch, state: { state } }, { type, searc
dispatch('requestMergeRequests', type);
dispatch('resetMergeRequests', type);
- Api.mergeRequests({ scope, state, search })
+ return Api.mergeRequests({ scope, state, search })
.then(({ data }) => dispatch('receiveMergeRequestsSuccess', { type, data }))
.catch(() => dispatch('receiveMergeRequestsError', { type, search }));
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 8cb01f25223..3e67b222e66 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -102,7 +102,7 @@ export const receiveJobsSuccess = ({ commit }, { id, data }) =>
export const fetchJobs = ({ dispatch }, stage) => {
dispatch('requestJobs', stage.id);
- axios
+ return axios
.get(stage.dropdownPath)
.then(({ data }) => dispatch('receiveJobsSuccess', { id: stage.id, data }))
.catch(() => dispatch('receiveJobsError', stage));
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 555802e1811..8d6f9ccaf34 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -74,3 +74,5 @@ export const CLEAR_PROJECTS = 'CLEAR_PROJECTS';
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';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 702be2140e2..f8091f5b5e0 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -166,6 +166,11 @@ export default {
[types.SET_ERROR_MESSAGE](state, errorMessage) {
Object.assign(state, { errorMessage });
},
+ [types.OPEN_NEW_ENTRY_MODAL](state, { type, path }) {
+ Object.assign(state, {
+ newEntryModal: { type, path },
+ });
+ },
...projectMutations,
...mergeRequestMutation,
...fileMutations,
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index be229b2c723..0f32a267469 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -26,4 +26,8 @@ export default () => ({
rightPane: null,
links: {},
errorMessage: null,
+ newEntryModal: {
+ type: '',
+ path: '',
+ },
});
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index b6364318537..ad928484952 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -108,6 +108,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
projectPath: {
type: String,
required: true,
@@ -282,6 +287,7 @@
:issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath"
+ :markdown-version="markdownVersion"
:project-path="projectPath"
:project-namespace="projectNamespace"
:show-delete-button="showDeleteButton"
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 5f58f671c73..97acc5ba385 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -20,6 +20,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
canAttachFile: {
type: Boolean,
required: false,
@@ -47,6 +52,7 @@
<markdown-field
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :markdown-version="markdownVersion"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
>
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index 5bfc072e3da..e509bb52f7d 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -35,6 +35,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
projectPath: {
type: String,
required: true,
@@ -97,6 +102,7 @@
:form-state="formState"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :markdown-version="markdownVersion"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
/>
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index 12101c0daa5..b5e8e0ea44b 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -1,67 +1,67 @@
<script>
- import animateMixin from '../mixins/animate';
- import eventHub from '../event_hub';
- import tooltip from '../../vue_shared/directives/tooltip';
- import { spriteIcon } from '../../lib/utils/common_utils';
+import animateMixin from '../mixins/animate';
+import eventHub from '../event_hub';
+import tooltip from '../../vue_shared/directives/tooltip';
+import { spriteIcon } from '../../lib/utils/common_utils';
- export default {
- directives: {
- tooltip,
+export default {
+ directives: {
+ tooltip,
+ },
+ mixins: [animateMixin],
+ props: {
+ issuableRef: {
+ type: [String, Number],
+ required: true,
},
- mixins: [animateMixin],
- props: {
- issuableRef: {
- type: String,
- required: true,
- },
- canUpdate: {
- required: false,
- type: Boolean,
- default: false,
- },
- titleHtml: {
- type: String,
- required: true,
- },
- titleText: {
- type: String,
- required: true,
- },
- showInlineEditButton: {
- type: Boolean,
- required: false,
- default: false,
- },
+ canUpdate: {
+ required: false,
+ type: Boolean,
+ default: false,
},
- data() {
- return {
- preAnimation: false,
- pulseAnimation: false,
- titleEl: document.querySelector('title'),
- };
+ titleHtml: {
+ type: String,
+ required: true,
},
- computed: {
- pencilIcon() {
- return spriteIcon('pencil', 'link-highlight');
- },
+ titleText: {
+ type: String,
+ required: true,
},
- watch: {
- titleHtml() {
- this.setPageTitle();
- this.animateChange();
- },
+ showInlineEditButton: {
+ type: Boolean,
+ required: false,
+ default: false,
},
- methods: {
- setPageTitle() {
- const currentPageTitleScope = this.titleEl.innerText.split('·');
- currentPageTitleScope[0] = `${this.titleText} (${this.issuableRef}) `;
- this.titleEl.textContent = currentPageTitleScope.join('·');
- },
- edit() {
- eventHub.$emit('open.form');
- },
+ },
+ data() {
+ return {
+ preAnimation: false,
+ pulseAnimation: false,
+ titleEl: document.querySelector('title'),
+ };
+ },
+ computed: {
+ pencilIcon() {
+ return spriteIcon('pencil', 'link-highlight');
},
- };
+ },
+ watch: {
+ titleHtml() {
+ this.setPageTitle();
+ this.animateChange();
+ },
+ },
+ methods: {
+ setPageTitle() {
+ const currentPageTitleScope = this.titleEl.innerText.split('·');
+ currentPageTitleScope[0] = `${this.titleText} (${this.issuableRef}) `;
+ this.titleEl.textContent = currentPageTitleScope.join('·');
+ },
+ edit() {
+ eventHub.$emit('open.form');
+ },
+ },
+};
</script>
<template>
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index c9ce838cd48..2718f73a830 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -26,7 +26,7 @@ import './feature_highlight/feature_highlight_options';
import LazyLoader from './lazy_loader';
import initLogoAnimation from './logo';
import './milestone_select';
-import './projects_dropdown';
+import './frequent_items';
import initBreadcrumbs from './breadcrumb';
import initDispatcher from './dispatcher';
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 48cda28a1ae..8124ae6201f 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1251,13 +1251,15 @@ export default class Notes {
var postUrl = $originalContentEl.data('postUrl');
var targetId = $originalContentEl.data('targetId');
var targetType = $originalContentEl.data('targetType');
+ var markdownVersion = $originalContentEl.data('markdownVersion');
this.glForm = new GLForm($editForm.find('form'), this.enableGFM);
$editForm
.find('form')
.attr('action', `${postUrl}?html=true`)
- .attr('data-remote', 'true');
+ .attr('data-remote', 'true')
+ .attr('data-markdown-version', markdownVersion);
$editForm.find('.js-form-target-id').val(targetId);
$editForm.find('.js-form-target-type').val(targetType);
$editForm
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index c6a524f68cb..6612bc44e0b 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -34,6 +34,11 @@ export default {
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
data() {
return {
@@ -344,6 +349,7 @@ Please check your network connection and try again.`;
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
+ :markdown-version="markdownVersion"
:add-spacing-classes="false">
<textarea
id="note-body"
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 9c2908c477e..27ff7dea909 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -1,94 +1,90 @@
<script>
- import { mapState, mapActions } from 'vuex';
- import imageDiffHelper from '~/image_diff/helpers/index';
- import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
- import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
- import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
- import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
+import { mapState, mapActions } from 'vuex';
+import imageDiffHelper from '~/image_diff/helpers/index';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
+import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
- export default {
- components: {
- DiffFileHeader,
- SkeletonLoadingContainer,
+export default {
+ components: {
+ DiffFileHeader,
+ SkeletonLoadingContainer,
+ },
+ props: {
+ discussion: {
+ type: Object,
+ required: true,
},
- props: {
- discussion: {
- type: Object,
- required: true,
- },
+ },
+ data() {
+ return {
+ error: false,
+ };
+ },
+ computed: {
+ ...mapState({
+ noteableData: state => state.notes.noteableData,
+ }),
+ hasTruncatedDiffLines() {
+ return this.discussion.truncatedDiffLines && this.discussion.truncatedDiffLines.length !== 0;
},
- data() {
- return {
- error: false,
- };
+ isDiscussionsExpanded() {
+ return true; // TODO: @fatihacet - Fix this.
},
- computed: {
- ...mapState({
- noteableData: state => state.notes.noteableData,
- }),
- hasTruncatedDiffLines() {
- return this.discussion.truncatedDiffLines &&
- this.discussion.truncatedDiffLines.length !== 0;
- },
- isDiscussionsExpanded() {
- return true; // TODO: @fatihacet - Fix this.
- },
- isCollapsed() {
- return this.diffFile.collapsed || false;
- },
- isImageDiff() {
- return !this.diffFile.text;
- },
- diffFileClass() {
- const { text } = this.diffFile;
- return text ? 'text-file' : 'js-image-file';
- },
- diffFile() {
- return convertObjectPropsToCamelCase(this.discussion.diffFile, { deep: true });
- },
- imageDiffHtml() {
- return this.discussion.imageDiffHtml;
- },
- currentUser() {
- return this.noteableData.current_user;
- },
- userColorScheme() {
- return window.gon.user_color_scheme;
- },
- normalizedDiffLines() {
- if (this.discussion.truncatedDiffLines) {
- return this.discussion.truncatedDiffLines.map(line =>
- trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)),
- );
- }
-
- return [];
- },
+ isCollapsed() {
+ return this.diffFile.collapsed || false;
+ },
+ isImageDiff() {
+ return !this.diffFile.text;
+ },
+ diffFileClass() {
+ const { text } = this.diffFile;
+ return text ? 'text-file' : 'js-image-file';
+ },
+ diffFile() {
+ return convertObjectPropsToCamelCase(this.discussion.diffFile, { deep: true });
},
- mounted() {
- if (this.isImageDiff) {
- const canCreateNote = false;
- const renderCommentBadge = true;
- imageDiffHelper.initImageDiff(this.$refs.fileHolder, canCreateNote, renderCommentBadge);
- } else if (!this.hasTruncatedDiffLines) {
- this.fetchDiff();
+ imageDiffHtml() {
+ return this.discussion.imageDiffHtml;
+ },
+ userColorScheme() {
+ return window.gon.user_color_scheme;
+ },
+ normalizedDiffLines() {
+ if (this.discussion.truncatedDiffLines) {
+ return this.discussion.truncatedDiffLines.map(line =>
+ trimFirstCharOfLineContent(convertObjectPropsToCamelCase(line)),
+ );
}
+
+ return [];
+ },
+ },
+ mounted() {
+ if (this.isImageDiff) {
+ const canCreateNote = false;
+ const renderCommentBadge = true;
+ imageDiffHelper.initImageDiff(this.$refs.fileHolder, canCreateNote, renderCommentBadge);
+ } else if (!this.hasTruncatedDiffLines) {
+ this.fetchDiff();
+ }
+ },
+ methods: {
+ ...mapActions(['fetchDiscussionDiffLines']),
+ rowTag(html) {
+ return html.outerHTML ? 'tr' : 'template';
},
- methods: {
- ...mapActions(['fetchDiscussionDiffLines']),
- rowTag(html) {
- return html.outerHTML ? 'tr' : 'template';
- },
- fetchDiff() {
- this.error = false;
- this.fetchDiscussionDiffLines(this.discussion)
- .then(this.highlight)
- .catch(() => {
- this.error = true;
- });
- },
+ fetchDiff() {
+ this.error = false;
+ this.fetchDiscussionDiffLines(this.discussion)
+ .then(this.highlight)
+ .catch(() => {
+ this.error = true;
+ });
},
- };
+ },
+};
</script>
<template>
@@ -99,7 +95,7 @@
>
<diff-file-header
:diff-file="diffFile"
- :current-user="currentUser"
+ :can-current-user-fork="false"
:discussions-expanded="isDiscussionsExpanded"
:expanded="!isCollapsed"
/>
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index d2db68df98e..6f4a0709825 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -92,6 +92,7 @@ export default {
:is-editing="isEditing"
:note-body="noteBody"
:note-id="note.id"
+ :markdown-version="note.cached_markdown_version"
@handleFormUpdate="handleFormUpdate"
@cancelForm="formCancelHandler"
/>
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index a4e3faa5d75..26482a02e00 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -24,6 +24,11 @@ export default {
required: false,
default: 0,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
saveButtonTitle: {
type: String,
required: false,
@@ -156,6 +161,7 @@ export default {
<markdown-field
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :markdown-version="markdownVersion"
:quick-actions-docs-path="quickActionsDocsPath"
:add-spacing-classes="false">
<textarea
@@ -194,7 +200,7 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
class="btn btn-cancel note-edit-cancel js-close-discussion-note-form"
type="button"
@click="cancelHandler()">
- {{ __('Discard draft') }}
+ Cancel
</button>
</div>
</form>
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index a8995021699..9b8713b40fb 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -43,6 +43,11 @@ export default {
required: false,
default: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
},
data() {
return {
@@ -192,6 +197,7 @@ export default {
<comment-form
:noteable-type="noteableType"
+ :markdown-version="markdownVersion"
/>
</div>
</template>
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index eed3a82854d..6dd4c9d66ac 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
const notesDataset = document.getElementById('js-vue-notes').dataset;
const parsedUserData = JSON.parse(notesDataset.currentUserData);
const noteableData = JSON.parse(notesDataset.noteableData);
+ const { markdownVersion } = notesDataset;
let currentUserData = {};
noteableData.noteableType = notesDataset.noteableType;
@@ -33,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => {
return {
noteableData,
currentUserData,
+ markdownVersion,
notesData: JSON.parse(notesDataset.notesData),
};
},
@@ -42,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => {
noteableData: this.noteableData,
notesData: this.notesData,
userData: this.currentUserData,
+ markdownVersion: this.markdownVersion,
},
});
},
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 671fa4d7d22..3eefbe11c37 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -15,6 +15,8 @@ let eTagPoll;
export const expandDiscussion = ({ commit }, data) => commit(types.EXPAND_DISCUSSION, data);
+export const collapseDiscussion = ({ commit }, data) => commit(types.COLLAPSE_DISCUSSION, data);
+
export const setNotesData = ({ commit }, data) => commit(types.SET_NOTES_DATA, data);
export const setNoteableData = ({ commit }, data) => commit(types.SET_NOTEABLE_DATA, data);
@@ -41,6 +43,15 @@ export const fetchDiscussions = ({ commit }, path) =>
commit(types.SET_INITIAL_DISCUSSIONS, discussions);
});
+export const refetchDiscussionById = ({ commit }, { path, discussionId }) =>
+ service
+ .fetchDiscussions(path)
+ .then(res => res.json())
+ .then(discussions => {
+ const selectedDiscussion = discussions.find(discussion => discussion.id === discussionId);
+ if (selectedDiscussion) commit(types.UPDATE_DISCUSSION, selectedDiscussion);
+ });
+
export const deleteNote = ({ commit }, note) =>
service.deleteNote(note.path).then(() => {
commit(types.DELETE_NOTE, note);
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index a25098fbc06..6f374f78691 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -1,7 +1,6 @@
export const ADD_NEW_NOTE = 'ADD_NEW_NOTE';
export const ADD_NEW_REPLY_TO_DISCUSSION = 'ADD_NEW_REPLY_TO_DISCUSSION';
export const DELETE_NOTE = 'DELETE_NOTE';
-export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
export const REMOVE_PLACEHOLDER_NOTES = 'REMOVE_PLACEHOLDER_NOTES';
export const SET_NOTES_DATA = 'SET_NOTES_DATA';
export const SET_NOTEABLE_DATA = 'SET_NOTEABLE_DATA';
@@ -11,12 +10,16 @@ export const SET_LAST_FETCHED_AT = 'SET_LAST_FETCHED_AT';
export const SET_TARGET_NOTE_HASH = 'SET_TARGET_NOTE_HASH';
export const SHOW_PLACEHOLDER_NOTE = 'SHOW_PLACEHOLDER_NOTE';
export const TOGGLE_AWARD = 'TOGGLE_AWARD';
-export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
export const UPDATE_NOTE = 'UPDATE_NOTE';
export const UPDATE_DISCUSSION = 'UPDATE_DISCUSSION';
export const SET_DISCUSSION_DIFF_LINES = 'SET_DISCUSSION_DIFF_LINES';
export const SET_NOTES_FETCHED_STATE = 'SET_NOTES_FETCHED_STATE';
+// DISCUSSION
+export const COLLAPSE_DISCUSSION = 'COLLAPSE_DISCUSSION';
+export const EXPAND_DISCUSSION = 'EXPAND_DISCUSSION';
+export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
+
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
export const REOPEN_ISSUE = 'REOPEN_ISSUE';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index e5e40ce07fa..ab6a95e2601 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -58,6 +58,11 @@ export default {
discussion.expanded = true;
},
+ [types.COLLAPSE_DISCUSSION](state, { discussionId }) {
+ const discussion = utils.findNoteObjectById(state.discussions, discussionId);
+ discussion.expanded = false;
+ },
+
[types.REMOVE_PLACEHOLDER_NOTES](state) {
const { discussions } = state;
@@ -114,7 +119,6 @@ export default {
Object.assign(state, { discussions });
},
-
[types.SET_LAST_FETCHED_AT](state, fetchedAt) {
Object.assign(state, { lastFetchedAt: fetchedAt });
},
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 9aa83ce6269..ff19b9a9c30 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -39,7 +39,6 @@ export default class Todos {
}
initFilters() {
- this.initFilterDropdown($('.js-group-search'), 'group_id', ['text']);
this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']);
this.initFilterDropdown($('.js-type-search'), 'type');
this.initFilterDropdown($('.js-action-search'), 'action_id');
@@ -54,16 +53,7 @@ export default class Todos {
filterable: searchFields ? true : false,
search: { fields: searchFields },
data: $dropdown.data('data'),
- clicked: () => {
- const $formEl = $dropdown.closest('form.filter-form');
- const mutexDropdowns = {
- group_id: 'project_id',
- project_id: 'group_id',
- };
-
- $formEl.find(`input[name="${mutexDropdowns[fieldName]}"]`).remove();
- $formEl.submit();
- },
+ clicked: () => $dropdown.closest('form.filter-form').submit(),
});
}
diff --git a/app/assets/javascripts/pages/profiles/keys/index.js b/app/assets/javascripts/pages/profiles/keys/index.js
new file mode 100644
index 00000000000..1cd3ee1dfdb
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/keys/index.js
@@ -0,0 +1,16 @@
+import AddSshKeyValidation from '~/profile/add_ssh_key_validation';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const input = document.querySelector('.js-add-ssh-key-validation-input');
+ const warning = document.querySelector('.js-add-ssh-key-validation-warning');
+ const originalSubmit = input.form.querySelector('.js-add-ssh-key-validation-original-submit');
+ const confirmSubmit = warning.querySelector('.js-add-ssh-key-validation-confirm-submit');
+
+ const addSshKeyValidation = new AddSshKeyValidation(
+ input,
+ warning,
+ originalSubmit,
+ confirmSubmit,
+ );
+ addSshKeyValidation.register();
+});
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index b76965f280b..0fdb0a080cf 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -1,13 +1,10 @@
<script>
import $ from 'jquery';
-import PerformanceBarService from '../services/performance_bar_service';
import detailedMetric from './detailed_metric.vue';
import requestSelector from './request_selector.vue';
import simpleMetric from './simple_metric.vue';
-import Flash from '../../flash';
-
export default {
components: {
detailedMetric,
@@ -69,37 +66,13 @@ export default {
},
},
mounted() {
- this.interceptor = PerformanceBarService.registerInterceptor(
- this.peekUrl,
- this.loadRequestDetails,
- );
-
- this.loadRequestDetails(this.requestId, window.location.href);
this.currentRequest = this.requestId;
if (this.lineProfileModal.length) {
this.lineProfileModal.modal('toggle');
}
},
- beforeDestroy() {
- PerformanceBarService.removeInterceptor(this.interceptor);
- },
methods: {
- loadRequestDetails(requestId, requestUrl) {
- if (!this.store.canTrackRequest(requestUrl)) {
- return;
- }
-
- this.store.addRequest(requestId, requestUrl);
-
- PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
- .then(res => {
- this.store.addRequestDetails(requestId, res.data.data);
- })
- .catch(() =>
- Flash(`Error getting performance bar results for ${requestId}`),
- );
- },
changeCurrentRequest(newRequestId) {
this.currentRequest = newRequestId;
},
diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js
index 4a98aed7679..41880d27516 100644
--- a/app/assets/javascripts/performance_bar/index.js
+++ b/app/assets/javascripts/performance_bar/index.js
@@ -1,12 +1,13 @@
import Vue from 'vue';
-import performanceBarApp from './components/performance_bar_app.vue';
+import Flash from '../flash';
+import PerformanceBarService from './services/performance_bar_service';
import PerformanceBarStore from './stores/performance_bar_store';
export default ({ container }) =>
new Vue({
el: container,
components: {
- performanceBarApp,
+ performanceBarApp: () => import('./components/performance_bar_app.vue'),
},
data() {
const performanceBarData = document.querySelector(this.$options.el)
@@ -21,6 +22,34 @@ export default ({ container }) =>
profileUrl: performanceBarData.profileUrl,
};
},
+ mounted() {
+ this.interceptor = PerformanceBarService.registerInterceptor(
+ this.peekUrl,
+ this.loadRequestDetails,
+ );
+
+ this.loadRequestDetails(this.requestId, window.location.href);
+ },
+ beforeDestroy() {
+ PerformanceBarService.removeInterceptor(this.interceptor);
+ },
+ methods: {
+ loadRequestDetails(requestId, requestUrl) {
+ if (!this.store.canTrackRequest(requestUrl)) {
+ return;
+ }
+
+ this.store.addRequest(requestId, requestUrl);
+
+ PerformanceBarService.fetchRequestDetails(this.peekUrl, requestId)
+ .then(res => {
+ this.store.addRequestDetails(requestId, res.data.data);
+ })
+ .catch(() =>
+ Flash(`Error getting performance bar results for ${requestId}`),
+ );
+ },
+ },
render(createElement) {
return createElement('performance-bar-app', {
props: {
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 56fdb858088..c7df69c69ed 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -175,6 +175,7 @@ export default {
<span
:aria-label="stage.title"
aria-hidden="true"
+ class="no-pointer-events"
>
<icon :name="borderlessIcon" />
</span>
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 0e973cab4d2..0964baf8954 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -28,12 +28,16 @@ MarkdownPreview.prototype.ajaxCache = {};
MarkdownPreview.prototype.showPreview = function ($form) {
var mdText;
+ var markdownVersion;
+ var url;
var preview = $form.find('.js-md-preview');
- var url = preview.data('url');
if (preview.hasClass('md-preview-loading')) {
return;
}
+
mdText = $form.find('textarea.markdown-area').val();
+ markdownVersion = $form.attr('data-markdown-version');
+ url = this.versionedPreviewPath(preview.data('url'), markdownVersion);
if (mdText.trim().length === 0) {
preview.text(this.emptyMessage);
@@ -59,6 +63,14 @@ MarkdownPreview.prototype.showPreview = function ($form) {
}
};
+MarkdownPreview.prototype.versionedPreviewPath = function (markdownPreviewPath, markdownVersion) {
+ if (typeof markdownVersion === 'undefined') {
+ return markdownPreviewPath;
+ }
+
+ return `${markdownPreviewPath}${markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'}markdown_version=${markdownVersion}`;
+};
+
MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
if (!url) {
return;
diff --git a/app/assets/javascripts/profile/add_ssh_key_validation.js b/app/assets/javascripts/profile/add_ssh_key_validation.js
new file mode 100644
index 00000000000..ab6a6c1896c
--- /dev/null
+++ b/app/assets/javascripts/profile/add_ssh_key_validation.js
@@ -0,0 +1,43 @@
+export default class AddSshKeyValidation {
+ constructor(inputElement, warningElement, originalSubmitElement, confirmSubmitElement) {
+ this.inputElement = inputElement;
+ this.form = inputElement.form;
+
+ this.warningElement = warningElement;
+
+ this.originalSubmitElement = originalSubmitElement;
+ this.confirmSubmitElement = confirmSubmitElement;
+
+ this.isValid = false;
+ }
+
+ register() {
+ this.form.addEventListener('submit', event => this.submit(event));
+
+ this.confirmSubmitElement.addEventListener('click', () => {
+ this.isValid = true;
+ this.form.submit();
+ });
+
+ this.inputElement.addEventListener('input', () => this.toggleWarning(false));
+ }
+
+ submit(event) {
+ this.isValid = AddSshKeyValidation.isPublicKey(this.inputElement.value);
+
+ if (this.isValid) return true;
+
+ event.preventDefault();
+ this.toggleWarning(true);
+ return false;
+ }
+
+ toggleWarning(isVisible) {
+ this.warningElement.classList.toggle('hide', !isVisible);
+ this.originalSubmitElement.classList.toggle('hide', isVisible);
+ }
+
+ static isPublicKey(value) {
+ return /^(ssh|ecdsa-sha2)-/.test(value);
+ }
+}
diff --git a/app/assets/javascripts/projects_dropdown/components/app.vue b/app/assets/javascripts/projects_dropdown/components/app.vue
deleted file mode 100644
index 73d49488299..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/app.vue
+++ /dev/null
@@ -1,158 +0,0 @@
-<script>
-import bs from '../../breakpoints';
-import eventHub from '../event_hub';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
-import projectsListFrequent from './projects_list_frequent.vue';
-import projectsListSearch from './projects_list_search.vue';
-
-import search from './search.vue';
-
-export default {
- components: {
- search,
- loadingIcon,
- projectsListFrequent,
- projectsListSearch,
- },
- props: {
- currentProject: {
- type: Object,
- required: true,
- },
- store: {
- type: Object,
- required: true,
- },
- service: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- isLoadingProjects: false,
- isFrequentsListVisible: false,
- isSearchListVisible: false,
- isLocalStorageFailed: false,
- isSearchFailed: false,
- searchQuery: '',
- };
- },
- computed: {
- frequentProjects() {
- return this.store.getFrequentProjects();
- },
- searchProjects() {
- return this.store.getSearchedProjects();
- },
- },
- created() {
- if (this.currentProject.id) {
- this.logCurrentProjectAccess();
- }
-
- eventHub.$on('dropdownOpen', this.fetchFrequentProjects);
- eventHub.$on('searchProjects', this.fetchSearchedProjects);
- eventHub.$on('searchCleared', this.handleSearchClear);
- eventHub.$on('searchFailed', this.handleSearchFailure);
- },
- beforeDestroy() {
- eventHub.$off('dropdownOpen', this.fetchFrequentProjects);
- eventHub.$off('searchProjects', this.fetchSearchedProjects);
- eventHub.$off('searchCleared', this.handleSearchClear);
- eventHub.$off('searchFailed', this.handleSearchFailure);
- },
- methods: {
- toggleFrequentProjectsList(state) {
- this.isLoadingProjects = !state;
- this.isSearchListVisible = !state;
- this.isFrequentsListVisible = state;
- },
- toggleSearchProjectsList(state) {
- this.isLoadingProjects = !state;
- this.isFrequentsListVisible = !state;
- this.isSearchListVisible = state;
- },
- toggleLoader(state) {
- this.isFrequentsListVisible = !state;
- this.isSearchListVisible = !state;
- this.isLoadingProjects = state;
- },
- fetchFrequentProjects() {
- const screenSize = bs.getBreakpointSize();
- if (this.searchQuery && (screenSize !== 'sm' && screenSize !== 'xs')) {
- this.toggleSearchProjectsList(true);
- } else {
- this.toggleLoader(true);
- this.isLocalStorageFailed = false;
- const projects = this.service.getFrequentProjects();
- if (projects) {
- this.toggleFrequentProjectsList(true);
- this.store.setFrequentProjects(projects);
- } else {
- this.isLocalStorageFailed = true;
- this.toggleFrequentProjectsList(true);
- this.store.setFrequentProjects([]);
- }
- }
- },
- fetchSearchedProjects(searchQuery) {
- this.searchQuery = searchQuery;
- this.toggleLoader(true);
- this.service
- .getSearchedProjects(this.searchQuery)
- .then(res => res.json())
- .then(results => {
- this.toggleSearchProjectsList(true);
- this.store.setSearchedProjects(results);
- })
- .catch(() => {
- this.isSearchFailed = true;
- this.toggleSearchProjectsList(true);
- });
- },
- logCurrentProjectAccess() {
- this.service.logProjectAccess(this.currentProject);
- },
- handleSearchClear() {
- this.searchQuery = '';
- this.toggleFrequentProjectsList(true);
- this.store.clearSearchedProjects();
- },
- handleSearchFailure() {
- this.isSearchFailed = true;
- this.toggleSearchProjectsList(true);
- },
- },
-};
-</script>
-
-<template>
- <div>
- <search/>
- <loading-icon
- v-if="isLoadingProjects"
- :label="s__('ProjectsDropdown|Loading projects')"
- class="loading-animation prepend-top-20"
- size="2"
- />
- <div
- v-if="isFrequentsListVisible"
- class="section-header"
- >
- {{ s__('ProjectsDropdown|Frequently visited') }}
- </div>
- <projects-list-frequent
- v-if="isFrequentsListVisible"
- :local-storage-failed="isLocalStorageFailed"
- :projects="frequentProjects"
- />
- <projects-list-search
- v-if="isSearchListVisible"
- :search-failed="isSearchFailed"
- :matcher="searchQuery"
- :projects="searchProjects"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
deleted file mode 100644
index 625e0aa548c..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<script>
- import { s__ } from '../../locale';
- import projectsListItem from './projects_list_item.vue';
-
- export default {
- components: {
- projectsListItem,
- },
- props: {
- projects: {
- type: Array,
- required: true,
- },
- localStorageFailed: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- isListEmpty() {
- return this.projects.length === 0;
- },
- listEmptyMessage() {
- return this.localStorageFailed ?
- s__('ProjectsDropdown|This feature requires browser localStorage support') :
- s__('ProjectsDropdown|Projects you visit often will appear here');
- },
- },
- };
-</script>
-
-<template>
- <div
- class="projects-list-frequent-container"
- >
- <ul
- class="list-unstyled"
- >
- <li
- v-if="isListEmpty"
- class="section-empty"
- >
- {{ listEmptyMessage }}
- </li>
- <projects-list-item
- v-for="(project, index) in projects"
- v-else
- :key="index"
- :project-id="project.id"
- :project-name="project.name"
- :namespace="project.namespace"
- :web-url="project.webUrl"
- :avatar-url="project.avatarUrl"
- />
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
deleted file mode 100644
index eafbf6c99e2..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-<script>
- /* eslint-disable vue/require-default-prop, vue/require-prop-types */
- import identicon from '../../vue_shared/components/identicon.vue';
-
- export default {
- components: {
- identicon,
- },
- props: {
- matcher: {
- type: String,
- required: false,
- },
- projectId: {
- type: Number,
- required: true,
- },
- projectName: {
- type: String,
- required: true,
- },
- namespace: {
- type: String,
- required: true,
- },
- webUrl: {
- type: String,
- required: true,
- },
- avatarUrl: {
- required: true,
- validator(value) {
- return value === null || typeof value === 'string';
- },
- },
- },
- computed: {
- hasAvatar() {
- return this.avatarUrl !== null;
- },
- highlightedProjectName() {
- if (this.matcher) {
- const matcherRegEx = new RegExp(this.matcher, 'gi');
- const matches = this.projectName.match(matcherRegEx);
-
- if (matches && matches.length > 0) {
- return this.projectName.replace(matches[0], `<b>${matches[0]}</b>`);
- }
- }
- return this.projectName;
- },
- /**
- * Smartly truncates project namespace by doing two things;
- * 1. Only include Group names in path by removing project name
- * 2. Only include first and last group names in the path
- * when namespace has more than 2 groups present
- *
- * First part (removal of project name from namespace) can be
- * done from backend but doing so involves migration of
- * existing project namespaces which is not wise thing to do.
- */
- truncatedNamespace() {
- const namespaceArr = this.namespace.split(' / ');
- namespaceArr.splice(-1, 1);
- let namespace = namespaceArr.join(' / ');
-
- if (namespaceArr.length > 2) {
- namespace = `${namespaceArr[0]} / ... / ${namespaceArr.pop()}`;
- }
-
- return namespace;
- },
- },
- };
-</script>
-
-<template>
- <li
- class="projects-list-item-container"
- >
- <a
- :href="webUrl"
- class="clearfix"
- >
- <div
- class="project-item-avatar-container"
- >
- <img
- v-if="hasAvatar"
- :src="avatarUrl"
- class="avatar s32"
- />
- <identicon
- v-else
- :entity-id="projectId"
- :entity-name="projectName"
- size-class="s32"
- />
- </div>
- <div
- class="project-item-metadata-container"
- >
- <div
- :title="projectName"
- class="project-title"
- v-html="highlightedProjectName"
- >
- </div>
- <div
- :title="namespace"
- class="project-namespace"
- >{{ truncatedNamespace }}</div>
- </div>
- </a>
- </li>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
deleted file mode 100644
index 76e9cb9e53f..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
+++ /dev/null
@@ -1,63 +0,0 @@
-<script>
-import { s__ } from '../../locale';
-import projectsListItem from './projects_list_item.vue';
-
-export default {
- components: {
- projectsListItem,
- },
- props: {
- matcher: {
- type: String,
- required: true,
- },
- projects: {
- type: Array,
- required: true,
- },
- searchFailed: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- isListEmpty() {
- return this.projects.length === 0;
- },
- listEmptyMessage() {
- return this.searchFailed ?
- s__('ProjectsDropdown|Something went wrong on our end.') :
- s__('ProjectsDropdown|Sorry, no projects matched your search');
- },
- },
-};
-</script>
-
-<template>
- <div
- class="projects-list-search-container"
- >
- <ul
- class="list-unstyled"
- >
- <li
- v-if="isListEmpty"
- :class="{ 'section-failure': searchFailed }"
- class="section-empty"
- >
- {{ listEmptyMessage }}
- </li>
- <projects-list-item
- v-for="(project, index) in projects"
- v-else
- :key="index"
- :project-id="project.id"
- :project-name="project.name"
- :namespace="project.namespace"
- :web-url="project.webUrl"
- :avatar-url="project.avatarUrl"
- :matcher="matcher"
- />
- </ul>
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/components/search.vue b/app/assets/javascripts/projects_dropdown/components/search.vue
deleted file mode 100644
index 28f2a18f2a6..00000000000
--- a/app/assets/javascripts/projects_dropdown/components/search.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
- import _ from 'underscore';
- import eventHub from '../event_hub';
-
- export default {
- data() {
- return {
- searchQuery: '',
- };
- },
- watch: {
- searchQuery() {
- this.handleInput();
- },
- },
- mounted() {
- eventHub.$on('dropdownOpen', this.setFocus);
- },
- beforeDestroy() {
- eventHub.$off('dropdownOpen', this.setFocus);
- },
- methods: {
- setFocus() {
- this.$refs.search.focus();
- },
- emitSearchEvents() {
- if (this.searchQuery) {
- eventHub.$emit('searchProjects', this.searchQuery);
- } else {
- eventHub.$emit('searchCleared');
- }
- },
- /**
- * Callback function within _.debounce is intentionally
- * kept as ES5 `function() {}` instead of ES6 `() => {}`
- * as it otherwise messes up function context
- * and component reference is no longer accessible via `this`
- */
- // eslint-disable-next-line func-names
- handleInput: _.debounce(function () {
- this.emitSearchEvents();
- }, 500),
- },
- };
-</script>
-
-<template>
- <div
- class="search-input-container d-none d-sm-block"
- >
- <input
- ref="search"
- v-model="searchQuery"
- :placeholder="s__('ProjectsDropdown|Search your projects')"
- type="search"
- class="form-control"
- />
- <i
- v-if="!searchQuery"
- class="search-icon fa fa-fw fa-search"
- aria-hidden="true"
- >
- </i>
- </div>
-</template>
diff --git a/app/assets/javascripts/projects_dropdown/constants.js b/app/assets/javascripts/projects_dropdown/constants.js
deleted file mode 100644
index 8937097184c..00000000000
--- a/app/assets/javascripts/projects_dropdown/constants.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export const FREQUENT_PROJECTS = {
- MAX_COUNT: 20,
- LIST_COUNT_DESKTOP: 5,
- LIST_COUNT_MOBILE: 3,
- ELIGIBLE_FREQUENCY: 3,
-};
-
-export const HOUR_IN_MS = 3600000;
-
-export const STORAGE_KEY = 'frequent-projects';
diff --git a/app/assets/javascripts/projects_dropdown/index.js b/app/assets/javascripts/projects_dropdown/index.js
deleted file mode 100644
index 6056f12aa4f..00000000000
--- a/app/assets/javascripts/projects_dropdown/index.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import $ from 'jquery';
-import Vue from 'vue';
-
-import Translate from '../vue_shared/translate';
-import eventHub from './event_hub';
-import ProjectsService from './service/projects_service';
-import ProjectsStore from './store/projects_store';
-
-import projectsDropdownApp from './components/app.vue';
-
-Vue.use(Translate);
-
-document.addEventListener('DOMContentLoaded', () => {
- const el = document.getElementById('js-projects-dropdown');
- const navEl = document.getElementById('nav-projects-dropdown');
-
- // Don't do anything if element doesn't exist (No projects dropdown)
- // This is for when the user accesses GitLab without logging in
- if (!el || !navEl) {
- return;
- }
-
- $(navEl).on('shown.bs.dropdown', () => {
- eventHub.$emit('dropdownOpen');
- });
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- components: {
- projectsDropdownApp,
- },
- data() {
- const { dataset } = this.$options.el;
- const store = new ProjectsStore();
- const service = new ProjectsService(dataset.userName);
-
- const project = {
- id: Number(dataset.projectId),
- name: dataset.projectName,
- namespace: dataset.projectNamespace,
- webUrl: dataset.projectWebUrl,
- avatarUrl: dataset.projectAvatarUrl || null,
- lastAccessedOn: Date.now(),
- };
-
- return {
- store,
- service,
- state: store.state,
- currentUserName: dataset.userName,
- currentProject: project,
- };
- },
- render(createElement) {
- return createElement('projects-dropdown-app', {
- props: {
- currentUserName: this.currentUserName,
- currentProject: this.currentProject,
- store: this.store,
- service: this.service,
- },
- });
- },
- });
-});
diff --git a/app/assets/javascripts/projects_dropdown/service/projects_service.js b/app/assets/javascripts/projects_dropdown/service/projects_service.js
deleted file mode 100644
index ed1c3deead2..00000000000
--- a/app/assets/javascripts/projects_dropdown/service/projects_service.js
+++ /dev/null
@@ -1,137 +0,0 @@
-import _ from 'underscore';
-import Vue from 'vue';
-import VueResource from 'vue-resource';
-
-import bp from '../../breakpoints';
-import Api from '../../api';
-import AccessorUtilities from '../../lib/utils/accessor';
-
-import { FREQUENT_PROJECTS, HOUR_IN_MS, STORAGE_KEY } from '../constants';
-
-Vue.use(VueResource);
-
-export default class ProjectsService {
- constructor(currentUserName) {
- this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
- this.currentUserName = currentUserName;
- this.storageKey = `${this.currentUserName}/${STORAGE_KEY}`;
- this.projectsPath = Vue.resource(Api.buildUrl(Api.projectsPath));
- }
-
- getSearchedProjects(searchQuery) {
- return this.projectsPath.get({
- simple: true,
- per_page: 20,
- membership: !!gon.current_user_id,
- order_by: 'last_activity_at',
- search: searchQuery,
- });
- }
-
- getFrequentProjects() {
- if (this.isLocalStorageAvailable) {
- return this.getTopFrequentProjects();
- }
- return null;
- }
-
- logProjectAccess(project) {
- let matchFound = false;
- let storedFrequentProjects;
-
- if (this.isLocalStorageAvailable) {
- const storedRawProjects = localStorage.getItem(this.storageKey);
-
- // Check if there's any frequent projects list set
- if (!storedRawProjects) {
- // No frequent projects list set, set one up.
- storedFrequentProjects = [];
- storedFrequentProjects.push({ ...project, frequency: 1 });
- } else {
- // Check if project is already present in frequents list
- // When found, update metadata of it.
- storedFrequentProjects = JSON.parse(storedRawProjects).map(projectItem => {
- if (projectItem.id === project.id) {
- matchFound = true;
- const diff = Math.abs(project.lastAccessedOn - projectItem.lastAccessedOn) / HOUR_IN_MS;
- const updatedProject = {
- ...project,
- frequency: projectItem.frequency,
- lastAccessedOn: projectItem.lastAccessedOn,
- };
-
- // Check if duration since last access of this project
- // is over an hour
- if (diff > 1) {
- return {
- ...updatedProject,
- frequency: updatedProject.frequency + 1,
- lastAccessedOn: Date.now(),
- };
- }
-
- return {
- ...updatedProject,
- };
- }
-
- return projectItem;
- });
-
- // Check whether currently logged project is present in frequents list
- if (!matchFound) {
- // We always keep size of frequents collection to 20 projects
- // out of which only 5 projects with
- // highest value of `frequency` and most recent `lastAccessedOn`
- // are shown in projects dropdown
- if (storedFrequentProjects.length === FREQUENT_PROJECTS.MAX_COUNT) {
- storedFrequentProjects.shift(); // Remove an item from head of array
- }
-
- storedFrequentProjects.push({ ...project, frequency: 1 });
- }
- }
-
- localStorage.setItem(this.storageKey, JSON.stringify(storedFrequentProjects));
- }
- }
-
- getTopFrequentProjects() {
- const storedFrequentProjects = JSON.parse(localStorage.getItem(this.storageKey));
- let frequentProjectsCount = FREQUENT_PROJECTS.LIST_COUNT_DESKTOP;
-
- if (!storedFrequentProjects) {
- return [];
- }
-
- if (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'xs') {
- frequentProjectsCount = FREQUENT_PROJECTS.LIST_COUNT_MOBILE;
- }
-
- const frequentProjects = storedFrequentProjects.filter(
- project => project.frequency >= FREQUENT_PROJECTS.ELIGIBLE_FREQUENCY,
- );
-
- if (!frequentProjects || frequentProjects.length === 0) {
- return [];
- }
-
- // Sort all frequent projects in decending order of frequency
- // and then by lastAccessedOn with recent most first
- frequentProjects.sort((projectA, projectB) => {
- if (projectA.frequency < projectB.frequency) {
- return 1;
- } else if (projectA.frequency > projectB.frequency) {
- return -1;
- } else if (projectA.lastAccessedOn < projectB.lastAccessedOn) {
- return 1;
- } else if (projectA.lastAccessedOn > projectB.lastAccessedOn) {
- return -1;
- }
-
- return 0;
- });
-
- return _.first(frequentProjects, frequentProjectsCount);
- }
-}
diff --git a/app/assets/javascripts/projects_dropdown/store/projects_store.js b/app/assets/javascripts/projects_dropdown/store/projects_store.js
deleted file mode 100644
index ffefbe693f4..00000000000
--- a/app/assets/javascripts/projects_dropdown/store/projects_store.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export default class ProjectsStore {
- constructor() {
- this.state = {};
- this.state.frequentProjects = [];
- this.state.searchedProjects = [];
- }
-
- setFrequentProjects(rawProjects) {
- this.state.frequentProjects = rawProjects;
- }
-
- getFrequentProjects() {
- return this.state.frequentProjects;
- }
-
- setSearchedProjects(rawProjects) {
- this.state.searchedProjects = rawProjects.map(rawProject => ({
- id: rawProject.id,
- name: rawProject.name,
- namespace: rawProject.name_with_namespace,
- webUrl: rawProject.web_url,
- avatarUrl: rawProject.avatar_url,
- }));
- }
-
- getSearchedProjects() {
- return this.state.searchedProjects;
- }
-
- clearSearchedProjects() {
- this.state.searchedProjects = [];
- }
-}
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
deleted file mode 100644
index ffaed9c7193..00000000000
--- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
+++ /dev/null
@@ -1,98 +0,0 @@
-<script>
-import { __ } from '~/locale';
-import tooltip from '~/vue_shared/directives/tooltip';
-
-import Icon from '~/vue_shared/components/icon.vue';
-import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
-
-const MARK_TEXT = __('Mark todo as done');
-const TODO_TEXT = __('Add todo');
-
-export default {
- directives: {
- tooltip,
- },
- components: {
- Icon,
- LoadingIcon,
- },
- props: {
- issuableId: {
- type: Number,
- required: true,
- },
- issuableType: {
- type: String,
- required: true,
- },
- isTodo: {
- type: Boolean,
- required: false,
- default: true,
- },
- isActionActive: {
- type: Boolean,
- required: false,
- default: false,
- },
- collapsed: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- buttonClasses() {
- return this.collapsed ?
- 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state' :
- 'btn btn-default btn-todo issuable-header-btn float-right';
- },
- buttonLabel() {
- return this.isTodo ? MARK_TEXT : TODO_TEXT;
- },
- collapsedButtonIconClasses() {
- return this.isTodo ? 'todo-undone' : '';
- },
- collapsedButtonIcon() {
- return this.isTodo ? 'todo-done' : 'todo-add';
- },
- },
- methods: {
- handleButtonClick() {
- this.$emit('toggleTodo');
- },
- },
-};
-</script>
-
-<template>
- <button
- v-tooltip
- :class="buttonClasses"
- :title="buttonLabel"
- :aria-label="buttonLabel"
- :data-issuable-id="issuableId"
- :data-issuable-type="issuableType"
- type="button"
- data-container="body"
- data-placement="left"
- data-boundary="viewport"
- @click="handleButtonClick"
- >
- <icon
- v-show="collapsed"
- :css-classes="collapsedButtonIconClasses"
- :name="collapsedButtonIcon"
- />
- <span
- v-show="!collapsed"
- class="issuable-todo-inner"
- >
- {{ buttonLabel }}
- </span>
- <loading-icon
- v-show="isActionActive"
- :inline="true"
- />
- </button>
-</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index 5e464f8a0e2..21f21232596 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -79,66 +79,62 @@ export default {
</script>
<template>
- <div class="mr-widget-heading deploy-heading">
+ <div class="mr-widget-heading deploy-heading append-bottom-default">
<div class="ci-widget media">
- <div class="ci-status-icon ci-status-icon-success">
- <span class="js-icon-link icon-link">
- <status-icon status="success" />
- </span>
- </div>
<div class="media-body">
<div class="deploy-body">
- <template v-if="hasDeploymentMeta">
- <span>
- Deployed to
- </span>
- <a
- :href="deployment.url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="deploy-link js-deploy-meta"
+ <div class="deployment-info">
+ <template v-if="hasDeploymentMeta">
+ <span>
+ Deployed to
+ </span>
+ <a
+ :href="deployment.url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="deploy-link js-deploy-meta"
+ >
+ {{ deployment.name }}
+ </a>
+ </template>
+ <span
+ v-tooltip
+ v-if="hasDeploymentTime"
+ :title="deployment.deployed_at_formatted"
+ class="js-deploy-time"
>
- {{ deployment.name }}
- </a>
- </template>
- <template v-if="hasExternalUrls">
- <span>
- on
+ {{ deployTimeago }}
</span>
+ <memory-usage
+ v-if="hasMetrics"
+ :metrics-url="deployment.metrics_url"
+ :metrics-monitoring-url="deployment.metrics_monitoring_url"
+ />
+ </div>
+ <div>
<a
+ v-if="hasExternalUrls"
:href="deployment.external_url"
target="_blank"
rel="noopener noreferrer nofollow"
- class="deploy-link js-deploy-url"
+ class="deploy-link js-deploy-url btn btn-default btn-sm inline"
>
- {{ deployment.external_url_formatted }}
- <icon
- :size="16"
- name="external-link"
- />
+ <span>
+ View app
+ <icon name="external-link" />
+ </span>
</a>
- </template>
- <span
- v-tooltip
- v-if="hasDeploymentTime"
- :title="deployment.deployed_at_formatted"
- class="js-deploy-time"
- >
- {{ deployTimeago }}
- </span>
- <loading-button
- v-if="deployment.stop_url"
- :loading="isStopping"
- container-class="btn btn-default btn-sm prepend-left-default"
- label="Stop environment"
- @click="stopEnvironment"
- />
+ <loading-button
+ v-if="deployment.stop_url"
+ :loading="isStopping"
+ container-class="btn btn-default btn-sm inline prepend-left-4"
+ title="Stop environment"
+ @click="stopEnvironment"
+ >
+ <icon name="stop" />
+ </loading-button>
+ </div>
</div>
- <memory-usage
- v-if="hasMetrics"
- :metrics-url="deployment.metrics_url"
- :metrics-monitoring-url="deployment.metrics_monitoring_url"
- />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index 3ce9d8dc26a..a4c2289c590 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -1,8 +1,8 @@
<script>
import tooltip from '~/vue_shared/directives/tooltip';
import { n__ } from '~/locale';
-import { webIDEUrl } from '~/lib/utils/url_utility';
-import icon from '~/vue_shared/components/icon.vue';
+import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility';
+import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default {
@@ -11,7 +11,7 @@ export default {
tooltip,
},
components: {
- icon,
+ Icon,
clipboardButton,
},
props: {
@@ -43,7 +43,10 @@ export default {
return this.isBranchTitleLong(this.mr.targetBranch);
},
webIdePath() {
- return webIDEUrl(this.mr.statusPath.replace('.json', ''));
+ return mergeUrlParams({
+ target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ?
+ this.mr.targetProjectFullPath : '',
+ }, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`));
},
},
methods: {
@@ -54,104 +57,114 @@ export default {
};
</script>
<template>
- <div class="mr-source-target">
- <div class="normal">
- <strong>
- {{ s__("mrWidget|Request to merge") }}
- <span
- :class="{ 'label-truncated': isSourceBranchLong }"
- :title="isSourceBranchLong ? mr.sourceBranch : ''"
- :v-tooltip="isSourceBranchLong"
- class="label-branch js-source-branch"
- data-placement="bottom"
- v-html="mr.sourceBranchLink"
- >
- </span>
+ <div class="mr-source-target append-bottom-default">
+ <div class="git-merge-icon-container append-right-default">
+ <icon name="git-merge" />
+ </div>
+ <div class="git-merge-container d-flex">
+ <div class="normal">
+ <strong>
+ {{ s__("mrWidget|Request to merge") }}
+ <span
+ :class="{ 'label-truncated': isSourceBranchLong }"
+ :title="isSourceBranchLong ? mr.sourceBranch : ''"
+ :v-tooltip="isSourceBranchLong"
+ class="label-branch js-source-branch"
+ data-placement="bottom"
+ v-html="mr.sourceBranchLink"
+ >
+ </span>
- <clipboard-button
- :text="branchNameClipboardData"
- :title="__('Copy branch name to clipboard')"
- css-class="btn-default btn-transparent btn-clipboard"
- />
+ <clipboard-button
+ :text="branchNameClipboardData"
+ :title="__('Copy branch name to clipboard')"
+ css-class="btn-default btn-transparent btn-clipboard"
+ />
- {{ s__("mrWidget|into") }}
+ {{ s__("mrWidget|into") }}
- <span
- :v-tooltip="isTargetBranchLong"
- :class="{ 'label-truncatedtooltip': isTargetBranchLong }"
- :title="isTargetBranchLong ? mr.targetBranch : ''"
- class="label-branch"
- data-placement="bottom"
- >
- <a
- :href="mr.targetBranchTreePath"
- class="js-target-branch"
+ <span
+ :v-tooltip="isTargetBranchLong"
+ :class="{ 'label-truncatedtooltip': isTargetBranchLong }"
+ :title="isTargetBranchLong ? mr.targetBranch : ''"
+ class="label-branch"
+ data-placement="bottom"
>
- {{ mr.targetBranch }}
- </a>
- </span>
- </strong>
- <span
- v-if="shouldShowCommitsBehindText"
- class="diverged-commits-count"
- >
- (<a :href="mr.targetBranchPath">{{ commitsText }}</a>)
- </span>
- </div>
+ <a
+ :href="mr.targetBranchTreePath"
+ class="js-target-branch"
+ >
+ {{ mr.targetBranch }}
+ </a>
+ </span>
+ </strong>
+ <div
+ v-if="shouldShowCommitsBehindText"
+ class="diverged-commits-count"
+ >
+ <span class="monospace">{{ mr.sourceBranch }}</span>
+ is {{ commitsText }}
+ <span class="monospace">{{ mr.targetBranch }}</span>
+ </div>
+ </div>
- <div v-if="mr.isOpen">
- <a
- v-if="!mr.sourceBranchRemoved"
- :href="webIdePath"
- class="btn btn-sm btn-default inline js-web-ide"
- >
- {{ s__("mrWidget|Web IDE") }}
- </a>
- <button
- :disabled="mr.sourceBranchRemoved"
- data-target="#modal_merge_info"
- data-toggle="modal"
- class="btn btn-sm btn-default inline js-check-out-branch"
- type="button"
+ <div
+ v-if="mr.isOpen"
+ class="branch-actions"
>
- {{ s__("mrWidget|Check out branch") }}
- </button>
- <span class="dropdown prepend-left-10">
+ <a
+ v-if="!mr.sourceBranchRemoved"
+ :href="webIdePath"
+ class="btn btn-default inline js-web-ide d-none d-md-inline-block"
+ >
+ {{ s__("mrWidget|Open in Web IDE") }}
+ </a>
<button
+ :disabled="mr.sourceBranchRemoved"
+ data-target="#modal_merge_info"
+ data-toggle="modal"
+ class="btn btn-default inline js-check-out-branch"
type="button"
- class="btn btn-sm inline dropdown-toggle"
- data-toggle="dropdown"
- aria-label="Download as"
- aria-haspopup="true"
- aria-expanded="false"
>
- <icon name="download" />
- <i
- class="fa fa-caret-down"
- aria-hidden="true">
- </i>
+ {{ s__("mrWidget|Check out branch") }}
</button>
- <ul class="dropdown-menu dropdown-menu-right">
- <li>
- <a
- :href="mr.emailPatchesPath"
- class="js-download-email-patches"
- download
- >
- {{ s__("mrWidget|Email patches") }}
- </a>
- </li>
- <li>
- <a
- :href="mr.plainDiffPath"
- class="js-download-plain-diff"
- download
- >
- {{ s__("mrWidget|Plain diff") }}
- </a>
- </li>
- </ul>
- </span>
+ <span class="dropdown prepend-left-10">
+ <button
+ type="button"
+ class="btn inline dropdown-toggle"
+ data-toggle="dropdown"
+ aria-label="Download as"
+ aria-haspopup="true"
+ aria-expanded="false"
+ >
+ <icon name="download" />
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true">
+ </i>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right">
+ <li>
+ <a
+ :href="mr.emailPatchesPath"
+ class="js-download-email-patches"
+ download
+ >
+ {{ s__("mrWidget|Email patches") }}
+ </a>
+ </li>
+ <li>
+ <a
+ :href="mr.plainDiffPath"
+ class="js-download-plain-diff"
+ download
+ >
+ {{ s__("mrWidget|Plain diff") }}
+ </a>
+ </li>
+ </ul>
+ </span>
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index 2f0b5e12c12..4a3fd01fa39 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -26,6 +26,10 @@ export default {
type: String,
required: false,
},
+ sourceBranchLink: {
+ type: String,
+ required: false,
+ },
},
computed: {
hasPipeline() {
@@ -54,12 +58,18 @@ export default {
<template>
<div
v-if="hasPipeline || hasCIError"
- class="mr-widget-heading"
+ class="mr-widget-heading append-bottom-default"
>
<div class="ci-widget media">
<template v-if="hasCIError">
- <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
- <icon name="status_failed" />
+ <div
+ class="add-border ci-status-icon ci-status-icon-failed ci-error
+ js-ci-error append-right-default"
+ >
+ <icon
+ :size="32"
+ name="status_failed_borderless"
+ />
</div>
<div class="media-body">
Could not connect to the CI server. Please check your settings and try again
@@ -68,50 +78,66 @@ export default {
<template v-else-if="hasPipeline">
<a
:href="status.details_path"
- class="append-right-10"
+ class="align-self-start append-right-default"
>
- <ci-icon :status="status" />
+ <ci-icon
+ :status="status"
+ :size="32"
+ :borderless="true"
+ class="add-border"
+ />
</a>
+ <div class="ci-widget-container d-flex">
+ <div class="ci-widget-content">
+ <div class="media-body">
+ <div class="font-weight-bold">
+ Pipeline
+ <a
+ :href="pipeline.path"
+ class="pipeline-id font-weight-normal pipeline-number"
+ >#{{ pipeline.id }}</a>
- <div class="media-body">
- Pipeline
- <a
- :href="pipeline.path"
- class="pipeline-id"
- >
- #{{ pipeline.id }}
- </a>
-
- {{ pipeline.details.status.label }}
+ {{ pipeline.details.status.label }}
- <template v-if="hasCommitInfo">
- for
-
- <a
- :href="pipeline.commit.commit_path"
- class="commit-sha js-commit-link"
- >
- {{ pipeline.commit.short_id }}</a>.
- </template>
-
- <span class="mr-widget-pipeline-graph">
- <span
- v-if="hasStages"
- class="stage-cell"
- >
+ <template v-if="hasCommitInfo">
+ for
+ <a
+ :href="pipeline.commit.commit_path"
+ class="commit-sha js-commit-link font-weight-normal"
+ >
+ {{ pipeline.commit.short_id }}</a>
+ on
+ <span
+ class="label-branch"
+ v-html="sourceBranchLink"
+ >
+ </span>
+ </template>
+ </div>
<div
- v-for="(stage, i) in pipeline.details.stages"
- :key="i"
- class="stage-container dropdown js-mini-pipeline-graph"
+ v-if="pipeline.coverage"
+ class="coverage"
>
- <pipeline-stage :stage="stage" />
+ Coverage {{ pipeline.coverage }}%
</div>
+ </div>
+ </div>
+ <div>
+ <span class="mr-widget-pipeline-graph">
+ <span
+ v-if="hasStages"
+ class="stage-cell"
+ >
+ <div
+ v-for="(stage, i) in pipeline.details.stages"
+ :key="i"
+ class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages"
+ >
+ <pipeline-stage :stage="stage" />
+ </div>
+ </span>
</span>
- </span>
-
- <template v-if="pipeline.coverage">
- Coverage {{ pipeline.coverage }}%
- </template>
+ </div>
</div>
</template>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
index 53c4dc8c8f4..9aff95dcfec 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -32,7 +32,7 @@
};
</script>
<template>
- <div class="space-children d-flex append-right-10">
+ <div class="space-children d-flex append-right-10 widget-status-icon">
<div
v-if="isLoading"
class="mr-widget-icon"
@@ -43,6 +43,7 @@
<ci-icon
v-else
:status="statusObj"
+ :size="24"
/>
<button
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index fe777a07189..a5ca7b719a1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -233,7 +233,7 @@ export default {
<status-icon :status="iconClass" />
<div class="media-body">
<div class="mr-widget-body-controls media space-children">
- <span class="btn-group append-bottom-5">
+ <span class="btn-group">
<button
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index 09477da40b5..b5de3dd6d73 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -252,41 +252,44 @@ export default {
:pipeline="mr.pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
+ :source-branch-link="mr.sourceBranchLink"
/>
<deployment
v-for="deployment in mr.deployments"
:key="deployment.id"
:deployment="deployment"
/>
- <div class="mr-widget-section">
- <component
- :is="componentName"
- :mr="mr"
- :service="service"
- />
+ <div class="mr-section-container">
+ <div class="mr-widget-section">
+ <component
+ :is="componentName"
+ :mr="mr"
+ :service="service"
+ />
- <section
- v-if="mr.allowCollaboration"
- class="mr-info-list mr-links"
- >
- {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
- </section>
+ <section
+ v-if="mr.allowCollaboration"
+ class="mr-info-list mr-links"
+ >
+ {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
+ </section>
- <mr-widget-related-links
- v-if="shouldRenderRelatedLinks"
- :state="mr.state"
- :related-links="mr.relatedLinks"
- />
+ <mr-widget-related-links
+ v-if="shouldRenderRelatedLinks"
+ :state="mr.state"
+ :related-links="mr.relatedLinks"
+ />
- <source-branch-removal-status
- v-if="shouldRenderSourceBranchRemovalStatus"
- />
- </div>
- <div
- v-if="shouldRenderMergeHelp"
- class="mr-widget-footer"
- >
- <mr-widget-merge-help />
+ <source-branch-removal-status
+ v-if="shouldRenderSourceBranchRemovalStatus"
+ />
+ </div>
+ <div
+ v-if="shouldRenderMergeHelp"
+ class="mr-widget-footer"
+ >
+ <mr-widget-merge-help />
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index c881cd496d1..e84c436905d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -16,10 +16,11 @@ export default class MergeRequestStore {
const pipelineStatus = data.pipeline ? data.pipeline.details.status : null;
this.squash = data.squash;
- this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath ||
- data.squash_before_merge_help_path;
+ this.squashBeforeMergeHelpPath =
+ this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
+ this.iid = data.iid;
this.title = data.title;
this.targetBranch = data.target_branch;
this.sourceBranch = data.source_branch;
@@ -85,6 +86,8 @@ export default class MergeRequestStore {
this.isMergeAllowed = data.mergeable || false;
this.mergeOngoing = data.merge_ongoing;
this.allowCollaboration = data.allow_collaboration;
+ this.targetProjectFullPath = data.target_project_full_path;
+ this.sourceProjectFullPath = data.source_project_full_path;
// Cherry-pick and Revert actions related
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
@@ -97,7 +100,8 @@ export default class MergeRequestStore {
this.hasCI = data.has_ci;
this.ciStatus = data.ci_status;
this.isPipelineFailed = this.ciStatus === 'failed' || this.ciStatus === 'canceled';
- this.isPipelinePassing = this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings';
+ this.isPipelinePassing =
+ this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings';
this.isPipelineSkipped = this.ciStatus === 'skipped';
this.pipelineDetailedStatus = pipelineStatus;
this.isPipelineActive = data.pipeline ? data.pipeline.active : false;
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index b298b989203..416eda796a7 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -45,6 +45,11 @@ export default {
emitSubmit(event) {
this.$emit('submit', event);
},
+ opened({ propertyName }) {
+ if (propertyName === 'opacity') {
+ this.$emit('open');
+ }
+ },
},
};
</script>
@@ -55,6 +60,7 @@ export default {
class="modal fade"
tabindex="-1"
role="dialog"
+ @transitionend="opened"
>
<div
:class="modalSizeClass"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 298971a36b2..d62537021ca 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+ import { s__ } from '~/locale';
import Flash from '../../../flash';
import GLForm from '../../../gl_form';
import markdownHeader from './header.vue';
@@ -22,6 +23,11 @@
type: String,
required: true,
},
+ markdownVersion: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
addSpacingClasses: {
type: Boolean,
required: false,
@@ -92,10 +98,11 @@
if (text) {
this.markdownPreviewLoading = true;
- this.$http.post(this.markdownPreviewPath, { text })
- .then(resp => resp.json())
- .then(data => this.renderMarkdown(data))
- .catch(() => new Flash('Error loading markdown preview'));
+ this.$http
+ .post(this.versionedPreviewPath(), { text })
+ .then(resp => resp.json())
+ .then(data => this.renderMarkdown(data))
+ .catch(() => new Flash(s__('Error loading markdown preview')));
} else {
this.renderMarkdown();
}
@@ -119,6 +126,13 @@
$(this.$refs['markdown-preview']).renderGFM();
});
},
+
+ versionedPreviewPath() {
+ const { markdownPreviewPath, markdownVersion } = this;
+ return `${markdownPreviewPath}${
+ markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
+ }markdown_version=${markdownVersion}`;
+ },
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index 522091ea889..552a92541be 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -126,7 +126,7 @@ export default {
:cx="dotX"
:cy="dotY"
r="1.5"
- tranform="translate(0 -1)"
+ transform="translate(0 -1)"
/>
</svg>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
index 80dc7d3557c..ac2e99abe77 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
@@ -12,11 +12,6 @@ export default {
type: Boolean,
required: true,
},
- cssClasses: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
tooltipLabel() {
@@ -35,12 +30,10 @@ export default {
<button
v-tooltip
:title="tooltipLabel"
- :class="cssClasses"
type="button"
class="btn btn-blank gutter-toggle btn-sidebar-action"
data-container="body"
data-placement="left"
- data-boundary="viewport"
@click="toggle"
>
<i
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index ded33e8b151..d28ad407734 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -110,7 +110,7 @@ code {
padding: 2px 4px;
color: $red-600;
background-color: $red-100;
- border-radius: 3px;
+ border-radius: $border-radius-default;
.code > & {
background-color: inherit;
@@ -128,7 +128,8 @@ table {
border-spacing: 0;
}
-.tooltip {
+.tooltip,
+.no-pointer-events {
// Fix bootstrap4 bug whereby tooltips flicker when they are hovered over their borders
pointer-events: none;
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 74475daae14..c7b5e22c33d 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -36,7 +36,7 @@
width: 100%;
}
- &.projects-dropdown-menu {
+ &.frequent-items-dropdown-menu {
padding: 0;
overflow-y: initial;
max-height: initial;
@@ -790,6 +790,7 @@
@include media-breakpoint-down(xs) {
.navbar-gitlab {
li.header-projects,
+ li.header-groups,
li.header-more,
li.header-new,
li.header-user {
@@ -813,18 +814,18 @@
}
}
-header.header-content .dropdown-menu.projects-dropdown-menu {
+header.header-content .dropdown-menu.frequent-items-dropdown-menu {
padding: 0;
}
-.projects-dropdown-container {
+.frequent-items-dropdown-container {
display: flex;
flex-direction: row;
width: 500px;
height: 334px;
- .project-dropdown-sidebar,
- .project-dropdown-content {
+ .frequent-items-dropdown-sidebar,
+ .frequent-items-dropdown-content {
padding: 8px 0;
}
@@ -832,12 +833,12 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
color: $almost-black;
}
- .project-dropdown-sidebar {
+ .frequent-items-dropdown-sidebar {
width: 30%;
border-right: 1px solid $border-color;
}
- .project-dropdown-content {
+ .frequent-items-dropdown-content {
position: relative;
width: 70%;
}
@@ -848,33 +849,35 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
height: auto;
flex: 1;
- .project-dropdown-sidebar,
- .project-dropdown-content {
+ .frequent-items-dropdown-sidebar,
+ .frequent-items-dropdown-content {
width: 100%;
}
- .project-dropdown-sidebar {
+ .frequent-items-dropdown-sidebar {
border-bottom: 1px solid $border-color;
border-right: 0;
}
}
- .projects-list-frequent-container,
- .projects-list-search-container {
+ .section-header,
+ .frequent-items-list-container li.section-empty {
+ padding: 0 $gl-padding;
+ color: $gl-text-color-secondary;
+ font-size: $gl-font-size;
+ }
+
+ .frequent-items-list-container {
padding: 8px 0;
overflow-y: auto;
li.section-empty.section-failure {
color: $callout-danger-color;
}
- }
- .section-header,
- .projects-list-frequent-container li.section-empty,
- .projects-list-search-container li.section-empty {
- padding: 0 15px;
- color: $gl-text-color-secondary;
- font-size: $gl-font-size;
+ .frequent-items-list-item-container a {
+ display: flex;
+ }
}
.search-input-container {
@@ -894,12 +897,12 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
margin-top: 8px;
}
- .projects-list-search-container {
+ .frequent-items-search-container {
height: 284px;
}
@include media-breakpoint-down(xs) {
- .projects-list-frequent-container {
+ .frequent-items-list-container {
width: auto;
height: auto;
padding-bottom: 0;
@@ -907,32 +910,38 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
}
}
-.projects-list-item-container {
- .project-item-avatar-container .project-item-metadata-container {
+.frequent-items-list-item-container {
+ .frequent-items-item-avatar-container,
+ .frequent-items-item-metadata-container {
float: left;
}
- .project-title,
- .project-namespace {
+ .frequent-items-item-metadata-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ .frequent-items-item-title,
+ .frequent-items-item-namespace {
max-width: 250px;
- overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&:hover {
- .project-item-avatar-container .avatar {
+ .frequent-items-item-avatar-container .avatar {
border-color: $md-area-border;
}
}
- .project-title {
+ .frequent-items-item-title {
font-size: $gl-font-size;
font-weight: 400;
line-height: 16px;
}
- .project-namespace {
+ .frequent-items-item-namespace {
margin-top: 4px;
font-size: 12px;
line-height: 12px;
@@ -940,7 +949,7 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
}
@include media-breakpoint-down(xs) {
- .project-item-metadata-container {
+ .frequent-items-item-metadata-container {
float: none;
}
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 551a7e852ae..5d79610b21e 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -224,7 +224,10 @@
.form-control {
position: relative;
min-width: 200px;
- padding: 5px 25px 6px 0;
+ padding-right: 25px;
+ padding-left: 0;
+ height: $input-height;
+ line-height: inherit;
border-color: transparent;
&:focus,
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 282e424fc38..a22454c24e2 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -255,3 +255,8 @@ label {
color: $theme-gray-600;
}
}
+
+.input-lg {
+ max-width: 320px;
+ width: 100%;
+}
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index 1cf12b1a015..d2ba76f5160 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -9,12 +9,8 @@
.gfm-project_member {
padding: 0 2px;
- border-radius: #{$border-radius-default / 2};
- background-color: $user-mention-bg;
-
- &:hover {
- background-color: $user-mention-bg-hover;
- }
+ background-color: $blue-100;
+ border-radius: $border-radius-default;
}
.gfm-color_chip {
diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss
index aaa8bed3df0..dff6bce370f 100644
--- a/app/assets/stylesheets/framework/gitlab_theme.scss
+++ b/app/assets/stylesheets/framework/gitlab_theme.scss
@@ -29,15 +29,21 @@
.navbar-sub-nav,
.navbar-nav {
> li {
- > a:hover,
- > a:focus {
- background-color: rgba($search-and-nav-links, 0.2);
+ > a,
+ > button {
+ &:hover,
+ &:focus {
+ background-color: rgba($search-and-nav-links, 0.2);
+ }
}
- &.active > a,
- &.dropdown.show > a {
- color: $nav-svg-color;
- background-color: $color-alternate;
+ &.active,
+ &.dropdown.show {
+ > a,
+ > button {
+ color: $nav-svg-color;
+ background-color: $color-alternate;
+ }
}
&.line-separator {
@@ -147,7 +153,6 @@
}
}
-
// Sidebar
.nav-sidebar li.active {
box-shadow: inset 4px 0 0 $border-and-box-shadow;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 8bcaf5eb6ac..2097bcebf69 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -269,14 +269,8 @@
.navbar-sub-nav,
.navbar-nav {
> li {
- > a:hover,
- > a:focus {
- text-decoration: none;
- outline: 0;
- color: $white-light;
- }
-
- > a {
+ > a,
+ > button {
display: -webkit-flex;
display: flex;
align-items: center;
@@ -288,6 +282,18 @@
border-radius: $border-radius-default;
height: 32px;
font-weight: $gl-font-weight-bold;
+
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ outline: 0;
+ color: $white-light;
+ }
+ }
+
+ > button {
+ background: transparent;
+ border: 0;
}
&.line-separator {
@@ -311,7 +317,7 @@
font-size: 10px;
}
- .project-item-select-holder {
+ .frequent-items-item-select-holder {
display: inline;
}
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 30314f3d6cb..d1f7ff4438b 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -3,12 +3,20 @@
svg {
fill: $green-500;
}
+
+ &.add-border {
+ @include borderless-status-icon($green-500);
+ }
}
.ci-status-icon-failed {
svg {
fill: $gl-danger;
}
+
+ &.add-border {
+ @include borderless-status-icon($red-500);
+ }
}
.ci-status-icon-pending,
@@ -17,12 +25,20 @@
svg {
fill: $orange-500;
}
+
+ &.add-border {
+ @include borderless-status-icon($orange-500);
+ }
}
.ci-status-icon-running {
svg {
fill: $blue-400;
}
+
+ &.add-border {
+ @include borderless-status-icon($blue-400);
+ }
}
.ci-status-icon-canceled,
@@ -30,6 +46,10 @@
svg {
fill: $gl-text-color;
}
+
+ &.add-border {
+ @include borderless-status-icon($gl-text-color);
+ }
}
.ci-status-icon-created,
@@ -38,6 +58,10 @@
svg {
fill: $gray-darkest;
}
+
+ &.add-border {
+ @include borderless-status-icon($gray-darkest);
+ }
}
.ci-status-icon-manual {
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 0b645eb811b..76ebfc22ef7 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -232,3 +232,10 @@
word-break: break-word;
max-width: 100%;
}
+
+@mixin borderless-status-icon($color) {
+ svg {
+ border: 1px solid $color;
+ border-radius: 50%;
+ }
+}
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 9dbb04e5443..8bab8cf36b1 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -321,11 +321,18 @@
}
&.activities {
+ display: flex;
border-bottom: 1px solid $border-color;
+ overflow: hidden;
.nav-links {
border-bottom: 0;
}
+
+ @include media-breakpoint-down(xs) {
+ display: block;
+ overflow: visible;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 9e77ea03a24..9874c928604 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -350,7 +350,8 @@ code {
}
.commit-sha,
-.ref-name {
+.ref-name,
+.pipeline-number {
@extend .monospace;
font-size: 95%;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 7808f6d3a25..6cfa09b56a7 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -743,6 +743,7 @@ Pipeline Graph
*/
$stage-hover-bg: $gray-darker;
$ci-action-icon-size: 22px;
+$ci-action-icon-size-lg: 24px;
$pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 750d2c8b990..5de53892fac 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -80,7 +80,6 @@
overflow-x: scroll;
white-space: nowrap;
min-height: 200px;
- display: flex;
@include media-breakpoint-only(sm) {
height: calc(100vh - #{$issue-board-list-difference-sm});
@@ -111,15 +110,17 @@
.board {
display: inline-block;
- flex: 1;
- min-width: 300px;
- max-width: 400px;
+ width: calc(85vw - 15px);
height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
white-space: normal;
vertical-align: top;
+ @include media-breakpoint-up(sm) {
+ width: 400px;
+ }
+
&.is-expandable {
.board-header {
cursor: pointer;
@@ -127,8 +128,6 @@
}
&.is-collapsed {
- flex: none;
- min-width: 0;
width: 50px;
.board-header {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 7e89f8998fb..5e39bbb9890 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -518,6 +518,12 @@
outline: none;
color: $gl-link-hover-color;
}
+
+ .caret-icon {
+ position: relative;
+ top: 2px;
+ left: -1px;
+ }
}
// Mobile
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 199039f38f7..3144dcc4dc0 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -23,7 +23,7 @@
}
.btn-group {
- > a {
+ > .btn:not(.btn-danger) {
color: $gl-text-color-secondary;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index f6617380cc0..f9fd9f1ab8b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -449,7 +449,6 @@
.todo-undone {
color: $gl-link-color;
- fill: $gl-link-color;
}
.author {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index efd730af558..c8349a4ef79 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -15,16 +15,38 @@
}
}
+.mr-widget-heading {
+ position: relative;
+ border: 1px solid $border-color;
+ border-radius: 4px;
+
+ &:not(.deploy-heading)::before {
+ content: '';
+ border-left: 1px solid $theme-gray-200;
+ position: absolute;
+ left: 32px;
+ top: -17px;
+ height: 16px;
+ }
+}
+
+.mr-section-container {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ border-top: 0;
+}
+
+.mr-widget-heading,
+.mr-widget-section,
+.mr-widget-footer {
+ padding: $gl-padding;
+}
+
.mr-state-widget {
color: $gl-text-color;
- border: 1px solid $border-color;
- border-radius: 2px;
- line-height: 28px;
- .mr-widget-heading,
.mr-widget-section,
.mr-widget-footer {
- padding: $gl-padding;
border-top: solid 1px $border-color;
}
@@ -94,10 +116,8 @@
.modify-merge-commit-link {
padding: 0;
-
background-color: transparent;
border: 0;
-
color: $gl-text-color;
&:hover,
@@ -124,10 +144,17 @@
.ci-widget {
color: $gl-text-color;
display: flex;
+ align-items: center;
+ justify-content: space-between;
@include media-breakpoint-down(xs) {
flex-wrap: wrap;
}
+
+ .ci-widget-content {
+ display: flex;
+ align-items: center;
+ }
}
.mr-widget-icon {
@@ -136,8 +163,6 @@
}
.ci-status-icon svg {
- width: $status-icon-size;
- height: $status-icon-size;
margin: 3px 0;
position: relative;
overflow: visible;
@@ -145,8 +170,6 @@
}
.mr-widget-pipeline-graph {
- padding: 0 4px;
-
.dropdown-menu {
z-index: 300;
}
@@ -157,7 +180,7 @@
}
.normal {
- line-height: 28px;
+ flex: 1;
}
.capitalize {
@@ -168,7 +191,7 @@
@extend .ref-name;
color: $gl-text-color;
- font-weight: $gl-font-weight-bold;
+ font-weight: normal;
overflow: hidden;
word-break: break-all;
@@ -191,7 +214,13 @@
}
}
+ .widget-status-icon {
+ align-self: flex-start;
+ }
+
.mr-widget-body {
+ line-height: 28px;
+
@include clearfix;
&.media > *:first-child {
@@ -477,15 +506,60 @@
.mr-source-target {
display: flex;
flex-wrap: wrap;
- justify-content: space-between;
- align-items: center;
- background-color: $gray-light;
- border-radius: $border-radius-default $border-radius-default 0 0;
- padding: $gl-padding / 2 $gl-padding;
+ border-radius: $border-radius-default;
+ padding: $gl-padding;
+ border: 1px solid $border-color;
+ min-height: 69px;
+
+ @include media-breakpoint-up(md) {
+ align-items: center;
+ }
.dropdown-toggle .fa {
color: $gl-text-color;
}
+
+ .git-merge-icon-container {
+ border: 1px solid $theme-gray-400;
+ border-radius: 50%;
+ height: 32px;
+ width: 32px;
+ color: $theme-gray-700;
+ line-height: 28px;
+
+ .ic-git-merge {
+ vertical-align: middle;
+ width: 31px;
+ }
+ }
+
+ .git-merge-container {
+ justify-content: space-between;
+ flex: 1;
+ flex-direction: row;
+ align-items: center;
+
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .branch-actions {
+ margin-top: 16px;
+ }
+ }
+
+ @include media-breakpoint-up(lg) {
+ .branch-actions {
+ align-self: center;
+ margin-left: $gl-padding;
+ }
+ }
+ }
+
+ .diverged-commits-count {
+ color: $gl-text-color-secondary;
+ font-size: 12px;
+ }
}
.card-new-merge-request {
@@ -720,13 +794,25 @@
}
.deploy-heading {
+ margin-top: -19px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ background-color: $gray-light;
+
+ @include media-breakpoint-up(md) {
+ padding: $gl-padding-8 $gl-padding;
+ }
+
.media-body {
min-width: 0;
+ font-size: 12px;
+ margin-left: 48px;
}
}
.deploy-body {
display: flex;
+ align-items: center;
flex-wrap: wrap;
@include media-breakpoint-up(xs) {
@@ -734,6 +820,15 @@
white-space: nowrap;
}
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .deployment-info {
+ margin-bottom: $gl-padding;
+ }
+ }
+
> *:not(:last-child) {
margin-right: .3em;
}
@@ -741,18 +836,22 @@
svg {
vertical-align: text-top;
}
-}
-.deploy-link {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- min-width: 100px;
- max-width: 150px;
+ .deployment-info {
+ flex: 1;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ min-width: 100px;
- @include media-breakpoint-up(xs) {
- min-width: 0;
- max-width: 100%;
+ @include media-breakpoint-up(xs) {
+ min-width: 0;
+ max-width: 100%;
+ }
+ }
+
+ .btn svg {
+ fill: $theme-gray-700;
}
}
@@ -772,3 +871,33 @@
}
}
}
+
+.ci-widget-container {
+ justify-content: space-between;
+ flex: 1;
+ flex-direction: row;
+
+ @include media-breakpoint-down(md) {
+ flex-direction: column;
+
+ .stage-cell .stage-container {
+ margin-top: 16px;
+ }
+
+ .dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu {
+ transform: initial;
+ }
+ }
+
+ .coverage {
+ font-size: 12px;
+ color: $theme-gray-700;
+ line-height: initial;
+ }
+
+ .mini-pipeline-graph-dropdown-toggle,
+ .stage-cell .mini-pipeline-graph-dropdown-toggle svg {
+ height: $ci-action-icon-size-lg;
+ width: $ci-action-icon-size-lg;
+ }
+}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 52332ac97dd..b68c89c25d8 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -301,6 +301,21 @@
border-bottom: 2px solid $border-color;
}
}
+
+ //delete when all pipelines are updated to new size
+ &.mr-widget-pipeline-stages {
+ + .stage-container {
+ margin-left: 4px;
+ }
+
+ &:not(:last-child) {
+ &::after {
+ width: 4px;
+ right: -4px;
+ top: 11px;
+ }
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 3c24aaa65e8..8b1227b9131 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -44,6 +44,7 @@
padding-bottom: $grid-size;
.file {
+ height: 32px;
cursor: pointer;
&.file-active {
@@ -716,32 +717,6 @@
justify-content: center;
}
-.ide-new-btn {
- .btn {
- padding-top: 3px;
- padding-bottom: 3px;
- }
-
- .dropdown {
- display: flex;
- }
-
- .dropdown-toggle svg {
- top: 0;
- }
-
- .dropdown-menu {
- left: auto;
- right: 0;
-
- label {
- font-weight: $gl-font-weight-normal;
- padding: 5px 8px;
- margin-bottom: 0;
- }
- }
-}
-
.ide {
overflow: hidden;
@@ -1329,3 +1304,35 @@
line-height: 16px;
color: $gl-text-color-secondary;
}
+
+.ide-merge-request-info {
+ .detail-page-header {
+ line-height: initial;
+ min-height: 38px;
+ }
+
+ .issuable-details {
+ overflow: auto;
+ }
+}
+
+.ide-entry-dropdown-toggle {
+ padding: $gl-padding-4;
+ background-color: $theme-gray-100;
+
+ &:hover {
+ background-color: $theme-gray-200;
+ }
+
+ &:active,
+ &:focus {
+ color: $white-normal;
+ background-color: $blue-500;
+ outline: 0;
+ }
+}
+
+.ide-new-btn .dropdown.show .ide-entry-dropdown-toggle {
+ color: $white-normal;
+ background-color: $blue-500;
+}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 010a2c05a1c..e5d7dd13915 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -174,18 +174,6 @@
}
}
-@include media-breakpoint-down(lg) {
- .todos-filters {
- .filter-categories {
- width: 75%;
-
- .filter-item {
- margin-bottom: 10px;
- }
- }
- }
-}
-
@include media-breakpoint-down(xs) {
.todo {
.avatar {
@@ -211,10 +199,6 @@
}
.todos-filters {
- .filter-categories {
- width: auto;
- }
-
.dropdown-menu-toggle {
width: 100%;
}
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index b0c4c31cffc..5c2025c1988 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -22,7 +22,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def update
- if deploy_key.update_attributes(update_params)
+ if deploy_key.update(update_params)
flash[:notice] = 'Deploy key was successfully updated.'
redirect_to admin_deploy_keys_path
else
@@ -34,7 +34,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
deploy_key.destroy
respond_to do |format|
- format.html { redirect_to admin_deploy_keys_path, status: 302 }
+ format.html { redirect_to admin_deploy_keys_path, status: :found }
format.json { head :ok }
end
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 96b7bc65ac9..d7a5b745d3f 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -39,7 +39,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def update
- if @group.update_attributes(group_params)
+ if @group.update(group_params)
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render "edit"
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index 6944857bd33..a98c355c7ba 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -23,7 +23,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def update
- if hook.update_attributes(hook_params)
+ if hook.update(hook_params)
flash[:notice] = 'System hook was successfully updated.'
redirect_to admin_hooks_path
else
@@ -34,7 +34,7 @@ class Admin::HooksController < Admin::ApplicationController
def destroy
hook.destroy
- redirect_to admin_hooks_path, status: 302
+ redirect_to admin_hooks_path, status: :found
end
def test
diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb
index 43b4e3a2cc3..ceb45865804 100644
--- a/app/controllers/admin/identities_controller.rb
+++ b/app/controllers/admin/identities_controller.rb
@@ -25,7 +25,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
end
def update
- if @identity.update_attributes(identity_params)
+ if @identity.update(identity_params)
RepairLdapBlockedUserService.new(@user).execute
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.'
else
diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb
index 39dbf85f6c0..d2f947d2c66 100644
--- a/app/controllers/admin/impersonations_controller.rb
+++ b/app/controllers/admin/impersonations_controller.rb
@@ -11,7 +11,7 @@ class Admin::ImpersonationsController < Admin::ApplicationController
session[:impersonator_id] = nil
- redirect_to admin_user_path(original_user), status: 302
+ redirect_to admin_user_path(original_user), status: :found
end
private
diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
index ae7a7f6279c..ac1ae0f16b3 100644
--- a/app/controllers/admin/jobs_controller.rb
+++ b/app/controllers/admin/jobs_controller.rb
@@ -20,6 +20,6 @@ class Admin::JobsController < Admin::ApplicationController
def cancel_all
Ci::Build.running_or_pending.each(&:cancel)
- redirect_to admin_jobs_path, status: 303
+ redirect_to admin_jobs_path, status: :see_other
end
end
diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb
index 7aba77d8129..51d5799cd89 100644
--- a/app/controllers/admin/runner_projects_controller.rb
+++ b/app/controllers/admin/runner_projects_controller.rb
@@ -16,7 +16,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
runner = rp.runner
rp.destroy
- redirect_to admin_runner_path(runner), status: 302
+ redirect_to admin_runner_path(runner), status: :found
end
private
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 4b01904f2a1..6c76c55a9d4 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -28,7 +28,7 @@ class Admin::RunnersController < Admin::ApplicationController
def destroy
@runner.destroy
- redirect_to admin_runners_path, status: 302
+ redirect_to admin_runners_path, status: :found
end
def resume
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index a7025b62ad7..e70aa549140 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -16,7 +16,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
def update
- if service.update_attributes(service_params[:service])
+ if service.update(service_params[:service])
PropagateServiceTemplateWorker.perform_async(service.id) if service.active?
redirect_to admin_application_settings_services_path,
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 653f3dfffc4..a51a8c3ed4a 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -163,7 +163,7 @@ class Admin::UsersController < Admin::ApplicationController
format.json { head :ok }
else
format.html { redirect_back_or_admin_user(alert: 'There was an error removing the e-mail.') }
- format.json { render json: 'There was an error removing the e-mail.', status: 400 }
+ format.json { render json: 'There was an error removing the e-mail.', status: :bad_request }
end
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 21cc6dfdd16..f45fcd4d900 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -30,7 +30,13 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
helper_method :can?
- helper_method :import_sources_enabled?, :github_import_enabled?, :gitea_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
+ helper_method :import_sources_enabled?, :github_import_enabled?,
+ :gitea_import_enabled?, :github_import_configured?,
+ :gitlab_import_enabled?, :gitlab_import_configured?,
+ :bitbucket_import_enabled?, :bitbucket_import_configured?,
+ :google_code_import_enabled?, :fogbugz_import_enabled?,
+ :git_import_enabled?, :gitlab_project_import_enabled?,
+ :manifest_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -351,6 +357,10 @@ class ApplicationController < ActionController::Base
Gitlab::CurrentSettings.import_sources.include?('gitlab_project')
end
+ def manifest_import_enabled?
+ Group.supports_nested_groups? && Gitlab::CurrentSettings.import_sources.include?('manifest')
+ end
+
# U2F (universal 2nd factor) devices need a unique identifier for the application
# to perform authentication.
# https://developers.yubico.com/U2F/App_ID.html
diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb
index 56770a17406..6ec6897e707 100644
--- a/app/controllers/concerns/group_tree.rb
+++ b/app/controllers/concerns/group_tree.rb
@@ -1,21 +1,16 @@
module GroupTree
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_group_tree(groups)
- @groups = if params[:filter].present?
- # We find the ancestors by ID of the search results here.
- # Otherwise the ancestors would also have filters applied,
- # which would cause them not to be preloaded.
- group_ids = groups.search(params[:filter]).select(:id)
- Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
- .base_and_ancestors
- else
- # Only show root groups if no parent-id is given
- groups.where(parent_id: params[:parent_id])
- end
+ groups = groups.sort_by_attribute(@sort = params[:sort])
- @groups = @groups.with_selects_for_list(archived: params[:archived])
- .sort_by_attribute(@sort = params[:sort])
- .page(params[:page])
+ groups = if params[:filter].present?
+ filtered_groups_with_ancestors(groups)
+ else
+ # If `params[:parent_id]` is `nil`, we will only show root-groups
+ groups.where(parent_id: params[:parent_id]).page(params[:page])
+ end
+
+ @groups = groups.with_selects_for_list(archived: params[:archived])
respond_to do |format|
format.html
@@ -28,4 +23,21 @@ module GroupTree
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
+
+ def filtered_groups_with_ancestors(groups)
+ filtered_groups = groups.search(params[:filter]).page(params[:page])
+
+ if Group.supports_nested_groups?
+ # We find the ancestors by ID of the search results here.
+ # Otherwise the ancestors would also have filters applied,
+ # which would cause them not to be preloaded.
+ #
+ # Pagination needs to be applied before loading the ancestors to
+ # make sure ancestors are not cut off by pagination.
+ Gitlab::GroupHierarchy.new(Group.where(id: filtered_groups.select(:id)))
+ .base_and_ancestors
+ else
+ filtered_groups
+ end
+ end
end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index ba510968684..37e03d70b6f 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -127,7 +127,7 @@ module IssuableActions
errors: [
"Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
]
- }, status: 409
+ }, status: :conflict
end
end
end
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 5e4e8a87153..79ee5b2f91e 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -27,7 +27,7 @@ module LfsRequest
message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
documentation_url: help_url
},
- status: 501
+ status: :not_implemented
)
end
diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb
index 7ac63c914fa..99123fcb3b0 100644
--- a/app/controllers/concerns/preview_markdown.rb
+++ b/app/controllers/concerns/preview_markdown.rb
@@ -14,6 +14,8 @@ module PreviewMarkdown
else {}
end
+ markdown_params[:markdown_engine] = result[:markdown_engine]
+
render json: {
body: view_context.markdown(result[:text], markdown_params),
references: {
diff --git a/app/controllers/concerns/todos_actions.rb b/app/controllers/concerns/todos_actions.rb
deleted file mode 100644
index c0acdb3498d..00000000000
--- a/app/controllers/concerns/todos_actions.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module TodosActions
- extend ActiveSupport::Concern
-
- def create
- todo = TodoService.new.mark_todo(issuable, current_user)
-
- render json: {
- count: TodosFinder.new(current_user, state: :pending).execute.count,
- delete_path: dashboard_todo_path(todo)
- }
- end
-end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index bd7111e28bc..f9e8fe624e8 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -70,7 +70,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def todo_params
- params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id)
+ params.permit(:action_id, :author_id, :project_id, :type, :sort, :state)
end
def redirect_out_of_range(todos)
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index cc5ba5878f8..35a61b359c8 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -7,6 +7,6 @@ class Groups::AvatarsController < Groups::ApplicationController
@group.remove_avatar!
@group.save
- redirect_to edit_group_path(@group), status: 302
+ redirect_to edit_group_path(@group), status: :found
end
end
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index 78992ec7f46..1036b4e6ed3 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -23,7 +23,7 @@ class Groups::RunnersController < Groups::ApplicationController
def destroy
@runner.destroy
- redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: 302
+ redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found
end
def resume
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
new file mode 100644
index 00000000000..e5a719fa0df
--- /dev/null
+++ b/app/controllers/import/manifest_controller.rb
@@ -0,0 +1,93 @@
+class Import::ManifestController < Import::BaseController
+ before_action :whitelist_query_limiting, only: [:create]
+ before_action :verify_import_enabled
+ before_action :ensure_import_vars, only: [:create, :status]
+
+ def new
+ end
+
+ def status
+ @already_added_projects = find_already_added_projects
+ already_added_import_urls = @already_added_projects.pluck(:import_url)
+
+ @pending_repositories = repositories.to_a.reject do |repository|
+ already_added_import_urls.include?(repository[:url])
+ end
+ end
+
+ def upload
+ group = Group.find(params[:group_id])
+
+ unless can?(current_user, :create_projects, group)
+ @errors = ["You don't have enough permissions to create projects in the selected group"]
+
+ render :new && return
+ end
+
+ manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile)
+
+ if manifest.valid?
+ session[:manifest_import_repositories] = manifest.projects
+ session[:manifest_import_group_id] = group.id
+
+ redirect_to status_import_manifest_path
+ else
+ @errors = manifest.errors
+
+ render :new
+ end
+ end
+
+ def jobs
+ render json: find_jobs
+ end
+
+ def create
+ repository = repositories.find do |project|
+ project[:id] == params[:repo_id].to_i
+ end
+
+ project = Gitlab::ManifestImport::ProjectCreator.new(repository, group, current_user).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project_save_error(project) }, status: :unprocessable_entity
+ end
+ end
+
+ private
+
+ def ensure_import_vars
+ unless group && repositories.present?
+ redirect_to(new_import_manifest_path)
+ end
+ end
+
+ def group
+ @group ||= Group.find_by(id: session[:manifest_import_group_id])
+ end
+
+ def repositories
+ @repositories ||= session[:manifest_import_repositories]
+ end
+
+ def find_jobs
+ find_already_added_projects.to_json(only: [:id], methods: [:import_status])
+ end
+
+ def find_already_added_projects
+ group.all_projects
+ .where(import_type: 'manifest')
+ .where(creator_id: current_user)
+ .includes(:import_state)
+ end
+
+ def verify_import_enabled
+ render_404 unless manifest_import_enabled?
+ end
+
+ def whitelist_query_limiting
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/48939')
+ end
+end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 67057b5b126..3cb9e46b548 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -41,7 +41,7 @@ class JwtController < ApplicationController
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}" }
]
- }, status: 401
+ }, status: :unauthorized
end
def render_unauthorized
@@ -50,7 +50,7 @@ class JwtController < ApplicationController
{ code: 'UNAUTHORIZED',
message: 'HTTP Basic: Access denied' }
]
- }, status: 401
+ }, status: :unauthorized
end
def auth_params
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index 8ec4bb1233f..ed20302487c 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -5,14 +5,14 @@ class NotificationSettingsController < ApplicationController
return render_404 unless can_read?(resource)
@notification_setting = current_user.notification_settings_for(resource)
- @saved = @notification_setting.update_attributes(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params)
render_response
end
def update
@notification_setting = current_user.notification_settings.find(params[:id])
- @saved = @notification_setting.update_attributes(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params)
render_response
end
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index f0cdc228366..f1e77d68acd 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -7,7 +7,7 @@ class Profiles::ActiveSessionsController < Profiles::ApplicationController
ActiveSession.destroy(current_user, params[:id])
respond_to do |format|
- format.html { redirect_to profile_active_sessions_url, status: 302 }
+ format.html { redirect_to profile_active_sessions_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 39b9f8a84d1..4f030ded80f 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -4,6 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
Users::UpdateService.new(current_user, user: @user).execute { |user| user.remove_avatar! }
- redirect_to profile_path, status: 302
+ redirect_to profile_path, status: :found
end
end
diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb
index 2353f0840d6..a186c5f36a8 100644
--- a/app/controllers/profiles/chat_names_controller.rb
+++ b/app/controllers/profiles/chat_names_controller.rb
@@ -39,7 +39,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
flash[:alert] = "Could not delete chat nickname #{@chat_name.chat_name}."
end
- redirect_to profile_chat_names_path, status: 302
+ redirect_to profile_chat_names_path, status: :found
end
private
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index bbd7ba49d77..a39824ec9c8 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -19,7 +19,7 @@ class Profiles::EmailsController < Profiles::ApplicationController
Emails::DestroyService.new(current_user, user: current_user).execute(@email)
respond_to do |format|
- format.html { redirect_to profile_emails_url, status: 302 }
+ format.html { redirect_to profile_emails_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index 38e3eacd229..c32507756e8 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -21,7 +21,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
@gpg_key.destroy
respond_to do |format|
- format.html { redirect_to profile_gpg_keys_url, status: 302 }
+ format.html { redirect_to profile_gpg_keys_url, status: :found }
format.js { head :ok }
end
end
@@ -30,7 +30,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
@gpg_key.revoke
respond_to do |format|
- format.html { redirect_to profile_gpg_keys_url, status: 302 }
+ format.html { redirect_to profile_gpg_keys_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 12a6cd11f80..6035258667e 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -26,7 +26,7 @@ class Profiles::KeysController < Profiles::ApplicationController
Keys::DestroyService.new(current_user).execute(@key)
respond_to do |format|
- format.html { redirect_to profile_keys_url, status: 302 }
+ format.html { redirect_to profile_keys_url, status: :found }
format.js { head :ok }
end
end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index aa9789f8a0f..29ff18a1219 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -78,7 +78,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def destroy
current_user.disable_two_factor!
- redirect_to profile_account_path, status: 302
+ redirect_to profile_account_path, status: :found
end
def skip
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 5ab6d103c89..b4f814fd3a4 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -61,7 +61,7 @@ class Projects::ApplicationController < ApplicationController
def require_non_empty_project
# Be sure to return status code 303 to avoid a double DELETE:
# http://api.rubyonrails.org/classes/ActionController/Redirecting.html
- redirect_to project_path(@project), status: 303 if @project.empty_repo?
+ redirect_to project_path(@project), status: :see_other if @project.empty_repo?
end
def require_branch_head
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 992c8ea6992..07627ffb69f 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -14,7 +14,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
end
def labels
- render json: @autocomplete_service.labels(target)
+ render json: @autocomplete_service.labels_as_hash(target)
end
def milestones
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 21a403f3765..a13d552dbd8 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -21,6 +21,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.save
- redirect_to edit_project_path(@project), status: 302
+ redirect_to edit_project_path(@project), status: :found
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index cd7250b10fc..d1dc9fe9600 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -98,7 +98,7 @@ class Projects::BranchesController < Projects::ApplicationController
flash_type = result[:status] == :error ? :alert : :notice
flash[flash_type] = result[:message]
- redirect_to project_branches_path(@project), status: 303
+ redirect_to project_branches_path(@project), status: :see_other
end
format.js { render nothing: true, status: result[:return_code] }
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index 62193257940..358fe59618b 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -62,7 +62,7 @@ class Projects::ClustersController < Projects::ApplicationController
def destroy
if cluster.destroy
flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
- redirect_to project_clusters_path(project), status: 302
+ redirect_to project_clusters_path(project), status: :found
else
flash[:notice] = _('Kubernetes cluster integration was not removed.')
render :show
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index f43ef2e5f2f..06739d8fd4a 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -35,7 +35,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def update
- if deploy_key.update_attributes(update_params)
+ if deploy_key.update(update_params)
flash[:notice] = 'Deploy key was successfully updated.'
redirect_to_repository_settings(@project)
else
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 27b7425b965..68353e6a210 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -2,7 +2,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
- before_action :authorize_create_deployment!, only: [:stop]
+ before_action :authorize_stop_environment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics]
@@ -116,7 +116,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
set_workhorse_internal_api_content_type
render json: Gitlab::Workhorse.terminal_websocket(terminal)
else
- render text: 'Not found', status: 404
+ render text: 'Not found', status: :not_found
end
end
@@ -175,4 +175,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def environment
@environment ||= project.environments.find(params[:id])
end
+
+ def authorize_stop_environment!
+ access_denied! unless can?(current_user, :stop_environment, environment)
+ end
end
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 07249fe3182..a52814e6e52 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -53,7 +53,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
send_challenges
- render plain: "HTTP Basic: Access denied\n", status: 401
+ render plain: "HTTP Basic: Access denied\n", status: :unauthorized
rescue Gitlab::Auth::MissingPersonalAccessTokenError
render_missing_personal_access_token
end
@@ -83,7 +83,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
render plain: "HTTP Basic: Access denied\n" \
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}",
- status: 401
+ status: :unauthorized
end
def repository
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index f58ee3e9109..bc5f38f3c2b 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -24,7 +24,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
def update
@group_link = @project.project_group_links.find(params[:id])
- @group_link.update_attributes(group_link_params)
+ @group_link.update(group_link_params)
end
def destroy
@@ -34,7 +34,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
respond_to do |format|
format.html do
- redirect_to project_project_members_path(project), status: 302
+ redirect_to project_project_members_path(project), status: :found
end
format.js { head :ok }
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 6800d742b0a..2da2aad9b33 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -29,7 +29,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def update
- if hook.update_attributes(hook_params)
+ if hook.update(hook_params)
flash[:notice] = 'Hook was successfully updated.'
redirect_to project_settings_integrations_path(@project)
else
@@ -48,7 +48,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy
hook.destroy
- redirect_to project_settings_integrations_path(@project), status: 302
+ redirect_to project_settings_integrations_path(@project), status: :found
end
private
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 91016f6494e..21d3c918581 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -39,7 +39,7 @@ class Projects::LabelsController < Projects::ApplicationController
else
respond_to do |format|
format.html { render :new }
- format.json { render json: { message: @label.errors.messages }, status: 400 }
+ format.json { render json: { message: @label.errors.messages }, status: :bad_request }
end
end
end
@@ -115,7 +115,7 @@ class Projects::LabelsController < Projects::ApplicationController
flash[:notice] = "#{@label.title} promoted to <a href=\"#{group_labels_path(@project.group)}\">group label</a>.".html_safe
respond_to do |format|
format.html do
- redirect_to(project_labels_path(@project), status: 303)
+ redirect_to(project_labels_path(@project), status: :see_other)
end
format.json do
render json: { url: project_labels_path(@project) }
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
index 3f4962b543d..c64ccc3d473 100644
--- a/app/controllers/projects/lfs_api_controller.rb
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -25,7 +25,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
documentation_url: "#{Gitlab.config.gitlab.url}/help"
},
- status: 501
+ status: :not_implemented
)
end
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
index 45c98d60822..dd7e673ec75 100644
--- a/app/controllers/projects/lfs_storage_controller.rb
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -28,7 +28,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
if store_file!(oid, size)
head 200
else
- render plain: 'Unprocessable entity', status: 422
+ render plain: 'Unprocessable entity', status: :unprocessable_entity
end
rescue ActiveRecord::RecordInvalid
render_lfs_forbidden
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index a7c5f858c42..dc6551fc761 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -192,7 +192,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
deployment = environment.first_deployment_for(@merge_request.diff_head_sha)
stop_url =
- if environment.stop_action? && can?(current_user, :create_deployment, environment)
+ if can?(current_user, :stop_environment, environment)
stop_project_environment_path(project, environment)
end
@@ -227,7 +227,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def rebase
RebaseWorker.perform_async(@merge_request.id, current_user.id)
- render nothing: true, status: 200
+ render nothing: true, status: :ok
end
protected
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 594563d1f6f..5e86ec93f34 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -96,7 +96,7 @@ class Projects::MilestonesController < Projects::ApplicationController
Milestones::DestroyService.new(project, current_user).execute(milestone)
respond_to do |format|
- format.html { redirect_to namespace_project_milestones_path, status: 303 }
+ format.html { redirect_to namespace_project_milestones_path, status: :see_other }
format.js { head :ok }
end
end
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index 5698ff4e706..3b24d231f3d 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -13,7 +13,7 @@ class Projects::MirrorsController < Projects::ApplicationController
end
def update
- if project.update_attributes(mirror_params)
+ if project.update(mirror_params)
flash[:notice] = 'Mirroring settings were successfully updated.'
else
flash[:alert] = project.errors.full_messages.join(', ').html_safe
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index fa258f3d9af..aeda7b3edf5 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -64,7 +64,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
def destroy
if schedule.destroy
- redirect_to pipeline_schedules_path(@project), status: 302
+ redirect_to pipeline_schedules_path(@project), status: :found
else
redirect_to pipeline_schedules_path(@project),
status: :forbidden,
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 3e0a530fdb9..19e09b3af6f 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -14,7 +14,7 @@ class Projects::ReleasesController < Projects::ApplicationController
# it exists only to save a description to each Tag.
# If description is empty we should destroy the existing record.
if release_params[:description].present?
- release.update_attributes(release_params)
+ release.update(release_params)
else
release.destroy
end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index d01f324e6fd..ecb2ece7532 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -24,7 +24,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
send_git_archive @repository, ref: @ref, format: params[:format], append_sha: append_sha
rescue => ex
logger.error("#{self.class.name}: #{ex}")
- return git_not_found!
+ git_not_found!
end
def assign_archive_vars
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index a080724634b..c098c82081e 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -21,6 +21,6 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
runner_project = project.runner_projects.find(params[:id])
runner_project.destroy
- redirect_to project_runners_path(project), status: 302
+ redirect_to project_runners_path(project), status: :found
end
end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index bef94cea989..cc7cce887bf 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -24,7 +24,7 @@ class Projects::RunnersController < Projects::ApplicationController
@runner.destroy
end
- redirect_to project_runners_path(@project), status: 302
+ redirect_to project_runners_path(@project), status: :found
end
def resume
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 690596b12db..d55046047ae 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -34,7 +34,7 @@ class Projects::ServicesController < Projects::ApplicationController
private
def service_test_response
- if @service.update_attributes(service_params[:service])
+ if @service.update(service_params[:service])
data = @service.test_data(project, current_user)
outcome = @service.test(data)
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 208a1d19862..f742d7edf83 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -82,7 +82,7 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.destroy
- redirect_to project_snippets_path(@project), status: 302
+ redirect_to project_snippets_path(@project), status: :found
end
protected
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index b62d7d9b7c5..b17753222a0 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -50,7 +50,7 @@ class Projects::TagsController < Projects::ApplicationController
respond_to do |format|
if result[:status] == :success
format.html do
- redirect_to project_tags_path(@project), status: 303
+ redirect_to project_tags_path(@project), status: :see_other
end
format.js
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
index 694b468c8d3..52d6fb82093 100644
--- a/app/controllers/projects/templates_controller.rb
+++ b/app/controllers/projects/templates_controller.rb
@@ -14,6 +14,6 @@ class Projects::TemplatesController < Projects::ApplicationController
def get_template_class
template_types = { issue: Gitlab::Template::IssueTemplate, merge_request: Gitlab::Template::MergeRequestTemplate }.with_indifferent_access
@template_type = template_types[params[:template_type]]
- render json: [], status: 404 unless @template_type
+ render json: [], status: :not_found unless @template_type
end
end
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index 93fb9da6510..a41fcb85c40 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -1,13 +1,19 @@
class Projects::TodosController < Projects::ApplicationController
- include Gitlab::Utils::StrongMemoize
- include TodosActions
-
before_action :authenticate_user!, only: [:create]
+ def create
+ todo = TodoService.new.mark_todo(issuable, current_user)
+
+ render json: {
+ count: TodosFinder.new(current_user, state: :pending).execute.count,
+ delete_path: dashboard_todo_path(todo)
+ }
+ end
+
private
def issuable
- strong_memoize(:issuable) do
+ @issuable ||= begin
case params[:issuable_type]
when "issue"
IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index e04145dd0b3..6f3de43f85a 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -50,7 +50,7 @@ class Projects::TriggersController < Projects::ApplicationController
flash[:alert] = "Could not remove the trigger."
end
- redirect_to project_settings_ci_cd_path(@project), status: 302
+ redirect_to project_settings_ci_cd_path(@project), status: :found
end
private
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index aa844e94d89..9dc0c31be49 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -116,11 +116,16 @@ class Projects::WikisController < Projects::ApplicationController
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
- @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+
+ @sidebar_page = @project_wiki.find_sidebar(params[:version_id])
+
+ unless @sidebar_page # Fallback to default sidebar
+ @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
+ end
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project)
- return false
+ false
end
def wiki_params
@@ -129,7 +134,7 @@ class Projects::WikisController < Projects::ApplicationController
def build_page(args)
WikiPage.new(@project_wiki).tap do |page|
- page.update_attributes(args)
+ page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
end
end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index ec3a5788ba1..9d1c44db137 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -2,6 +2,7 @@ class ProjectsController < Projects::ApplicationController
include IssuableCollections
include ExtractsPath
include PreviewMarkdown
+ include SendFileUpload
before_action :whitelist_query_limiting, only: [:create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs]
@@ -132,7 +133,7 @@ class ProjectsController < Projects::ApplicationController
::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:notice] = _("Project '%{project_name}' is in the process of being deleted.") % { project_name: @project.full_name }
- redirect_to dashboard_projects_path, status: 302
+ redirect_to dashboard_projects_path, status: :found
rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), status: 302, alert: ex.message
end
@@ -188,9 +189,9 @@ class ProjectsController < Projects::ApplicationController
end
def download_export
- export_project_path = @project.export_project_path
-
- if export_project_path
+ if export_project_object_storage?
+ send_upload(@project.import_export_upload.export_file)
+ elsif export_project_path
send_file export_project_path, disposition: 'attachment'
else
redirect_to(
@@ -265,8 +266,6 @@ class ProjectsController < Projects::ApplicationController
render json: options.to_json
end
- private
-
# Render project landing depending of which features are available
# So if page is not availble in the list it renders the next page
#
@@ -424,4 +423,12 @@ class ProjectsController < Projects::ApplicationController
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42440')
end
+
+ def export_project_path
+ @export_project_path ||= @project.export_project_path
+ end
+
+ def export_project_object_storage?
+ @project.export_project_object_exists?
+ end
end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 1de6ae24622..9dd652206fe 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -32,8 +32,8 @@ class SessionsController < Devise::SessionsController
super do |resource|
# User has successfully signed in, so clear any unused reset token
if resource.reset_password_token.present?
- resource.update_attributes(reset_password_token: nil,
- reset_password_sent_at: nil)
+ resource.update(reset_password_token: nil,
+ reset_password_sent_at: nil)
end
# hide the signed-in notification
diff --git a/app/controllers/sherlock/transactions_controller.rb b/app/controllers/sherlock/transactions_controller.rb
index cb6c3a7cd98..ae4953c3259 100644
--- a/app/controllers/sherlock/transactions_controller.rb
+++ b/app/controllers/sherlock/transactions_controller.rb
@@ -13,7 +13,7 @@ module Sherlock
def destroy_all
Gitlab::Sherlock.collection.clear
- redirect_to :back, status: 302
+ redirect_to :back, status: :found
end
end
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 3d51520ddf4..1d6d0943674 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -89,7 +89,7 @@ class SnippetsController < ApplicationController
@snippet.destroy
- redirect_to snippets_path, status: 302
+ redirect_to snippets_path, status: :found
end
protected
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index c7d6bc6cfdc..b06595081e7 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -16,6 +16,7 @@
# personal: boolean
# search: string
# non_archived: boolean
+# archived: 'only' or boolean
#
class ProjectsFinder < UnionFinder
include CustomAttributesFilter
@@ -130,7 +131,7 @@ class ProjectsFinder < UnionFinder
def by_archived(projects)
if params[:non_archived]
projects.non_archived
- elsif params.key?(:archived) # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false`
+ elsif params.key?(:archived)
if params[:archived] == 'only'
projects.archived
elsif Gitlab::Utils.to_boolean(params[:archived])
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 2156413fb26..09e2c586f2a 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -15,7 +15,6 @@
class TodosFinder
prepend FinderWithCrossProjectAccess
include FinderMethods
- include Gitlab::Utils::StrongMemoize
requires_cross_project_access unless: -> { project? }
@@ -35,11 +34,9 @@ class TodosFinder
items = by_author(items)
items = by_state(items)
items = by_type(items)
- items = by_group(items)
# Filtering by project HAS TO be the last because we use
# the project IDs yielded by the todos query thus far
items = by_project(items)
- items = visible_to_user(items)
sort(items)
end
@@ -85,10 +82,6 @@ class TodosFinder
params[:project_id].present?
end
- def group?
- params[:group_id].present?
- end
-
def project
return @project if defined?(@project)
@@ -107,14 +100,18 @@ class TodosFinder
@project
end
- def group
- strong_memoize(:group) do
- Group.find(params[:group_id])
+ def project_ids(items)
+ ids = items.except(:order).select(:project_id)
+ if Gitlab::Database.mysql?
+ # To make UPDATE work on MySQL, wrap it in a SELECT with an alias
+ ids = Todo.except(:order).select('*').from("(#{ids.to_sql}) AS t")
end
+
+ ids
end
def type?
- type.present? && %w(Issue MergeRequest Epic).include?(type)
+ type.present? && %w(Issue MergeRequest).include?(type)
end
def type
@@ -151,37 +148,12 @@ class TodosFinder
def by_project(items)
if project?
- items = items.where(project: project)
- end
-
- items
- end
+ items.where(project: project)
+ else
+ projects = Project.public_or_visible_to_user(current_user)
- def by_group(items)
- if group?
- groups = group.self_and_descendants
- items = items.where(
- 'project_id IN (?) OR group_id IN (?)',
- Project.where(group: groups).select(:id),
- groups.select(:id)
- )
+ items.joins(:project).merge(projects)
end
-
- items
- end
-
- def visible_to_user(items)
- projects = Project.public_or_visible_to_user(current_user)
- groups = Group.public_or_visible_to_user(current_user)
-
- items
- .joins('LEFT JOIN namespaces ON namespaces.id = todos.group_id')
- .joins('LEFT JOIN projects ON projects.id = todos.project_id')
- .where(
- 'project_id IN (?) OR group_id IN (?)',
- projects.select(:id),
- groups.select(:id)
- )
end
def by_state(items)
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index ef1bf283d0c..358b896702b 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -251,6 +251,7 @@ module ApplicationSettingsHelper
:user_oauth_applications,
:version_check_enabled,
:allow_local_requests_from_hooks_and_services,
+ :hide_third_party_offers,
:enforce_terms,
:terms,
:mirror_available
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index c24d340d184..8fd0b6f14c6 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -4,6 +4,7 @@ module ClustersHelper
end
def render_gcp_signup_offer
+ return if Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers?
return unless show_gcp_signup_offer?
content_tag :section, class: 'no-animate expanded' do
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 353479776b8..8766bb43cac 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -131,19 +131,6 @@ module IssuablesHelper
end
end
- def group_dropdown_label(group_id, default_label)
- return default_label if group_id.nil?
- return "Any group" if group_id == "0"
-
- group = ::Group.find_by(id: group_id)
-
- if group
- group.full_name
- else
- default_label
- end
- end
-
def milestone_dropdown_label(milestone_title, default_label = "Milestone")
title =
case milestone_title
@@ -249,6 +236,7 @@ module IssuablesHelper
issuableRef: issuable.to_reference,
markdownPreviewPath: preview_markdown_path(parent),
markdownDocsPath: help_page_path('user/markdown'),
+ markdownVersion: issuable.cached_markdown_version,
issuableTemplates: issuable_templates(issuable),
initialTitleHtml: markdown_field(issuable, :title),
initialTitleText: issuable.title,
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index 39e7a7fd396..cbb971cf8b7 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -107,6 +107,7 @@ module MarkupHelper
def markup(file_name, text, context = {})
context[:project] ||= @project
+ context[:markdown_engine] ||= :redcarpet
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
prepare_for_rendering(html, context)
end
@@ -120,7 +121,8 @@ module MarkupHelper
project: @project,
project_wiki: @project_wiki,
page_slug: wiki_page.slug,
- issuable_state_filter_enabled: true
+ issuable_state_filter_enabled: true,
+ markdown_engine: :redcarpet
}
html =
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 9be93fa69ae..9008db1b300 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -3,7 +3,7 @@ module NamespacesHelper
params.dig(:project, :namespace_id) || params[:namespace_id]
end
- def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
+ def namespaces_options(selected = :current_user, display_path: false, extra_group: nil, groups_only: false)
groups = current_user.manageable_groups
.joins(:route)
.includes(:route)
@@ -20,10 +20,13 @@ module NamespacesHelper
options = []
options << options_for_group(groups, display_path: display_path, type: 'group')
- options << options_for_group(users, display_path: display_path, type: 'user')
- if selected == :current_user && current_user.namespace
- selected = current_user.namespace.id
+ unless groups_only
+ options << options_for_group(users, display_path: display_path, type: 'user')
+
+ if selected == :current_user && current_user.namespace
+ selected = current_user.namespace.id
+ end
end
grouped_options_for_select(options, selected)
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 3fa2e5452c8..5404ead44f3 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -169,6 +169,7 @@ module NotesHelper
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'),
markdownDocsPath: help_page_path('user/markdown'),
+ markdownVersion: issuable.cached_markdown_version,
quickActionsDocsPath: help_page_path('user/project/quick_actions'),
closePath: close_issuable_path(issuable),
reopenPath: reopen_issuable_path(issuable),
diff --git a/app/helpers/pipeline_schedules_helper.rb b/app/helpers/pipeline_schedules_helper.rb
index 6edaf78de1b..4b9f6bd2caf 100644
--- a/app/helpers/pipeline_schedules_helper.rb
+++ b/app/helpers/pipeline_schedules_helper.rb
@@ -3,7 +3,7 @@ module PipelineSchedulesHelper
ActiveSupport::TimeZone.all.map do |timezone|
{
name: timezone.name,
- offset: timezone.utc_offset,
+ offset: timezone.now.utc_offset,
identifier: timezone.tzinfo.identifier
}
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index b0f381db5ab..221f1aa9dd8 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -413,20 +413,6 @@ module ProjectsHelper
@ref || @repository.try(:root_ref)
end
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1235
- def sanitize_repo_path(project, message)
- return '' unless message.present?
-
- exports_path = File.join(Settings.shared['path'], 'tmp/project_exports')
- filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]")
-
- disk_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
- end
-
- filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]")
- end
-
def project_child_container_class(view_path)
view_path == "projects/issues/issues" ? "prepend-top-default" : "project-show-#{view_path}"
end
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 9151543dfdc..ebfde993456 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -8,7 +8,7 @@ module SubmoduleHelper
url = repository.submodule_url_for(ref, submodule_item.path)
if url == '.' || url == './'
- url = File.join(Gitlab.config.gitlab.url, @project.full_path)
+ url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end
if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z}
@@ -31,7 +31,7 @@ module SubmoduleHelper
[namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, submodule_item.id)]
elsif relative_self_url?(url)
- relative_self_links(url, submodule_item.id)
+ relative_self_links(url, submodule_item.id, repository.project)
elsif github_dot_com_url?(url)
standard_links('github.com', namespace, project, submodule_item.id)
elsif gitlab_dot_com_url?(url)
@@ -73,7 +73,7 @@ module SubmoduleHelper
[base, [base, '/tree/', commit].join('')]
end
- def relative_self_links(url, commit)
+ def relative_self_links(url, commit, project)
url.rstrip!
# Map relative links to a namespace and project
# For example:
@@ -85,7 +85,7 @@ module SubmoduleHelper
namespace = components.pop.gsub(/^\.\.$/, '')
if namespace.empty?
- namespace = @project.namespace.full_path
+ namespace = project.namespace.full_path
end
begin
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index 271e839692a..336385f6798 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -5,9 +5,13 @@ module TimeHelper
seconds = interval_in_seconds - minutes * 60
if minutes >= 1
- "#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}"
+ if seconds % 60 == 0
+ pluralize(minutes, "minute")
+ else
+ [pluralize(minutes, "minute"), pluralize(seconds, "second")].to_sentence
+ end
else
- "#{pluralize(seconds, "second")}"
+ pluralize(seconds, "second")
end
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 7cd74358168..f7620e0b6b8 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -43,7 +43,7 @@ module TodosHelper
project_commit_path(todo.project,
todo.target, anchor: anchor)
else
- path = [todo.parent, todo.target]
+ path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target]
path.unshift(:pipelines) if todo.build_failed?
@@ -167,12 +167,4 @@ module TodosHelper
def show_todo_state?(todo)
(todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state)
end
-
- def todo_group_options
- groups = current_user.authorized_groups.map do |group|
- { id: group.id, text: group.full_name }
- end
-
- groups.unshift({ id: '', text: 'Any Group' }).to_json
- end
end
diff --git a/spec/mailers/previews/devise_mailer_preview.rb b/app/mailers/previews/devise_mailer_preview.rb
index d6588efc486..d6588efc486 100644
--- a/spec/mailers/previews/devise_mailer_preview.rb
+++ b/app/mailers/previews/devise_mailer_preview.rb
diff --git a/spec/mailers/previews/email_rejection_mailer_preview.rb b/app/mailers/previews/email_rejection_mailer_preview.rb
index 639e8471232..639e8471232 100644
--- a/spec/mailers/previews/email_rejection_mailer_preview.rb
+++ b/app/mailers/previews/email_rejection_mailer_preview.rb
diff --git a/spec/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index e32fd0bd120..3615cde8026 100644
--- a/spec/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -153,7 +153,7 @@ class NotifyPreview < ActionMailer::Preview
cleanup do
note = yield
- Notify.public_send(method, user.id, note)
+ Notify.public_send(method, user.id, note) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/spec/mailers/previews/repository_check_mailer_preview.rb b/app/mailers/previews/repository_check_mailer_preview.rb
index 19d4eab1805..19d4eab1805 100644
--- a/spec/mailers/previews/repository_check_mailer_preview.rb
+++ b/app/mailers/previews/repository_check_mailer_preview.rb
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index bddeb8b0352..f770b219422 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -294,6 +294,7 @@ class ApplicationSetting < ActiveRecord::Base
gitaly_timeout_medium: 30,
gitaly_timeout_default: 55,
allow_local_requests_from_hooks_and_services: false,
+ hide_third_party_offers: false,
mirror_available: true
}
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 19949f83351..db86400128c 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -371,7 +371,7 @@ module Ci
def update_coverage
coverage = trace.extract_coverage(coverage_regex)
- update_attributes(coverage: coverage) if coverage.present?
+ update(coverage: coverage) if coverage.present?
end
def parse_trace_sections!
@@ -437,9 +437,9 @@ module Ci
end
def artifacts_metadata_entry(path, **options)
- artifacts_metadata.use_file do |metadata_path|
+ artifacts_metadata.open do |metadata_stream|
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
- metadata_path,
+ metadata_stream,
path,
**options)
@@ -653,6 +653,7 @@ module Ci
variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage)
variables.append(key: 'CI_COMMIT_SHA', value: sha)
+ variables.append(key: 'CI_COMMIT_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_COMMIT_REF_NAME', value: ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: ref_slug)
variables.append(key: "CI_COMMIT_TAG", value: ref) if tag?
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index 4856f10846c..b442de34061 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -1,54 +1,58 @@
module Ci
class BuildTraceChunk < ActiveRecord::Base
include FastDestroyAll
+ include ::Gitlab::ExclusiveLeaseHelpers
extend Gitlab::Ci::Model
belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
default_value_for :data_store, :redis
- WriteError = Class.new(StandardError)
-
CHUNK_SIZE = 128.kilobytes
- CHUNK_REDIS_TTL = 1.week
WRITE_LOCK_RETRY = 10
WRITE_LOCK_SLEEP = 0.01.seconds
WRITE_LOCK_TTL = 1.minute
+ # Note: The ordering of this enum is related to the precedence of persist store.
+ # The bottom item takes the higest precedence, and the top item takes the lowest precedence.
enum data_store: {
redis: 1,
- db: 2
+ database: 2,
+ fog: 3
}
class << self
- def redis_data_key(build_id, chunk_index)
- "gitlab:ci:trace:#{build_id}:chunks:#{chunk_index}"
+ def all_stores
+ @all_stores ||= self.data_stores.keys
end
- def redis_data_keys
- redis.pluck(:build_id, :chunk_index).map do |data|
- redis_data_key(data.first, data.second)
- end
+ def persistable_store
+ # get first available store from the back of the list
+ all_stores.reverse.find { |store| get_store_class(store).available? }
end
- def redis_delete_data(keys)
- return if keys.empty?
-
- Gitlab::Redis::SharedState.with do |redis|
- redis.del(keys)
- end
+ def get_store_class(store)
+ @stores ||= {}
+ @stores[store] ||= "Ci::BuildTraceChunks::#{store.capitalize}".constantize.new
end
##
# FastDestroyAll concerns
def begin_fast_destroy
- redis_data_keys
+ all_stores.each_with_object({}) do |store, result|
+ relation = public_send(store) # rubocop:disable GitlabSecurity/PublicSend
+ keys = get_store_class(store).keys(relation)
+
+ result[store] = keys if keys.present?
+ end
end
##
# FastDestroyAll concerns
def finalize_fast_destroy(keys)
- redis_delete_data(keys)
+ keys.each do |store, value|
+ get_store_class(store).delete_keys(value)
+ end
end
end
@@ -66,10 +70,15 @@ module Ci
end
def append(new_data, offset)
+ raise ArgumentError, 'New data is missing' unless new_data
raise ArgumentError, 'Offset is out of range' if offset > size || offset < 0
raise ArgumentError, 'Chunk size overflow' if CHUNK_SIZE < (offset + new_data.bytesize)
- set_data(data.byteslice(0, offset) + new_data)
+ in_lock(*lock_params) do # Write opetation is atomic
+ unsafe_set_data!(data.byteslice(0, offset) + new_data)
+ end
+
+ schedule_to_persist if full?
end
def size
@@ -88,93 +97,63 @@ module Ci
(start_offset...end_offset)
end
- def use_database!
- in_lock do
- break if db?
- break unless size > 0
-
- self.update!(raw_data: data, data_store: :db)
- self.class.redis_delete_data([redis_data_key])
+ def persist_data!
+ in_lock(*lock_params) do # Write opetation is atomic
+ unsafe_persist_to!(self.class.persistable_store)
end
end
private
- def get_data
- if redis?
- redis_data
- elsif db?
- raw_data
- else
- raise 'Unsupported data store'
- end&.force_encoding(Encoding::BINARY) # Redis/Database return UTF-8 string as default
- end
-
- def set_data(value)
- raise ArgumentError, 'too much data' if value.bytesize > CHUNK_SIZE
-
- in_lock do
- if redis?
- redis_set_data(value)
- elsif db?
- self.raw_data = value
- else
- raise 'Unsupported data store'
- end
+ def unsafe_persist_to!(new_store)
+ return if data_store == new_store.to_s
+ raise ArgumentError, 'Can not persist empty data' unless size > 0
- @data = value
+ old_store_class = self.class.get_store_class(data_store)
- save! if changed?
+ get_data.tap do |the_data|
+ self.raw_data = nil
+ self.data_store = new_store
+ unsafe_set_data!(the_data)
end
- schedule_to_db if full?
- end
-
- def schedule_to_db
- return if db?
-
- Ci::BuildTraceChunkFlushWorker.perform_async(id)
+ old_store_class.delete_data(self)
end
- def full?
- size == CHUNK_SIZE
+ def get_data
+ self.class.get_store_class(data_store).data(self)&.force_encoding(Encoding::BINARY) # Redis/Database return UTF-8 string as default
+ rescue Excon::Error::NotFound
+ # If the data store is :fog and the file does not exist in the object storage, this method returns nil.
end
- def redis_data
- Gitlab::Redis::SharedState.with do |redis|
- redis.get(redis_data_key)
- end
- end
+ def unsafe_set_data!(value)
+ raise ArgumentError, 'New data size exceeds chunk size' if value.bytesize > CHUNK_SIZE
- def redis_set_data(data)
- Gitlab::Redis::SharedState.with do |redis|
- redis.set(redis_data_key, data, ex: CHUNK_REDIS_TTL)
- end
- end
+ self.class.get_store_class(data_store).set_data(self, value)
+ @data = value
- def redis_data_key
- self.class.redis_data_key(build_id, chunk_index)
+ save! if changed?
end
- def in_lock
- write_lock_key = "trace_write:#{build_id}:chunks:#{chunk_index}"
+ def schedule_to_persist
+ return if data_persisted?
- lease = Gitlab::ExclusiveLease.new(write_lock_key, timeout: WRITE_LOCK_TTL)
- retry_count = 0
+ Ci::BuildTraceChunkFlushWorker.perform_async(id)
+ end
- until uuid = lease.try_obtain
- # Keep trying until we obtain the lease. To prevent hammering Redis too
- # much we'll wait for a bit between retries.
- sleep(WRITE_LOCK_SLEEP)
- break if WRITE_LOCK_RETRY < (retry_count += 1)
- end
+ def data_persisted?
+ !redis?
+ end
- raise WriteError, 'Failed to obtain write lock' unless uuid
+ def full?
+ size == CHUNK_SIZE
+ end
- self.reload if self.persisted?
- return yield
- ensure
- Gitlab::ExclusiveLease.cancel(write_lock_key, uuid)
+ def lock_params
+ ["trace_write:#{build_id}:chunks:#{chunk_index}",
+ { ttl: WRITE_LOCK_TTL,
+ retries: WRITE_LOCK_RETRY,
+ sleep_sec: WRITE_LOCK_SLEEP }]
end
end
end
diff --git a/app/models/ci/build_trace_chunks/database.rb b/app/models/ci/build_trace_chunks/database.rb
new file mode 100644
index 00000000000..3666d77c790
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/database.rb
@@ -0,0 +1,29 @@
+module Ci
+ module BuildTraceChunks
+ class Database
+ def available?
+ true
+ end
+
+ def keys(relation)
+ []
+ end
+
+ def delete_keys(keys)
+ # no-op
+ end
+
+ def data(model)
+ model.raw_data
+ end
+
+ def set_data(model, data)
+ model.raw_data = data
+ end
+
+ def delete_data(model)
+ model.update_columns(raw_data: nil) unless model.raw_data.nil?
+ end
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_chunks/fog.rb b/app/models/ci/build_trace_chunks/fog.rb
new file mode 100644
index 00000000000..7506c40a39d
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/fog.rb
@@ -0,0 +1,59 @@
+module Ci
+ module BuildTraceChunks
+ class Fog
+ def available?
+ object_store.enabled
+ end
+
+ def data(model)
+ connection.get_object(bucket_name, key(model))[:body]
+ end
+
+ def set_data(model, data)
+ connection.put_object(bucket_name, key(model), data)
+ end
+
+ def delete_data(model)
+ delete_keys([[model.build_id, model.chunk_index]])
+ end
+
+ def keys(relation)
+ return [] unless available?
+
+ relation.pluck(:build_id, :chunk_index)
+ end
+
+ def delete_keys(keys)
+ keys.each do |key|
+ connection.delete_object(bucket_name, key_raw(*key))
+ end
+ end
+
+ private
+
+ def key(model)
+ key_raw(model.build_id, model.chunk_index)
+ end
+
+ def key_raw(build_id, chunk_index)
+ "tmp/builds/#{build_id.to_i}/chunks/#{chunk_index.to_i}.log"
+ end
+
+ def bucket_name
+ return unless available?
+
+ object_store.remote_directory
+ end
+
+ def connection
+ return unless available?
+
+ @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys)
+ end
+
+ def object_store
+ Gitlab.config.artifacts.object_store
+ end
+ end
+ end
+end
diff --git a/app/models/ci/build_trace_chunks/redis.rb b/app/models/ci/build_trace_chunks/redis.rb
new file mode 100644
index 00000000000..fdb6065e2a0
--- /dev/null
+++ b/app/models/ci/build_trace_chunks/redis.rb
@@ -0,0 +1,51 @@
+module Ci
+ module BuildTraceChunks
+ class Redis
+ CHUNK_REDIS_TTL = 1.week
+
+ def available?
+ true
+ end
+
+ def data(model)
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.get(key(model))
+ end
+ end
+
+ def set_data(model, data)
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(key(model), data, ex: CHUNK_REDIS_TTL)
+ end
+ end
+
+ def delete_data(model)
+ delete_keys([[model.build_id, model.chunk_index]])
+ end
+
+ def keys(relation)
+ relation.pluck(:build_id, :chunk_index)
+ end
+
+ def delete_keys(keys)
+ return if keys.empty?
+
+ keys = keys.map { |key| key_raw(*key) }
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.del(keys)
+ end
+ end
+
+ private
+
+ def key(model)
+ key_raw(model.build_id, model.chunk_index)
+ end
+
+ def key_raw(build_id, chunk_index)
+ "gitlab:ci:trace:#{build_id.to_i}:chunks:#{chunk_index.to_i}"
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 9f6358cecbe..b05bf909058 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -40,6 +40,18 @@ module CacheMarkdownField
end
end
+ class MarkdownEngine
+ def self.from_version(version = nil)
+ return :common_mark if version.nil? || version == 0
+
+ if version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
+ :redcarpet
+ else
+ :common_mark
+ end
+ end
+ end
+
def skip_project_check?
false
end
@@ -57,7 +69,7 @@ module CacheMarkdownField
# Banzai is less strict about authors, so don't always have an author key
context[:author] = self.author if self.respond_to?(:author)
- context[:markdown_engine] = markdown_engine
+ context[:markdown_engine] = MarkdownEngine.from_version(latest_cached_markdown_version)
context
end
@@ -123,14 +135,6 @@ module CacheMarkdownField
end
end
- def markdown_engine
- if latest_cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
- :redcarpet
- else
- :common_mark
- end
- end
-
included do
cattr_reader :cached_markdown_fields do
FieldData.new
diff --git a/app/models/concerns/cacheable_attributes.rb b/app/models/concerns/cacheable_attributes.rb
index d58d7165969..606549b947f 100644
--- a/app/models/concerns/cacheable_attributes.rb
+++ b/app/models/concerns/cacheable_attributes.rb
@@ -7,7 +7,7 @@ module CacheableAttributes
class_methods do
def cache_key
- "#{name}:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}".freeze
+ "#{name}:#{Gitlab::VERSION}:#{Rails.version}".freeze
end
# Can be overriden
@@ -69,6 +69,6 @@ module CacheableAttributes
end
def cache!
- Rails.cache.write(self.class.cache_key, self)
+ Rails.cache.write(self.class.cache_key, self, expires_in: 1.minute)
end
end
diff --git a/app/models/concerns/group_descendant.rb b/app/models/concerns/group_descendant.rb
index 261ace57a17..5e9a95c3282 100644
--- a/app/models/concerns/group_descendant.rb
+++ b/app/models/concerns/group_descendant.rb
@@ -44,8 +44,8 @@ module GroupDescendant
This error is not user facing, but causes a +1 query.
MSG
extras = {
- parent: parent,
- child: child,
+ parent: parent.inspect,
+ child: child.inspect,
preloaded: preloaded.map(&:full_path)
}
issue_url = 'https://gitlab.com/gitlab-org/gitlab-ce/issues/40785'
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 7a459078151..b93c1145f82 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -243,12 +243,6 @@ module Issuable
opened?
end
- def overdue?
- return false unless respond_to?(:due_date)
-
- due_date.try(:past?) || false
- end
-
def user_notes_count
if notes.loaded?
# Use the in-memory association to select and count to avoid hitting the db
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index 94eef4ff7cd..dbe8d31de37 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -23,7 +23,7 @@ module ProtectedRef
# If we don't `protected_branch` or `protected_tag` would be empty and
# `project` cannot be delegated to it, which in turn would cause validations
# to fail.
- has_many :"#{type}_access_levels", inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent
+ has_many :"#{type}_access_levels", inverse_of: self.model_name.singular
validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." }
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
index e3a7f2d5498..71b0c3468b9 100644
--- a/app/models/concerns/protected_ref_access.rb
+++ b/app/models/concerns/protected_ref_access.rb
@@ -2,19 +2,20 @@ module ProtectedRefAccess
extend ActiveSupport::Concern
ALLOWED_ACCESS_LEVELS = [
- Gitlab::Access::MASTER,
+ Gitlab::Access::MAINTAINER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS
].freeze
HUMAN_ACCESS_LEVELS = {
- Gitlab::Access::MASTER => "Maintainers".freeze,
+ Gitlab::Access::MAINTAINER => "Maintainers".freeze,
Gitlab::Access::DEVELOPER => "Developers + Maintainers".freeze,
Gitlab::Access::NO_ACCESS => "No one".freeze
}.freeze
included do
- scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
+ scope :master, -> { maintainer } # @deprecated
+ scope :maintainer, -> { where(access_level: Gitlab::Access::MAINTAINER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
validates :access_level, presence: true, if: :role?, inclusion: {
diff --git a/app/models/concerns/select_for_project_authorization.rb b/app/models/concerns/select_for_project_authorization.rb
index 58194b0ea13..7af0fdbd618 100644
--- a/app/models/concerns/select_for_project_authorization.rb
+++ b/app/models/concerns/select_for_project_authorization.rb
@@ -6,8 +6,11 @@ module SelectForProjectAuthorization
select("projects.id AS project_id, members.access_level")
end
- def select_as_master_for_project_authorization
- select(["projects.id AS project_id", "#{Gitlab::Access::MASTER} AS access_level"])
+ def select_as_maintainer_for_project_authorization
+ select(["projects.id AS project_id", "#{Gitlab::Access::MAINTAINER} AS access_level"])
end
+
+ # @deprecated
+ alias_method :select_as_master_for_project_authorization, :select_as_maintainer_for_project_authorization
end
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index ac86e9e8de0..687246b47b2 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -92,10 +92,6 @@ class Deployment < ActiveRecord::Base
@stop_action ||= manual_actions.find_by(name: on_stop)
end
- def stop_action?
- stop_action.present?
- end
-
def formatted_deployment_time
created_at.to_time.in_time_zone.to_s(:medium)
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 8d523dae324..4856d313318 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -117,7 +117,7 @@ class Environment < ActiveRecord::Base
external_url.gsub(%r{\A.*?://}, '')
end
- def stop_action?
+ def stop_action_available?
available? && stop_action.present?
end
diff --git a/app/models/group.rb b/app/models/group.rb
index b0392774379..ddebaff50b0 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -39,8 +39,6 @@ class Group < Namespace
has_many :boards
has_many :badges, class_name: 'GroupBadge'
- has_many :todos
-
accepts_nested_attributes_for :variables, allow_destroy: true
validate :visibility_level_allowed_by_projects
@@ -84,12 +82,6 @@ class Group < Namespace
where(id: user.authorized_groups.select(:id).reorder(nil))
end
- def public_or_visible_to_user(user)
- where('id IN (?) OR namespaces.visibility_level IN (?)',
- user.authorized_groups.select(:id),
- Gitlab::VisibilityLevel.levels_for_user(user))
- end
-
def select_for_project_authorization
if current_scope.joins_values.include?(:shared_projects)
joins('INNER JOIN namespaces project_namespace ON project_namespace.id = projects.namespace_id')
@@ -186,10 +178,13 @@ class Group < Namespace
add_user(user, :developer, current_user: current_user)
end
- def add_master(user, current_user = nil)
- add_user(user, :master, current_user: current_user)
+ def add_maintainer(user, current_user = nil)
+ add_user(user, :maintainer, current_user: current_user)
end
+ # @deprecated
+ alias_method :add_master, :add_maintainer
+
def add_owner(user, current_user = nil)
add_user(user, :owner, current_user: current_user)
end
@@ -206,12 +201,15 @@ class Group < Namespace
members_with_parents.owners.where(user_id: user).any?
end
- def has_master?(user)
+ def has_maintainer?(user)
return false unless user
- members_with_parents.masters.where(user_id: user).any?
+ members_with_parents.maintainers.where(user_id: user).any?
end
+ # @deprecated
+ alias_method :has_master?, :has_maintainer?
+
# Check if user is a last owner of the group.
# Parent owners are ignored for nested groups.
def last_owner?(user)
diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb
new file mode 100644
index 00000000000..60d53d6c2c8
--- /dev/null
+++ b/app/models/import_export_upload.rb
@@ -0,0 +1,13 @@
+class ImportExportUpload < ActiveRecord::Base
+ include WithUploads
+ include ObjectStorage::BackgroundMove
+
+ belongs_to :project
+
+ mount_uploader :import_file, ImportExportUploader
+ mount_uploader :export_file, ImportExportUploader
+
+ def retrieve_upload(_identifier, paths)
+ Upload.find_by(model: self, path: paths)
+ end
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 983684a5e05..4715d942c8d 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -275,6 +275,10 @@ class Issue < ActiveRecord::Base
user ? readable_by?(user) : publicly_visible?
end
+ def overdue?
+ due_date.try(:past?) || false
+ end
+
def check_for_spam?
project.public? && (title_changed? || description_changed?)
end
diff --git a/app/models/member.rb b/app/models/member.rb
index 68572f2e33a..00a13a279a9 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -69,9 +69,11 @@ class Member < ActiveRecord::Base
scope :guests, -> { active.where(access_level: GUEST) }
scope :reporters, -> { active.where(access_level: REPORTER) }
scope :developers, -> { active.where(access_level: DEVELOPER) }
- scope :masters, -> { active.where(access_level: MASTER) }
+ scope :maintainers, -> { active.where(access_level: MAINTAINER) }
+ scope :masters, -> { maintainers } # @deprecated
scope :owners, -> { active.where(access_level: OWNER) }
- scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) }
+ scope :owners_and_maintainers, -> { active.where(access_level: [OWNER, MAINTAINER]) }
+ scope :owners_and_masters, -> { owners_and_maintainers } # @deprecated
scope :order_name_asc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'ASC')) }
scope :order_name_desc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'DESC')) }
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 024106056b4..4f27d0aeaf8 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -17,19 +17,19 @@ class ProjectMember < Member
# Add users to projects with passed access option
#
# access can be an integer representing a access code
- # or symbol like :master representing role
+ # or symbol like :maintainer representing role
#
# Ex.
# add_users_to_projects(
# project_ids,
# user_ids,
- # ProjectMember::MASTER
+ # ProjectMember::MAINTAINER
# )
#
# add_users_to_projects(
# project_ids,
# user_ids,
- # :master
+ # :maintainer
# )
#
def add_users_to_projects(project_ids, users, access_level, current_user: nil, expires_at: nil)
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index d05dcfd083a..14cc12b38a5 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -131,9 +131,10 @@ class Milestone < ActiveRecord::Base
rel.order(:project_id, :due_date).select('DISTINCT ON (project_id) id')
else
rel
- .group(:project_id)
+ .group(:project_id, :due_date, :id)
.having('due_date = MIN(due_date)')
.pluck(:id, :project_id, :due_date)
+ .uniq(&:second)
.map(&:first)
end
end
diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb
index 22d48c9e661..d667948deae 100644
--- a/app/models/network/commit.rb
+++ b/app/models/network/commit.rb
@@ -11,8 +11,8 @@ module Network
@parent_spaces = []
end
- def method_missing(m, *args, &block)
- @commit.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(msg, *args, &block)
+ @commit.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
def space
diff --git a/app/models/note.rb b/app/models/note.rb
index bbad9d90cc4..fe3507adcb3 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -229,10 +229,6 @@ class Note < ActiveRecord::Base
!for_personal_snippet?
end
- def for_issuable?
- for_issue? || for_merge_request?
- end
-
def skip_project_check?
!for_project_noteable?
end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 9195408551f..1933c46ee44 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -32,6 +32,7 @@ class NotificationSetting < ActiveRecord::Base
:reopen_issue,
:close_issue,
:reassign_issue,
+ :issue_due,
:new_merge_request,
:push_to_merge_request,
:reopen_merge_request,
diff --git a/app/models/project.rb b/app/models/project.rb
index 8f40470de82..ae0a13b17dc 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -171,6 +171,7 @@ class Project < ActiveRecord::Base
has_one :fork_network, through: :fork_network_member
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
+ has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
# Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id'
@@ -268,7 +269,8 @@ class Project < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
delegate :add_user, :add_users, to: :team
- delegate :add_guest, :add_reporter, :add_developer, :add_master, :add_role, to: :team
+ delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_role, to: :team
+ delegate :add_master, to: :team # @deprecated
delegate :group_runners_enabled, :group_runners_enabled=, :group_runners_enabled?, to: :ci_cd_settings
# Validations
@@ -366,8 +368,10 @@ class Project < ActiveRecord::Base
chronic_duration_attr :build_timeout_human_readable, :build_timeout, default: 3600
validates :build_timeout, allow_nil: true,
- numericality: { greater_than_or_equal_to: 600,
- message: 'needs to be at least 10 minutes' }
+ numericality: { greater_than_or_equal_to: 10.minutes,
+ less_than: 1.month,
+ only_integer: true,
+ message: 'needs to be beetween 10 minutes and 1 month' }
# Returns a collection of projects that is either public or visible to the
# logged in user.
@@ -1646,10 +1650,10 @@ class Project < ActiveRecord::Base
params = {
name: default_branch,
push_access_levels_attributes: [{
- access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
+ access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MAINTAINER
}],
merge_access_levels_attributes: [{
- access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
+ access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MAINTAINER
}]
}
@@ -1712,7 +1716,7 @@ class Project < ActiveRecord::Base
:started
elsif after_export_in_progress?
:after_export_action
- elsif export_project_path
+ elsif export_project_path || export_project_object_exists?
:finished
else
:none
@@ -1727,16 +1731,21 @@ class Project < ActiveRecord::Base
import_export_shared.after_export_in_progress?
end
- def remove_exports
- return nil unless export_path.present?
-
- FileUtils.rm_rf(export_path)
+ def remove_exports(path = export_path)
+ if path.present?
+ FileUtils.rm_rf(path)
+ elsif export_project_object_exists?
+ import_export_upload.remove_export_file!
+ import_export_upload.save
+ end
end
def remove_exported_project_file
- return unless export_project_path.present?
+ remove_exports(export_project_path)
+ end
- FileUtils.rm_f(export_project_path)
+ def export_project_object_exists?
+ Gitlab::ImportExport.object_storage? && import_export_upload&.export_file&.file
end
def full_path_slug
@@ -2162,10 +2171,13 @@ class Project < ActiveRecord::Base
merge_requests = source_of_merge_requests.opened
.where(allow_collaboration: true)
- if branch_name
- merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
- else
- merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ # Issue for N+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/49322
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ if branch_name
+ merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
+ else
+ merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ end
end
end
diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb
index ac1e9ab2b0b..cf8fc41e870 100644
--- a/app/models/project_group_link.rb
+++ b/app/models/project_group_link.rb
@@ -4,7 +4,8 @@ class ProjectGroupLink < ActiveRecord::Base
GUEST = 10
REPORTER = 20
DEVELOPER = 30
- MASTER = 40
+ MAINTAINER = 40
+ MASTER = MAINTAINER # @deprecated
belongs_to :project
belongs_to :group
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 9a38806baab..c7d0f49d837 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -19,10 +19,13 @@ class ProjectTeam
add_user(user, :developer, current_user: current_user)
end
- def add_master(user, current_user: nil)
- add_user(user, :master, current_user: current_user)
+ def add_maintainer(user, current_user: nil)
+ add_user(user, :maintainer, current_user: current_user)
end
+ # @deprecated
+ alias_method :add_master, :add_maintainer
+
def add_role(user, role, current_user: nil)
public_send(:"add_#{role}", user, current_user: current_user) # rubocop:disable GitlabSecurity/PublicSend
end
@@ -81,10 +84,13 @@ class ProjectTeam
@developers ||= fetch_members(Gitlab::Access::DEVELOPER)
end
- def masters
- @masters ||= fetch_members(Gitlab::Access::MASTER)
+ def maintainers
+ @maintainers ||= fetch_members(Gitlab::Access::MAINTAINER)
end
+ # @deprecated
+ alias_method :masters, :maintainers
+
def owners
@owners ||=
if group
@@ -136,10 +142,13 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::DEVELOPER
end
- def master?(user)
- max_member_access(user.id) == Gitlab::Access::MASTER
+ def maintainer?(user)
+ max_member_access(user.id) == Gitlab::Access::MAINTAINER
end
+ # @deprecated
+ alias_method :master?, :maintainer?
+
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
def member?(user, min_access_level = Gitlab::Access::GUEST)
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index a6f94b3e3b0..3aa56b3983f 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProjectWiki
include Gitlab::ShellAdapter
include Storage::LegacyProjectWiki
@@ -9,6 +11,7 @@ class ProjectWiki
}.freeze unless defined?(MARKUPS)
CouldNotCreateWikiError = Class.new(StandardError)
+ SIDEBAR = '_sidebar'
# Returns a string describing what went wrong after
# an operation fails.
@@ -20,7 +23,6 @@ class ProjectWiki
@user = user
end
- delegate :empty?, to: :pages
delegate :repository_storage, :hashed_storage?, to: :project
def path
@@ -74,6 +76,10 @@ class ProjectWiki
!!find_page('home')
end
+ def empty?
+ pages(limit: 1).empty?
+ end
+
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
def pages(limit: nil)
@@ -95,6 +101,10 @@ class ProjectWiki
end
end
+ def find_sidebar(version = nil)
+ find_page(SIDEBAR, version)
+ end
+
def find_file(name, version = nil)
wiki.file(name, version)
end
@@ -107,7 +117,7 @@ class ProjectWiki
update_project_activity
rescue Gitlab::Git::Wiki::DuplicatePageError => e
@error_message = "Duplicate page: #{e.message}"
- return false
+ false
end
def update_page(page, content:, title: nil, format: :markdown, message: nil)
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index c4b5dd2dc96..976b501e297 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -57,7 +57,7 @@ class RemoteMirror < ActiveRecord::Base
Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path)
timestamp = Time.now
- remote_mirror.update_attributes!(
+ remote_mirror.update!(
last_update_at: timestamp, last_successful_update_at: timestamp, last_error: nil
)
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 5f9894f1168..a96c73e6ab7 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -83,7 +83,7 @@ class Repository
@raw_repository&.cleanup
end
- # Return absolute path to repository
+ # Don't use this! It's going away. Use Gitaly to read or write from repos.
def path_to_repo
@path_to_repo ||=
begin
@@ -174,8 +174,8 @@ class Repository
CommitCollection.new(project, commits, ref)
end
- def find_branch(name, fresh_repo: true)
- raw_repository.find_branch(name, fresh_repo)
+ def find_branch(name)
+ raw_repository.find_branch(name)
end
def find_tag(name)
@@ -250,7 +250,7 @@ class Repository
# This will still fail if the file is corrupted (e.g. 0 bytes)
raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
rescue Gitlab::Git::CommandError => ex
- Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}"
+ Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
end
def kept_around?(sha)
@@ -462,12 +462,12 @@ class Repository
expire_branches_cache
end
- def method_missing(m, *args, &block)
- if m == :lookup && !block_given?
- lookup_cache[m] ||= {}
- lookup_cache[m][args.join(":")] ||= raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(msg, *args, &block)
+ if msg == :lookup && !block_given?
+ lookup_cache[msg] ||= {}
+ lookup_cache[msg][args.join(":")] ||= raw_repository.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
else
- raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ raw_repository.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
end
@@ -564,7 +564,7 @@ class Repository
end
def rendered_readme
- MarkupHelper.markup_unsafe(readme.name, readme.data, project: project) if readme
+ MarkupHelper.markup_unsafe(readme.name, readme.data, project: project, markdown_engine: :redcarpet) if readme
end
cache_method :rendered_readme
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 942cbb754e3..a2ab405fdbe 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -22,18 +22,15 @@ class Todo < ActiveRecord::Base
belongs_to :author, class_name: "User"
belongs_to :note
belongs_to :project
- belongs_to :group
belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
belongs_to :user
delegate :name, :email, to: :author, prefix: true, allow_nil: true
- validates :action, :target_type, :user, presence: true
+ validates :action, :project, :target_type, :user, presence: true
validates :author, presence: true
validates :target_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit?
- validates :project, presence: true, unless: :group_id
- validates :group, presence: true, unless: :project_id
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
@@ -47,7 +44,7 @@ class Todo < ActiveRecord::Base
state :done
end
- after_save :keep_around_commit, if: :commit_id
+ after_save :keep_around_commit
class << self
# Priority sorting isn't displayed in the dropdown, because we don't show
@@ -82,10 +79,6 @@ class Todo < ActiveRecord::Base
end
end
- def parent
- project
- end
-
def unmergeable?
action == UNMERGEABLE
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 27a5d0278b7..4987d01aac6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -99,7 +99,8 @@ class User < ActiveRecord::Base
has_many :group_members, -> { where(requested_at: nil) }, source: 'GroupMember'
has_many :groups, through: :group_members
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
- has_many :masters_groups, -> { where(members: { access_level: Gitlab::Access::MASTER }) }, through: :group_members, source: :group
+ has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
+ alias_attribute :masters_groups, :maintainers_groups
# Projects
has_many :groups_projects, through: :groups, source: :projects
@@ -496,7 +497,7 @@ class User < ActiveRecord::Base
def disable_two_factor!
transaction do
- update_attributes(
+ update(
otp_required_for_login: false,
encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil,
@@ -728,7 +729,7 @@ class User < ActiveRecord::Base
end
def several_namespaces?
- owned_groups.any? || masters_groups.any?
+ owned_groups.any? || maintainers_groups.any?
end
def namespace_id
@@ -974,15 +975,15 @@ class User < ActiveRecord::Base
end
def manageable_groups
- union_sql = Gitlab::SQL::Union.new([owned_groups.select(:id), masters_groups.select(:id)]).to_sql
+ union_sql = Gitlab::SQL::Union.new([owned_groups.select(:id), maintainers_groups.select(:id)]).to_sql
# Update this line to not use raw SQL when migrated to Rails 5.2.
# Either ActiveRecord or Arel constructions are fine.
# This was replaced with the raw SQL construction because of bugs in the arel gem.
# Bugs were fixed in arel 9.0.0 (Rails 5.2).
- owned_and_master_groups = Group.where("namespaces.id IN (#{union_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+ owned_and_maintainer_groups = Group.where("namespaces.id IN (#{union_sql})") # rubocop:disable GitlabSecurity/SqlInjection
- Gitlab::GroupHierarchy.new(owned_and_master_groups).base_and_descendants
+ Gitlab::GroupHierarchy.new(owned_and_maintainer_groups).base_and_descendants
end
def namespaces
@@ -1023,11 +1024,11 @@ class User < ActiveRecord::Base
def ci_owned_runners
@ci_owned_runners ||= begin
project_runner_ids = Ci::RunnerProject
- .where(project: authorized_projects(Gitlab::Access::MASTER))
+ .where(project: authorized_projects(Gitlab::Access::MAINTAINER))
.select(:runner_id)
group_runner_ids = Ci::RunnerNamespace
- .where(namespace_id: owned_or_masters_groups.select(:id))
+ .where(namespace_id: owned_or_maintainers_groups.select(:id))
.select(:runner_id)
union = Gitlab::SQL::Union.new([project_runner_ids, group_runner_ids])
@@ -1053,7 +1054,7 @@ class User < ActiveRecord::Base
return @global_notification_setting if defined?(@global_notification_setting)
@global_notification_setting = notification_settings.find_or_initialize_by(source: nil)
- @global_notification_setting.update_attributes(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
+ @global_notification_setting.update(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
@global_notification_setting
end
@@ -1236,11 +1237,14 @@ class User < ActiveRecord::Base
!terms_accepted?
end
- def owned_or_masters_groups
- union = Gitlab::SQL::Union.new([owned_groups, masters_groups])
+ def owned_or_maintainers_groups
+ union = Gitlab::SQL::Union.new([owned_groups, maintainers_groups])
Group.from("(#{union.to_sql}) namespaces")
end
+ # @deprecated
+ alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
+
protected
# override, from Devise::Validatable
@@ -1333,8 +1337,8 @@ class User < ActiveRecord::Base
end
end
- def self.unique_internal(scope, username, email_pattern, &b)
- scope.first || create_unique_internal(scope, username, email_pattern, &b)
+ def self.unique_internal(scope, username, email_pattern, &block)
+ scope.first || create_unique_internal(scope, username, email_pattern, &block)
end
def self.create_unique_internal(scope, username, email_pattern, &creation_block)
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index fc97ebe9f63..55243136140 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Rails/ActiveRecordAliases
class WikiPage
PageChangedError = Class.new(StandardError)
PageRenameError = Class.new(StandardError)
diff --git a/app/policies/clusters/cluster_policy.rb b/app/policies/clusters/cluster_policy.rb
index 1f7c13072b9..b5b24491655 100644
--- a/app/policies/clusters/cluster_policy.rb
+++ b/app/policies/clusters/cluster_policy.rb
@@ -4,7 +4,7 @@ module Clusters
delegate { cluster.project }
- rule { can?(:master_access) }.policy do
+ rule { can?(:maintainer_access) }.policy do
enable :update_cluster
enable :admin_cluster
end
diff --git a/app/policies/deploy_token_policy.rb b/app/policies/deploy_token_policy.rb
index 7aa9106e8b1..d1b459cfc90 100644
--- a/app/policies/deploy_token_policy.rb
+++ b/app/policies/deploy_token_policy.rb
@@ -1,10 +1,10 @@
class DeployTokenPolicy < BasePolicy
with_options scope: :subject, score: 0
- condition(:master) { @subject.project.team.master?(@user) }
+ condition(:maintainer) { @subject.project.team.maintainer?(@user) }
rule { anonymous }.prevent_all
- rule { master }.policy do
+ rule { maintainer }.policy do
enable :create_deploy_token
enable :update_deploy_token
end
diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb
index 375a5535359..2d07311db72 100644
--- a/app/policies/environment_policy.rb
+++ b/app/policies/environment_policy.rb
@@ -1,9 +1,14 @@
class EnvironmentPolicy < BasePolicy
delegate { @subject.project }
- condition(:stop_action_allowed) do
- @subject.stop_action? && can?(:update_build, @subject.stop_action)
+ condition(:stop_with_deployment_allowed) do
+ @subject.stop_action_available? &&
+ can?(:create_deployment) && can?(:update_build, @subject.stop_action)
end
- rule { can?(:create_deployment) & stop_action_allowed }.enable :stop_environment
+ condition(:stop_with_update_allowed) do
+ !@subject.stop_action_available? && can?(:update_environment, @subject)
+ end
+
+ rule { stop_with_deployment_allowed | stop_with_update_allowed }.enable :stop_environment
end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index ded9fe30eff..dc339b71ec7 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -11,7 +11,7 @@ class GroupPolicy < BasePolicy
condition(:guest) { access_level >= GroupMember::GUEST }
condition(:developer) { access_level >= GroupMember::DEVELOPER }
condition(:owner) { access_level >= GroupMember::OWNER }
- condition(:master) { access_level >= GroupMember::MASTER }
+ condition(:maintainer) { access_level >= GroupMember::MAINTAINER }
condition(:reporter) { access_level >= GroupMember::REPORTER }
condition(:nested_groups_supported, scope: :global) { Group.supports_nested_groups? }
@@ -59,7 +59,7 @@ class GroupPolicy < BasePolicy
enable :admin_issue
end
- rule { master }.policy do
+ rule { maintainer }.policy do
enable :create_projects
enable :admin_pipeline
enable :admin_build
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 199bcf92b21..bc49092633f 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -46,7 +46,7 @@ class ProjectPolicy < BasePolicy
condition(:developer) { team_access_level >= Gitlab::Access::DEVELOPER }
desc "User has maintainer access"
- condition(:master) { team_access_level >= Gitlab::Access::MASTER }
+ condition(:maintainer) { team_access_level >= Gitlab::Access::MAINTAINER }
desc "Project is public"
condition(:public_project, scope: :subject, score: 0) { project.public? }
@@ -123,14 +123,14 @@ class ProjectPolicy < BasePolicy
rule { guest }.enable :guest_access
rule { reporter }.enable :reporter_access
rule { developer }.enable :developer_access
- rule { master }.enable :master_access
+ rule { maintainer }.enable :maintainer_access
rule { owner | admin }.enable :owner_access
rule { can?(:owner_access) }.policy do
enable :guest_access
enable :reporter_access
enable :developer_access
- enable :master_access
+ enable :maintainer_access
enable :change_namespace
enable :change_visibility_level
@@ -228,7 +228,7 @@ class ProjectPolicy < BasePolicy
enable :create_deployment
end
- rule { can?(:master_access) }.policy do
+ rule { can?(:maintainer_access) }.policy do
enable :push_to_delete_protected_branch
enable :update_project_snippet
enable :update_environment
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index ba0ae6ba8a0..83558fc6659 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -7,7 +7,7 @@ class EnvironmentEntity < Grape::Entity
expose :external_url
expose :environment_type
expose :last_deployment, using: DeploymentEntity
- expose :stop_action?
+ expose :stop_action_available?, as: :has_stop_action
expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment)
@@ -31,4 +31,14 @@ class EnvironmentEntity < Grape::Entity
end
expose :created_at, :updated_at
+
+ expose :can_stop do |environment|
+ environment.available? && can?(current_user, :stop_environment, environment)
+ end
+
+ private
+
+ def current_user
+ request.current_user
+ end
end
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index 5d72ebdd7fd..a78bd77cf7c 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -10,9 +10,15 @@ class MergeRequestWidgetEntity < IssuableEntity
expose :merge_when_pipeline_succeeds
expose :source_branch
expose :source_project_id
+ expose :source_project_full_path do |merge_request|
+ merge_request.source_project&.full_path
+ end
expose :squash
expose :target_branch
expose :target_project_id
+ expose :target_project_full_path do |merge_request|
+ merge_request.project&.full_path
+ end
expose :allow_collaboration
expose :should_be_rebased?, as: :should_be_rebased
diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb
index ce0c31b5806..0e1f94a9f61 100644
--- a/app/serializers/note_entity.rb
+++ b/app/serializers/note_entity.rb
@@ -62,6 +62,8 @@ class NoteEntity < API::Entities::Note
expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? }
+ expose :cached_markdown_version
+
private
def current_user
diff --git a/app/services/access_token_validation_service.rb b/app/services/access_token_validation_service.rb
index 46e19230328..2a337918d21 100644
--- a/app/services/access_token_validation_service.rb
+++ b/app/services/access_token_validation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AccessTokenValidationService
# Results:
VALID = :valid
diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb
index 227e9ea9c6d..e7eb74d3e7d 100644
--- a/app/services/after_branch_delete_service.rb
+++ b/app/services/after_branch_delete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# Branch can be deleted either by DeleteBranchService
# or by GitPushService.
diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb
index 0521393dd27..82ae66ab0f5 100644
--- a/app/services/akismet_service.rb
+++ b/app/services/akismet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AkismetService
attr_accessor :owner, :text, :options
diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb
index 5ad9a50687c..4c5e22bdd7e 100644
--- a/app/services/audit_event_service.rb
+++ b/app/services/audit_event_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AuditEventService
def initialize(author, entity, details = {})
@author, @entity, @details = author, entity, details
diff --git a/app/services/badges/update_service.rb b/app/services/badges/update_service.rb
index 7ca84b5df31..495a4a2c99d 100644
--- a/app/services/badges/update_service.rb
+++ b/app/services/badges/update_service.rb
@@ -3,7 +3,7 @@ module Badges
# returns the updated badge
def execute(badge)
if params.present?
- badge.update_attributes(params)
+ badge.update(params)
end
badge
diff --git a/app/services/base_count_service.rb b/app/services/base_count_service.rb
index 975e288301c..ad1647842b8 100644
--- a/app/services/base_count_service.rb
+++ b/app/services/base_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for services that count a single resource such as the number of
# issues for a project.
class BaseCountService
diff --git a/app/services/base_renderer.rb b/app/services/base_renderer.rb
index d6e30bd7008..30a6e8c62dd 100644
--- a/app/services/base_renderer.rb
+++ b/app/services/base_renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseRenderer
attr_reader :current_user
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 3519b7c5e7d..3e968c8f707 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BaseService
include Gitlab::Allowable
diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb
index 43c9a065fcf..439746e82bd 100644
--- a/app/services/ci/stop_environments_service.rb
+++ b/app/services/ci/stop_environments_service.rb
@@ -8,7 +8,7 @@ module Ci
return unless @ref.present?
environments.each do |environment|
- next unless environment.stop_action?
+ next unless environment.stop_action_available?
next unless can?(current_user, :stop_environment, environment)
environment.stop_with_action!(current_user)
diff --git a/app/services/cohorts_service.rb b/app/services/cohorts_service.rb
index 6781533af28..7a14e97f749 100644
--- a/app/services/cohorts_service.rb
+++ b/app/services/cohorts_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CohortsService
MONTHS_INCLUDED = 12
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index b9d0173a2d0..1ce6ab36cbf 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -13,8 +13,6 @@ module Commits
# rubocop:disable GitlabSecurity/PublicSend
message = @commit.public_send(:"#{action}_message", current_user)
-
- # rubocop:disable GitlabSecurity/PublicSend
repository.public_send(
action,
current_user,
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index 2a69a205629..3adf8a0c1a1 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'securerandom'
# Compare 2 refs for one repo or between repositories
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index 9b1a4d960e2..65208b07e27 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateBranchService < BaseService
def execute(branch_name, ref)
create_master_branch if project.empty_repo?
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index 7e5a77fb056..bb3f605da28 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateDeploymentService
attr_reader :job
diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb
index 54ff1f74126..09c68390007 100644
--- a/app/services/create_release_service.rb
+++ b/app/services/create_release_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb
index 40286dbf3bf..6f1fce4989e 100644
--- a/app/services/create_snippet_service.rb
+++ b/app/services/create_snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateSnippetService < BaseService
include SpamCheckService
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index e1499dcee64..44252f7b0a6 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteBranchService < BaseService
def execute(branch_name)
repository = project.repository
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index c98d1e3c540..ff3e4783fe3 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index 44dc90b3462..e7464fd9d5f 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# EventCreateService class
#
# Used for creating events feed on dashboard after certain user action
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index f3bfc53dcd3..29c8ce5fea3 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitPushService < BaseService
attr_accessor :push_data, :push_commits
include Gitlab::Access
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 9917a39b795..3ff2d1d107d 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitTagPushService < BaseService
attr_accessor :push_data
diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb
index c6e52c3bb91..2a7a5dae291 100644
--- a/app/services/gravatar_service.rb
+++ b/app/services/gravatar_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GravatarService
def execute(email, size = nil, scale = 2, username: nil)
return unless Gitlab::CurrentSettings.gravatar_enabled?
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index 5c337a9faa5..c2dfbac5414 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -1,11 +1,12 @@
module Groups
class NestedCreateService < Groups::BaseService
- attr_reader :group_path
+ attr_reader :group_path, :visibility_level
def initialize(user, params)
@current_user, @params = user, params.dup
-
@group_path = @params.delete(:group_path)
+ @visibility_level = @params.delete(:visibility_level) ||
+ Gitlab::CurrentSettings.current_application_settings.default_group_visibility
end
def execute
@@ -36,11 +37,12 @@ module Groups
new_params = params.reverse_merge(
path: subgroup_name,
name: subgroup_name,
- parent: last_group
+ parent: last_group,
+ visibility_level: visibility_level
)
- new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility
- last_group = namespace_or_group(partial_path) || Groups::CreateService.new(current_user, new_params).execute
+ last_group = namespace_or_group(partial_path) ||
+ Groups::CreateService.new(current_user, new_params).execute
end
last_group
diff --git a/app/services/ham_service.rb b/app/services/ham_service.rb
index b0e1799b489..794eb34d9ca 100644
--- a/app/services/ham_service.rb
+++ b/app/services/ham_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HamService
attr_accessor :spam_log
diff --git a/app/services/import_export_clean_up_service.rb b/app/services/import_export_clean_up_service.rb
index 74088b970c9..e75a951944e 100644
--- a/app/services/import_export_clean_up_service.rb
+++ b/app/services/import_export_clean_up_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ImportExportCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 1440
@@ -10,7 +12,9 @@ class ImportExportCleanUpService
def execute
Gitlab::Metrics.measure(:import_export_clean_up) do
- next unless File.directory?(path)
+ clean_up_export_object_files
+
+ break unless File.directory?(path)
clean_up_export_files
end
@@ -21,4 +25,11 @@ class ImportExportCleanUpService
def clean_up_export_files
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end
+
+ def clean_up_export_object_files
+ ImportExportUpload.where('updated_at < ?', mmin.minutes.ago).each do |upload|
+ upload.remove_export_file!
+ upload.save!
+ end
+ end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 683f64e82ad..7d60c65bb79 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssuableBaseService < BaseService
private
@@ -130,7 +132,7 @@ class IssuableBaseService < BaseService
def create_issuable(issuable, attributes, label_ids:)
issuable.with_transaction_returning_status do
if issuable.save
- issuable.update_attributes(label_ids: label_ids)
+ issuable.update(label_ids: label_ids)
end
end
end
diff --git a/app/services/members/update_service.rb b/app/services/members/update_service.rb
index 48b3d59f7bd..cb19cf01dd7 100644
--- a/app/services/members/update_service.rb
+++ b/app/services/members/update_service.rb
@@ -6,7 +6,7 @@ module Members
old_access_level = member.human_access
- if member.update_attributes(params)
+ if member.update(params)
after_execute(action: permission, old_access_level: old_access_level, member: member)
end
diff --git a/app/services/merge_request_metrics_service.rb b/app/services/merge_request_metrics_service.rb
index 9248de14a53..4e88b77c855 100644
--- a/app/services/merge_request_metrics_service.rb
+++ b/app/services/merge_request_metrics_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MergeRequestMetricsService
delegate :update!, to: :@merge_request_metrics
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index 5b4bc86b9ba..c741e913860 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -26,7 +26,7 @@ module MergeRequests
Gitlab::GitLogger.info("#{log_prefix} rebased to #{rebase_sha}")
- merge_request.update_attributes(rebase_commit_sha: rebase_sha)
+ merge_request.update(rebase_commit_sha: rebase_sha)
Gitlab::GitLogger.info("#{log_prefix} rebase SHA saved: #{rebase_sha}")
diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb
index 51ff9eff5e4..222a5c8c79c 100644
--- a/app/services/metrics_service.rb
+++ b/app/services/metrics_service.rb
@@ -1,35 +1,18 @@
+# frozen_string_literal: true
+
require 'prometheus/client/formats/text'
class MetricsService
- CHECKS = [
- Gitlab::HealthChecks::DbCheck,
- Gitlab::HealthChecks::Redis::RedisCheck,
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::GitalyCheck
- ].freeze
-
def prometheus_metrics_text
Prometheus::Client::Formats::Text.marshal_multiprocess(multiprocess_metrics_path)
end
- def health_metrics_text
- metrics = CHECKS.flat_map(&:metrics)
-
- formatter.marshal(metrics)
- end
-
def metrics_text
- prometheus_metrics_text.concat(health_metrics_text)
+ prometheus_metrics_text
end
private
- def formatter
- @formatter ||= Gitlab::HealthChecks::PrometheusTextFormat.new
- end
-
def multiprocess_metrics_path
::Prometheus::Client.configuration.multiprocess_files_dir
end
diff --git a/app/services/milestones/update_service.rb b/app/services/milestones/update_service.rb
index 31b441ed476..74edbf9b41d 100644
--- a/app/services/milestones/update_service.rb
+++ b/app/services/milestones/update_service.rb
@@ -11,7 +11,7 @@ module Milestones
end
if params.present?
- milestone.update_attributes(params.except(:state_event))
+ milestone.update(params.except(:state_event))
end
milestone
diff --git a/app/services/note_summary.rb b/app/services/note_summary.rb
index a6f6320d573..81f6f92f75c 100644
--- a/app/services/note_summary.rb
+++ b/app/services/note_summary.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NoteSummary
attr_reader :note
attr_reader :metadata
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 75fd08ea0a9..e16ef398184 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -5,7 +5,7 @@ module Notes
old_mentioned_users = note.mentioned_users.to_a
- note.update_attributes(params.merge(updated_by: current_user))
+ note.update(params.merge(updated_by: current_user))
note.create_new_cross_references!(current_user)
if note.previous_changes.include?('note')
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 4fa38665abc..4389fd89538 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Used by NotificationService to determine who should receive notification
#
@@ -10,16 +12,16 @@ module NotificationRecipientService
NotificationRecipient.new(user, *args).notifiable?
end
- def self.build_recipients(*a)
- Builder::Default.new(*a).notification_recipients
+ def self.build_recipients(*args)
+ Builder::Default.new(*args).notification_recipients
end
- def self.build_new_note_recipients(*a)
- Builder::NewNote.new(*a).notification_recipients
+ def self.build_new_note_recipients(*args)
+ Builder::NewNote.new(*args).notification_recipients
end
- def self.build_merge_request_unmergeable_recipients(*a)
- Builder::MergeRequestUnmergeable.new(*a).notification_recipients
+ def self.build_merge_request_unmergeable_recipients(*args)
+ Builder::MergeRequestUnmergeable.new(*args).notification_recipients
end
module Builder
@@ -44,7 +46,6 @@ module NotificationRecipientService
raise 'abstract'
end
- # rubocop:disable Rails/Delegate
def project
target.project
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 636cfbf5b45..4511c500fca 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable GitlabSecurity/PublicSend
# NotificationService class
@@ -135,6 +137,8 @@ class NotificationService
# * watchers of the mr's labels
# * users with custom level checked with "new merge request"
#
+ # In EE, approvers of the merge request are also included
+ #
def new_merge_request(merge_request, current_user)
new_resource_email(merge_request, :new_merge_request_email)
end
@@ -256,6 +260,10 @@ class NotificationService
# ignore gitlab service messages
return true if note.cross_reference? && note.system?
+ send_new_note_notifications(note)
+ end
+
+ def send_new_note_notifications(note)
notify_method = "note_#{note.to_ability_name}_email".to_sym
recipients = NotificationRecipientService.build_new_note_recipients(note)
@@ -268,9 +276,9 @@ class NotificationService
def new_access_request(member)
return true unless member.notifiable?(:subscription)
- recipients = member.source.members.active_without_invites_and_requests.owners_and_masters
- if fallback_to_group_owners_masters?(recipients, member)
- recipients = member.source.group.members.active_without_invites_and_requests.owners_and_masters
+ recipients = member.source.members.active_without_invites_and_requests.owners_and_maintainers
+ if fallback_to_group_owners_maintainers?(recipients, member)
+ recipients = member.source.group.members.active_without_invites_and_requests.owners_and_maintainers
end
recipients.each { |recipient| deliver_access_request_email(recipient, member) }
@@ -513,7 +521,7 @@ class NotificationService
return [] unless project
- notifiable_users(project.team.masters, :watch, target: project)
+ notifiable_users(project.team.maintainers, :watch, target: project)
end
def notifiable?(*args)
@@ -528,7 +536,7 @@ class NotificationService
mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
end
- def fallback_to_group_owners_masters?(recipients, member)
+ def fallback_to_group_owners_maintainers?(recipients, member)
return false if recipients.present?
member.source.respond_to?(:group) && member.source.group
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 4ee2c1796bd..a15ee4911ef 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PreviewMarkdownService < BaseService
def execute
text, commands = explain_quick_actions(params[:text])
@@ -6,7 +8,8 @@ class PreviewMarkdownService < BaseService
success(
text: text,
users: users,
- commands: commands.join(' ')
+ commands: commands.join(' '),
+ markdown_engine: markdown_engine
)
end
@@ -42,4 +45,8 @@ class PreviewMarkdownService < BaseService
def commands_target_id
params[:quick_actions_target_id]
end
+
+ def markdown_engine
+ CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
+ end
end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index aa60661f7f2..9d0eaaf3152 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -20,24 +20,28 @@ module Projects
MergeRequestsFinder.new(current_user, project_id: project.id, state: 'opened').execute.select([:iid, :title])
end
- def labels(target = nil)
- labels = LabelsFinder.new(current_user, project_id: project.id, include_ancestor_groups: true)
- .execute.select([:color, :title])
-
- return labels unless target&.respond_to?(:labels)
-
- issuable_label_titles = target.labels.pluck(:title)
-
- if issuable_label_titles
- labels = labels.as_json(only: [:title, :color])
-
- issuable_label_titles.each do |issuable_label_title|
- found_label = labels.find { |label| label['title'] == issuable_label_title }
- found_label[:set] = true if found_label
+ def labels_as_hash(target = nil)
+ available_labels = LabelsFinder.new(
+ current_user,
+ project_id: project.id,
+ include_ancestor_groups: true
+ ).execute
+
+ label_hashes = available_labels.as_json(only: [:title, :color])
+
+ if target&.respond_to?(:labels)
+ already_set_labels = available_labels & target.labels
+ if already_set_labels.present?
+ titles = already_set_labels.map(&:title)
+ label_hashes.each do |hash|
+ if titles.include?(hash['title'])
+ hash[:set] = true
+ end
+ end
end
end
- labels
+ label_hashes
end
def commands(noteable, type)
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 172497b8e67..85491089d8e 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -115,7 +115,7 @@ module Projects
@project.group.refresh_members_authorized_projects(blocking: false)
current_user.refresh_authorized_projects
else
- @project.add_master(@project.namespace.owner, current_user: current_user)
+ @project.add_maintainer(@project.namespace.owner, current_user: current_user)
end
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 02769e72229..87173cc79ec 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -124,7 +124,7 @@ module Projects
# It's possible that the project was destroyed, but some after_commit
# hook failed and caused us to end up here. A destroyed model will be a frozen hash,
# which cannot be altered.
- project.update_attributes(delete_error: message, pending_delete: false) unless project.destroyed?
+ project.update(delete_error: message, pending_delete: false) unless project.destroyed?
log_error("Deletion failed on #{project.full_path} with the following message: #{message}")
end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 348eb0bf8d8..a8aafa9fb4f 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -37,7 +37,7 @@ module Projects
return new_project unless new_project.persisted?
builds_access_level = @project.project_feature.builds_access_level
- new_project.project_feature.update_attributes(builds_access_level: builds_access_level)
+ new_project.project_feature.update(builds_access_level: builds_access_level)
link_fork_network(new_project)
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index 6ea43561d61..618c30b971f 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -22,7 +22,7 @@ module Projects
private
def download_and_save_file(file, sanitized_uri)
- IO.copy_stream(open(sanitized_uri.sanitized_url, headers(sanitized_uri)), file)
+ IO.copy_stream(open(sanitized_uri.sanitized_url, headers(sanitized_uri)), file) # rubocop:disable Security/Open
end
def headers(sanitized_uri)
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index d8250cd8102..f4fbaacc08b 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -22,7 +22,7 @@ module Projects
# If the block added errors, don't try to save the project
return validation_failed! if project.errors.any?
- if project.update_attributes(params.except(:default_branch))
+ if project.update(params.except(:default_branch))
if project.previous_changes.include?('path')
project.rename_repo
else
diff --git a/app/services/protected_branches/access_level_params.rb b/app/services/protected_branches/access_level_params.rb
index 253ae8b0124..4658b0e850d 100644
--- a/app/services/protected_branches/access_level_params.rb
+++ b/app/services/protected_branches/access_level_params.rb
@@ -14,7 +14,7 @@ module ProtectedBranches
private
def params_with_default(params)
- params[:"#{type}_access_level"] ||= Gitlab::Access::MASTER if use_default_access_level?(params)
+ params[:"#{type}_access_level"] ||= Gitlab::Access::MAINTAINER if use_default_access_level?(params)
params
end
diff --git a/app/services/protected_branches/legacy_api_create_service.rb b/app/services/protected_branches/legacy_api_create_service.rb
index e358fd0374e..bb7656489c5 100644
--- a/app/services/protected_branches/legacy_api_create_service.rb
+++ b/app/services/protected_branches/legacy_api_create_service.rb
@@ -9,14 +9,14 @@ module ProtectedBranches
if params.delete(:developers_can_push)
Gitlab::Access::DEVELOPER
else
- Gitlab::Access::MASTER
+ Gitlab::Access::MAINTAINER
end
merge_access_level =
if params.delete(:developers_can_merge)
Gitlab::Access::DEVELOPER
else
- Gitlab::Access::MASTER
+ Gitlab::Access::MAINTAINER
end
@params.merge!(push_access_levels_attributes: [{ access_level: push_access_level }],
diff --git a/app/services/protected_branches/legacy_api_update_service.rb b/app/services/protected_branches/legacy_api_update_service.rb
index 33176253ca2..1df38de0e4a 100644
--- a/app/services/protected_branches/legacy_api_update_service.rb
+++ b/app/services/protected_branches/legacy_api_update_service.rb
@@ -17,14 +17,14 @@ module ProtectedBranches
when true
params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::DEVELOPER }]
when false
- params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::MASTER }]
+ params[:push_access_levels_attributes] = [{ access_level: Gitlab::Access::MAINTAINER }]
end
case @developers_can_merge
when true
params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::DEVELOPER }]
when false
- params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::MASTER }]
+ params[:merge_access_levels_attributes] = [{ access_level: Gitlab::Access::MAINTAINER }]
end
service = ProtectedBranches::UpdateService.new(@project, @current_user, @params)
diff --git a/app/services/push_event_payload_service.rb b/app/services/push_event_payload_service.rb
index b0a389c85f9..bb1259787af 100644
--- a/app/services/push_event_payload_service.rb
+++ b/app/services/push_event_payload_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for creating push event payloads as stored in the
# "push_event_payloads" table.
#
diff --git a/app/services/repair_ldap_blocked_user_service.rb b/app/services/repair_ldap_blocked_user_service.rb
index 863cef7ff61..6ed42054ac3 100644
--- a/app/services/repair_ldap_blocked_user_service.rb
+++ b/app/services/repair_ldap_blocked_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepairLdapBlockedUserService
attr_accessor :user
diff --git a/app/services/repository_archive_clean_up_service.rb b/app/services/repository_archive_clean_up_service.rb
index ba7be4b3f89..99a9c834352 100644
--- a/app/services/repository_archive_clean_up_service.rb
+++ b/app/services/repository_archive_clean_up_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryArchiveCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 120
diff --git a/app/services/reset_project_cache_service.rb b/app/services/reset_project_cache_service.rb
index a162a6eedb9..676d367a1c1 100644
--- a/app/services/reset_project_cache_service.rb
+++ b/app/services/reset_project_cache_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ResetProjectCacheService < BaseService
def execute
@project.increment!(:jobs_cache_index)
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 1d4d03a8b7d..1b707d79b43 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SearchService
include Gitlab::Allowable
diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb
index d4ade869777..895261925ba 100644
--- a/app/services/spam_check_service.rb
+++ b/app/services/spam_check_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SpamCheckService
#
# Provide helper methods for checking if a given spammable object has
diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb
index 73ea3018fbd..f2f133dae28 100644
--- a/app/services/spam_service.rb
+++ b/app/services/spam_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SpamService
attr_accessor :spammable, :request, :options
attr_reader :spam_log
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index ac029fad7ea..93c2e222963 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SubmitUsagePingService
URL = 'https://version.gitlab.com/usage_data'.freeze
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index ba7946fd23c..bd3907cdf8e 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SystemHooksService
def execute_hooks_for(model, event)
data = build_event_data(model, event)
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 00bf5434b7f..77494295f14 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# SystemNoteService
#
# Used for creating system notes (e.g., when a user references a merge request
@@ -21,9 +23,11 @@ module SystemNoteService
total_count = new_commits.length + existing_commits.length
commits_text = "#{total_count} commit".pluralize(total_count)
- body = "added #{commits_text}\n\n"
- body << commits_list(noteable, new_commits, existing_commits, oldrev)
- body << "\n\n[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
+ text_parts = ["added #{commits_text}"]
+ text_parts << commits_list(noteable, new_commits, existing_commits, oldrev)
+ text_parts << "[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
+
+ body = text_parts.join("\n\n")
create_note(NoteSummary.new(noteable, project, author, body, action: 'commit', commit_count: total_count))
end
@@ -103,18 +107,19 @@ module SystemNoteService
added_labels = added_labels.map(&references).join(' ')
removed_labels = removed_labels.map(&references).join(' ')
- body = ''
+ text_parts = []
if added_labels.present?
- body << "added #{added_labels}"
- body << ' and ' if removed_labels.present?
+ text_parts << "added #{added_labels}"
+ text_parts << 'and' if removed_labels.present?
end
if removed_labels.present?
- body << "removed #{removed_labels}"
+ text_parts << "removed #{removed_labels}"
end
- body << ' ' << 'label'.pluralize(labels_count)
+ text_parts << 'label'.pluralize(labels_count)
+ body = text_parts.join(' ')
create_note(NoteSummary.new(noteable, project, author, body, action: 'label'))
end
@@ -188,8 +193,10 @@ module SystemNoteService
spent_at = noteable.spent_at
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
- body = "#{action} #{parsed_time} of time spent"
- body << " at #{spent_at}" if spent_at
+
+ text_parts = ["#{action} #{parsed_time} of time spent"]
+ text_parts << "at #{spent_at}" if spent_at
+ body = text_parts.join(' ')
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
@@ -268,17 +275,19 @@ module SystemNoteService
diff_refs = change_position.diff_refs
version_index = merge_request.merge_request_diffs.viewable.count
- body = "changed this line in"
+ text_parts = ["changed this line in"]
if version_params = merge_request.version_params_for(diff_refs)
line_code = change_position.line_code(project.repository)
url = url_helpers.diffs_project_merge_request_url(project, merge_request, version_params.merge(anchor: line_code))
- body << " [version #{version_index} of the diff](#{url})"
+ text_parts << "[version #{version_index} of the diff](#{url})"
else
- body << " version #{version_index} of the diff"
+ text_parts << "version #{version_index} of the diff"
end
+ body = text_parts.join(' ')
note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
+
note = Note.create(note_attributes.merge(system: true))
note.system_note_metadata = SystemNoteMetadata.new(action: 'outdated')
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 46f12086555..0bcd53c76a9 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# TodoService class
#
# Used for creating/updating todos after certain user actions
@@ -260,15 +262,15 @@ class TodoService
end
end
- def create_mention_todos(parent, target, author, note = nil, skip_users = [])
+ def create_mention_todos(project, target, author, note = nil, skip_users = [])
# Create Todos for directly addressed users
- directly_addressed_users = filter_directly_addressed_users(parent, note || target, author, skip_users)
- attributes = attributes_for_todo(parent, target, author, Todo::DIRECTLY_ADDRESSED, note)
+ directly_addressed_users = filter_directly_addressed_users(project, note || target, author, skip_users)
+ attributes = attributes_for_todo(project, target, author, Todo::DIRECTLY_ADDRESSED, note)
create_todos(directly_addressed_users, attributes)
# Create Todos for mentioned users
- mentioned_users = filter_mentioned_users(parent, note || target, author, skip_users)
- attributes = attributes_for_todo(parent, target, author, Todo::MENTIONED, note)
+ mentioned_users = filter_mentioned_users(project, note || target, author, skip_users)
+ attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note)
create_todos(mentioned_users, attributes)
end
@@ -299,36 +301,36 @@ class TodoService
def attributes_for_todo(project, target, author, action, note = nil)
attributes_for_target(target).merge!(
- project_id: project&.id,
+ project_id: project.id,
author_id: author.id,
action: action,
note: note
)
end
- def filter_todo_users(users, parent, target)
- reject_users_without_access(users, parent, target).uniq
+ def filter_todo_users(users, project, target)
+ reject_users_without_access(users, project, target).uniq
end
- def filter_mentioned_users(parent, target, author, skip_users = [])
+ def filter_mentioned_users(project, target, author, skip_users = [])
mentioned_users = target.mentioned_users(author) - skip_users
- filter_todo_users(mentioned_users, parent, target)
+ filter_todo_users(mentioned_users, project, target)
end
- def filter_directly_addressed_users(parent, target, author, skip_users = [])
+ def filter_directly_addressed_users(project, target, author, skip_users = [])
directly_addressed_users = target.directly_addressed_users(author) - skip_users
- filter_todo_users(directly_addressed_users, parent, target)
+ filter_todo_users(directly_addressed_users, project, target)
end
- def reject_users_without_access(users, parent, target)
- if target.is_a?(Note) && target.for_issuable?
+ def reject_users_without_access(users, project, target)
+ if target.is_a?(Note) && (target.for_issue? || target.for_merge_request?)
target = target.noteable
end
if target.is_a?(Issuable)
select_users(users, :"read_#{target.to_ability_name}", target)
else
- select_users(users, :read_project, parent)
+ select_users(users, :read_project, project)
end
end
diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb
index b7c36651968..422ba668e35 100644
--- a/app/services/update_release_service.rb
+++ b/app/services/update_release_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateReleaseService < BaseService
def execute(tag_name, release_description)
repository = project.repository
@@ -7,7 +9,7 @@ class UpdateReleaseService < BaseService
release = project.releases.find_by(tag: tag_name)
if release
- release.update_attributes(description: release_description)
+ release.update(description: release_description)
success(release)
else
diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb
index 358bca73aec..15bc1046a4e 100644
--- a/app/services/update_snippet_service.rb
+++ b/app/services/update_snippet_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UpdateSnippetService < BaseService
include SpamCheckService
diff --git a/app/services/upload_service.rb b/app/services/upload_service.rb
index d5a9b344905..39909ee4f82 100644
--- a/app/services/upload_service.rb
+++ b/app/services/upload_service.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
class UploadService
- def initialize(model, file, uploader_class = FileUploader)
- @model, @file, @uploader_class = model, file, uploader_class
+ def initialize(model, file, uploader_class = FileUploader, **uploader_context)
+ @model, @file, @uploader_class, @uploader_context = model, file, uploader_class, uploader_context
end
def execute
return nil unless @file && @file.size <= max_attachment_size
- uploader = @uploader_class.new(@model)
+ uploader = @uploader_class.new(@model, nil, @uploader_context)
uploader.store!(@file)
uploader.to_h
diff --git a/app/services/user_agent_detail_service.rb b/app/services/user_agent_detail_service.rb
index a1ee3df5fe1..5cb42e879a0 100644
--- a/app/services/user_agent_detail_service.rb
+++ b/app/services/user_agent_detail_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserAgentDetailService
attr_accessor :spammable, :request
diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb
index 8630e572624..adca43660e8 100644
--- a/app/services/user_project_access_changed_service.rb
+++ b/app/services/user_project_access_changed_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UserProjectAccessChangedService
def initialize(user_ids)
@user_ids = Array.wrap(user_ids)
diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb
index 643f2ce1481..c19e2ec2043 100644
--- a/app/services/validate_new_branch_service.rb
+++ b/app/services/validate_new_branch_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'base_service'
class ValidateNewBranchService < BaseService
diff --git a/app/services/verify_pages_domain_service.rb b/app/services/verify_pages_domain_service.rb
index 13cb53dee01..07f7391f877 100644
--- a/app/services/verify_pages_domain_service.rb
+++ b/app/services/verify_pages_domain_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'resolv'
class VerifyPagesDomainService < BaseService
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 8a86e47f0ea..34724e0250d 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WebHookService
class InternalErrorResponse
attr_reader :body, :headers, :code
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 21292ddcf44..83f7b99d2a5 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -15,7 +15,7 @@ class FileUploader < GitlabUploader
prepend ObjectStorage::Extension::RecordsUploads
MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}
- DYNAMIC_PATH_PATTERN = %r{(?<secret>\h{32})/(?<identifier>.*)}
+ DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\h{32})/(?<identifier>.*)}
after :remove, :prune_store_dir
@@ -67,6 +67,10 @@ class FileUploader < GitlabUploader
SecureRandom.hex
end
+ def self.extract_dynamic_path(path)
+ DYNAMIC_PATH_PATTERN.match(path)
+ end
+
def upload_paths(identifier)
[
File.join(secret, identifier),
@@ -143,7 +147,7 @@ class FileUploader < GitlabUploader
return if apply_context!(value.uploader_context)
# fallback to the regex based extraction
- if matches = DYNAMIC_PATH_PATTERN.match(value.path)
+ if matches = self.class.extract_dynamic_path(value.path)
@secret = matches[:secret]
@identifier = matches[:identifier]
end
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index 7919f126075..719bd6ef418 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -71,6 +71,28 @@ class GitlabUploader < CarrierWave::Uploader::Base
File.join('/', self.class.base_dir, dynamic_segment, filename)
end
+ def cached_size
+ size
+ end
+
+ def open
+ stream =
+ if file_storage?
+ File.open(path, "rb") if path
+ else
+ ::Gitlab::HttpIO.new(url, cached_size) if url
+ end
+
+ return unless stream
+ return stream unless block_given?
+
+ begin
+ yield(stream)
+ ensure
+ stream.close
+ end
+ end
+
private
# Designed to be overridden by child uploaders that have a dynamic path
diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb
new file mode 100644
index 00000000000..213ac5c8011
--- /dev/null
+++ b/app/uploaders/import_export_uploader.rb
@@ -0,0 +1,15 @@
+class ImportExportUploader < AttachmentUploader
+ EXTENSION_WHITELIST = %w[tar.gz].freeze
+
+ def extension_whitelist
+ EXTENSION_WHITELIST
+ end
+
+ def move_to_store
+ true
+ end
+
+ def move_to_cache
+ false
+ end
+end
diff --git a/app/uploaders/job_artifact_uploader.rb b/app/uploaders/job_artifact_uploader.rb
index 855cf2fc21c..f6af023e0f9 100644
--- a/app/uploaders/job_artifact_uploader.rb
+++ b/app/uploaders/job_artifact_uploader.rb
@@ -18,14 +18,6 @@ class JobArtifactUploader < GitlabUploader
dynamic_segment
end
- def open
- if file_storage?
- File.open(path, "rb") if path
- else
- ::Gitlab::Ci::Trace::HttpIO.new(url, cached_size) if url
- end
- end
-
private
def dynamic_segment
diff --git a/app/views/admin/application_settings/_third_party_offers.html.haml b/app/views/admin/application_settings/_third_party_offers.html.haml
new file mode 100644
index 00000000000..c5d775d4bf5
--- /dev/null
+++ b/app/views/admin/application_settings/_third_party_offers.html.haml
@@ -0,0 +1,13 @@
+- application_setting = local_assigns.fetch(:application_setting)
+
+= form_for application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
+ = form_errors(application_setting)
+
+ %fieldset
+ .form-group
+ .form-check
+ = f.check_box :hide_third_party_offers, class: 'form-check-input'
+ = f.label :hide_third_party_offers, class: 'form-check-label' do
+ Do not display offers from third parties within GitLab
+
+ = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index bd43504dd37..5cb8001a364 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -325,5 +325,16 @@
.settings-content
= render partial: 'repository_mirrors_form'
+%section.settings.as-third-party-offers.no-animate#js-third-party-offers-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Third party offers')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Control the display of third party offers.')
+ .settings-content
+ = render 'third_party_offers', application_setting: @application_setting
+
= render_if_exists 'admin/application_settings/pseudonymizer_settings', expanded: expanded
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index c8008771236..a3773e90cfb 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -6,7 +6,7 @@
= render_if_exists 'admin/namespace_plan', f: f
.form-group.row.group-description-holder
- = f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2'
+ = f.label :avatar, _("Group avatar"), class: 'col-form-label col-sm-2'
.col-sm-10
= render 'shared/choose_group_avatar_button', f: f
@@ -26,12 +26,12 @@
.alert.alert-info
= render 'shared/group_tips'
.form-actions
- = f.submit 'Create group', class: "btn btn-create"
- = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
+ = f.submit _('Create group'), class: "btn btn-create"
+ = link_to _('Cancel'), admin_groups_path, class: "btn btn-cancel"
- else
.form-actions
- = f.submit 'Save changes', class: "btn btn-save"
- = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
+ = f.submit _('Save changes'), class: "btn btn-save"
+ = link_to _('Cancel'), admin_group_path(@group), class: "btn btn-cancel"
= render_if_exists 'ldap_group_links/ldap_syncrhonizations', group: @group
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index 3f96988c203..0a688b90f3a 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -3,8 +3,8 @@
%li.group-row{ class: css_class }
.controls
- = link_to 'Edit', admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
- = link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
+ = link_to _('Edit'), admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
+ = link_to _('Delete'), [:admin, group], data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name } }, method: :delete, class: 'btn btn-remove'
.stats
%span.badge.badge-pill
= storage_counter(group.storage_size)
diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml
index c2b9807015d..8e9e1a58a17 100644
--- a/app/views/admin/groups/edit.html.haml
+++ b/app/views/admin/groups/edit.html.haml
@@ -1,4 +1,4 @@
-- page_title "Edit", @group.name, "Groups"
-%h3.page-title Edit group: #{@group.name}
+- page_title _("Edit"), @group.name, _("Groups")
+%h3.page-title= _('Edit group: %{group_name}') % { group_name: @group.name }
%hr
= render 'form', visibility_level: @group.visibility_level
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 25946ba6eaf..6a9b85b4109 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,5 +1,5 @@
- @no_container = true
-- page_title "Groups"
+- page_title _("Groups")
%div{ class: container_class }
.top-area
@@ -13,7 +13,7 @@
= icon("search", class: "search-icon")
= render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash
= link_to new_admin_group_path, class: "btn btn-new" do
- New group
+ = _('New group')
%ul.content-list
= render @groups
diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml
index 8f9fe96249f..553e8638e52 100644
--- a/app/views/admin/groups/new.html.haml
+++ b/app/views/admin/groups/new.html.haml
@@ -1,4 +1,4 @@
-- page_title "New Group"
-%h3.page-title New group
+- page_title _("New Group")
+%h3.page-title= _('New group')
%hr
= render 'form', visibility_level: default_group_visibility
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index a40f98ad24f..72b068ea6b5 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -1,61 +1,58 @@
-- add_to_breadcrumbs "Groups", admin_groups_path
+- add_to_breadcrumbs _("Groups"), admin_groups_path
- breadcrumb_title @group.name
-- page_title @group.name, "Groups"
+- page_title @group.name, _("Groups")
%h3.page-title
- Group: #{@group.full_name}
+ = _('Group: %{group_name}') % { group_name: @group.full_name }
= link_to admin_group_edit_path(@group), class: "btn float-right" do
%i.fa.fa-pencil-square-o
- Edit
+ = _('Edit')
%hr
.row
.col-md-6
.card
.card-header
- Group info:
+ = _('Group info:')
%ul.content-list
%li
.avatar-container.s60
= group_icon(@group, class: "avatar s60")
%li
- %span.light Name:
+ %span.light= _('Name:')
%strong= @group.name
%li
- %span.light Path:
+ %span.light= _('Path:')
%strong
= @group.path
%li
- %span.light Description:
+ %span.light= _('Description:')
%strong
= @group.description
%li
- %span.light Visibility level:
+ %span.light= _('Visibility level:')
%strong
= visibility_level_label(@group.visibility_level)
%li
- %span.light Created on:
+ %span.light= _('Created on:')
%strong
= @group.created_at.to_s(:medium)
= render_if_exists 'admin/namespace_plan_info', namespace: @group
%li
- %span.light Storage:
- %strong= storage_counter(@group.storage_size)
- (
- = storage_counter(@group.repository_size)
- repositories,
- = storage_counter(@group.build_artifacts_size)
- build artifacts,
- = storage_counter(@group.lfs_objects_size)
- LFS
- )
+ %span.light= _('Storage:')
+ - counter_storage = storage_counter(@group.storage_size)
+ - counter_repositories = storage_counter(@group.repository_size)
+ - counter_build_artifacts = storage_counter(@group.build_artifacts_size)
+ - counter_lfs_objects = storage_counter(@group.lfs_objects_size)
+ %strong
+ = _("%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)") % { counter_storage: counter_storage, counter_repositories: counter_repositories, counter_build_artifacts: counter_build_artifacts, counter_lfs_objects: counter_lfs_objects }
%li
- %span.light Group Git LFS status:
+ %span.light= _('Group Git LFS status:')
%strong
= group_lfs_status(@group)
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
@@ -67,7 +64,7 @@
.card
.card-header
%h3.card-title
- Projects
+ = _('Projects')
%span.badge.badge-pill
#{@group.projects.count}
%ul.content-list
@@ -85,7 +82,7 @@
- if @group.shared_projects.any?
.card
.card-header
- Projects shared with #{@group.name}
+ = _('Projects shared with %{group_name}') % { group_name: @group.name }
%span.badge.badge-pill
#{@group.shared_projects.count}
%ul.content-list
@@ -102,11 +99,11 @@
- if can?(current_user, :admin_group_member, @group)
.card
.card-header
- Add user(s) to the group:
+ = _('Add user(s) to the group:')
.card-body.form-holder
%p.light
- Read more about project permissions
- %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
+ - link_to_help = link_to(_("here"), help_page_path("user/permissions"), class: "vlink")
+ = _('Read more about project permissions <strong>%{link_to_help}</strong>').html_safe % { link_to_help: link_to_help }
= form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div
@@ -114,16 +111,15 @@
.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
- = button_tag 'Add users to group', class: "btn btn-create"
+ = button_tag _('Add users to group'), class: "btn btn-create"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters, force_mobile_view: true
.card
.card-header
- %strong= @group.name
- group members
+ = _("<strong>%{group_name}</strong> group members").html_safe % { group_name: @group.name }
%span.badge.badge-pill= @group.members.size
.float-right
- = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-sm"
+ = link_to icon('pencil-square-o', text: _('Manage access')), polymorphic_url([@group, :members]), class: "btn btn-sm"
%ul.content-list.group-users-list.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false }
.card-footer
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 37fb8fbab26..3ae9ce6c11f 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -5,7 +5,7 @@
%ol
%li
= _("Install a Runner compatible with GitLab CI")
- = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
+ = (_("(check out the %{link} for information on how to install it).") % { link: link }).html_safe
%li
= _("Specify the following URL during the Runner setup:")
%code#coordinator_address= root_url(only_path: false)
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index a676eba2aee..9c246e19faa 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,8 +1,8 @@
.nav-block.activities
+ = render 'shared/event_filter'
.controls
= link_to dashboard_projects_path(rss_url_options), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
%i.fa.fa-rss
- = render 'shared/event_filter'
.content_list
= spinner
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 8b3974d97f8..d5a9cc646a6 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -30,33 +30,27 @@
.todos-filters
.row-content-block.second-block
- = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form d-sm-flex' do
- .filter-categories.flex-fill
- .filter-item.inline
- - if params[:group_id].present?
- = hidden_field_tag(:group_id, params[:group_id])
- = dropdown_tag(group_dropdown_label(params[:group_id], 'Group'), options: { toggle_class: 'js-group-search js-filter-submit', title: 'Filter by group', filter: true, filterInput: 'input#group-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-group js-filter-submit',
- placeholder: 'Search groups', data: { data: todo_group_options, default_label: 'Group', display: 'static' } })
- .filter-item.inline
- - if params[:project_id].present?
- = hidden_field_tag(:project_id, params[:project_id])
- = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
- placeholder: 'Search projects', data: { data: todo_projects_options, default_label: 'Project', display: 'static' } })
- .filter-item.inline
- - if params[:author_id].present?
- = hidden_field_tag(:author_id, params[:author_id])
- = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
- placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } })
- .filter-item.inline
- - if params[:type].present?
- = hidden_field_tag(:type, params[:type])
- = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
- data: { data: todo_types_options, default_label: 'Type' } })
- .filter-item.inline.actions-filter
- - if params[:action_id].present?
- = hidden_field_tag(:action_id, params[:action_id])
- = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
- data: { data: todo_actions_options, default_label: 'Action' } })
+ = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do
+ .filter-item.inline
+ - if params[:project_id].present?
+ = hidden_field_tag(:project_id, params[:project_id])
+ = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
+ placeholder: 'Search projects', data: { data: todo_projects_options, default_label: 'Project', display: 'static' } })
+ .filter-item.inline
+ - if params[:author_id].present?
+ = hidden_field_tag(:author_id, params[:author_id])
+ = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
+ placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author', todo_filter: true, todo_state_filter: params[:state] || 'pending' } })
+ .filter-item.inline
+ - if params[:type].present?
+ = hidden_field_tag(:type, params[:type])
+ = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
+ data: { data: todo_types_options, default_label: 'Type' } })
+ .filter-item.inline.actions-filter
+ - if params[:action_id].present?
+ = hidden_field_tag(:action_id, params[:action_id])
+ = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
+ data: { data: todo_actions_options, default_label: 'Action' } })
.filter-item.sort-filter
.dropdown
%button.dropdown-menu-toggle.dropdown-menu-toggle-sort{ type: 'button', 'data-toggle' => 'dropdown' }
diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml
index 84b4ce5b606..ac5cac50699 100644
--- a/app/views/doorkeeper/applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/applications/_delete_form.html.haml
@@ -2,9 +2,9 @@
= form_tag oauth_application_path(application) do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
- if defined? small
- = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: "Are you sure?" } do
+ = button_tag type: "submit", class: "btn btn-transparent", data: { confirm: _("Are you sure?") } do
%span.sr-only
- Destroy
+ = _('Destroy')
= icon('trash')
- else
- = submit_tag 'Destroy', data: { confirm: "Are you sure?" }, class: submit_btn_css
+ = submit_tag _('Destroy'), data: { confirm: _("Are you sure?") }, class: submit_btn_css
diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml
index be0935b8313..1ddd0df54cd 100644
--- a/app/views/doorkeeper/applications/_form.html.haml
+++ b/app/views/doorkeeper/applications/_form.html.haml
@@ -10,16 +10,14 @@
= f.text_area :redirect_uri, class: 'form-control', required: true
%span.form-text.text-muted
- Use one line per URI
+ = _('Use one line per URI')
- if Doorkeeper.configuration.native_redirect_uri
%span.form-text.text-muted
- Use
- %code= Doorkeeper.configuration.native_redirect_uri
- for local tests
+ = _('Use <code>%{native_redirect_uri}</code> for local tests').html_safe % { native_redirect_uri: Doorkeeper.configuration.native_redirect_uri }
.form-group
= f.label :scopes, class: 'label-light'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes
.prepend-top-default
- = f.submit 'Save application', class: "btn btn-create"
+ = f.submit _('Save application'), class: "btn btn-create"
diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml
index 49f90298a50..aad4200f240 100644
--- a/app/views/doorkeeper/applications/edit.html.haml
+++ b/app/views/doorkeeper/applications/edit.html.haml
@@ -1,4 +1,4 @@
-- page_title "Edit", @application.name, "Applications"
+- page_title _("Edit"), @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
-%h3.page-title Edit application
+%h3.page-title= _('Edit application')
= render 'form', application: @application
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index cdf3ff81bd9..ab3a1b100ce 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -1,4 +1,4 @@
-- page_title "Applications"
+- page_title _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
.row.prepend-top-default
@@ -7,28 +7,27 @@
= page_title
%p
- if user_oauth_applications?
- Manage applications that can use GitLab as an OAuth provider,
- and applications that you've authorized to use your account.
+ = _("Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account.")
- else
- Manage applications that you've authorized to use your account.
+ = _("Manage applications that you've authorized to use your account.")
.col-lg-8
- if user_oauth_applications?
%h5.prepend-top-0
- Add new application
+ = _('Add new application')
= render 'form', application: @application
%hr
- if user_oauth_applications?
.oauth-applications
%h5
- Your applications (#{@applications.size})
+ = _("Your applications (%{size})") % { size: @applications.size }
- if @applications.any?
.table-responsive
%table.table
%thead
%tr
- %th Name
- %th Callback URL
- %th Clients
+ %th= _('Name')
+ %th= _('Callback URL')
+ %th= _('Clients')
%th.last-heading
%tbody
- @applications.each do |application|
@@ -41,25 +40,25 @@
%td
= link_to edit_oauth_application_path(application), class: "btn btn-transparent append-right-5" do
%span.sr-only
- Edit
+ = _('Edit')
= icon('pencil')
= render 'delete_form', application: application, small: true
- else
.settings-message.text-center
- You don't have any applications
+ = _("You don't have any applications")
.oauth-authorized-applications.prepend-top-20.append-bottom-default
- if user_oauth_applications?
%h5
- Authorized applications (#{@authorized_tokens.size})
+ = _("Authorized applications (%{size})") % { size: @authorized_tokens.size }
- if @authorized_tokens.any?
.table-responsive
%table.table.table-striped
%thead
%tr
- %th Name
- %th Authorized At
- %th Scope
+ %th= _('Name')
+ %th= _('Authorized At')
+ %th= _('Scope')
%th
%tbody
- @authorized_apps.each do |app|
@@ -72,12 +71,12 @@
- @authorized_anonymous_tokens.each do |token|
%tr
%td
- Anonymous
+ = _('Anonymous')
.form-text.text-muted
- %em Authorization was granted by entering your username and password in the application.
+ %em= _("Authorization was granted by entering your username and password in the application.")
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', token: token
- else
.settings-message.text-center
- You don't have any authorized applications
+ = _("You don't have any authorized applications")
diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml
index d3692d1f759..a66fab20d7c 100644
--- a/app/views/doorkeeper/applications/new.html.haml
+++ b/app/views/doorkeeper/applications/new.html.haml
@@ -1,6 +1,6 @@
-- page_title "New Application"
+- page_title _("New Application")
-%h3.page-title New Application
+%h3.page-title= _("New Application")
%hr
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 89ad626f73f..bb76ac6d5f6 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -1,27 +1,27 @@
-- add_to_breadcrumbs "Applications", oauth_applications_path
+- add_to_breadcrumbs _("Applications"), oauth_applications_path
- breadcrumb_title @application.name
-- page_title @application.name, "Applications"
+- page_title @application.name, _("Applications")
- @content_class = "limit-container-width" unless fluid_layout
%h3.page-title
- Application: #{@application.name}
+ = _("Application: %{name}") % { name: @application.name }
.table-holder.oauth-application-show
%table.table
%tr
%td
- Application Id
+ = _('Application Id')
%td
%code#application_id= @application.uid
%tr
%td
- Secret:
+ = _('Secret:')
%td
%code#secret= @application.secret
%tr
%td
- Callback url
+ = _('Callback url')
%td
- @application.redirect_uri.split.each do |uri|
%div
@@ -30,5 +30,5 @@
= render "shared/tokens/scopes_list", token: @application
.form-actions
- = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide float-left'
+ = link_to _('Edit'), edit_oauth_application_path(@application), class: 'btn btn-primary wide float-left'
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml
index 6117b00149f..32b4ccb0fe6 100644
--- a/app/views/doorkeeper/authorizations/error.html.haml
+++ b/app/views/doorkeeper/authorizations/error.html.haml
@@ -1,3 +1,3 @@
-%h3.page-title An error has occurred
+%h3.page-title= _("An error has occurred")
%main{ :role => "main" }
%pre= @pre_auth.error_response.body[:error_description]
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index 28cdc7607e0..ca62a59d909 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -3,34 +3,28 @@
.modal-content
.modal-header
%h3.page-title
- Authorize
- = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
- to use your account?
+ - link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
+ = _("Authorize %{link_to_client} to use your account?")
.modal-body
- if current_user.admin?
.text-warning
%p
= icon("exclamation-triangle fw")
- You are an admin, which means granting access to
- %strong= @pre_auth.client.name
- will allow them to interact with GitLab as an admin as well. Proceed with caution.
+ = _('You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution.').html_safe % { client_name: @pre_auth.client.name }
%p
- An application called
- = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
- is requesting access to your GitLab account.
+ - link_to_client = link_to(@pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer')
+ = _("An application called %{link_to_client} is requesting access to your GitLab account.").html_safe % { link_to_client: link_to_client }
- auth_app_owner = @pre_auth.client.application.owner
- if auth_app_owner
- This application was created by
- = succeed "." do
- = link_to auth_app_owner.name, user_path(auth_app_owner)
+ - link_to_owner = link_to(auth_app_owner.name, user_path(auth_app_owner))
+ = _("This application was created by %{link_to_owner}.").html_safe % { link_to_owner: link_to_owner }
- Please note that this application is not provided by GitLab and you should verify its authenticity before
- allowing access.
+ = _("Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access.")
- if @pre_auth.scopes
%p
- This application will be able to:
+ = _("This application will be able to:")
%ul
- @pre_auth.scopes.each do |scope|
%li
@@ -44,7 +38,7 @@
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Deny", class: "btn btn-danger"
+ = submit_tag _("Deny"), class: "btn btn-danger"
= form_tag oauth_authorization_path, method: :post, class: 'inline' do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
@@ -52,4 +46,4 @@
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Authorize", class: "btn btn-success prepend-left-10"
+ = submit_tag _("Authorize"), class: "btn btn-success prepend-left-10"
diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml
index 44e868e6782..e4bfd69e7f8 100644
--- a/app/views/doorkeeper/authorizations/show.html.haml
+++ b/app/views/doorkeeper/authorizations/show.html.haml
@@ -1,3 +1,3 @@
-%h3.page-title Authorization code:
+%h3.page-title= _("Authorization code:")
%main{ :role => "main" }
%code#authorization_code= params[:code]
diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
index 11c1e67878e..08f2442f025 100644
--- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
@@ -6,4 +6,4 @@
= form_tag path do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
- = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm'
+ = submit_tag _('Revoke'), onclick: "return confirm('#{_('Are you sure?')}')", class: 'btn btn-remove btn-sm'
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
index 30c9d02b72e..8e73298b250 100644
--- a/app/views/doorkeeper/authorized_applications/index.html.haml
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -1,12 +1,12 @@
%header
- %h1 Your authorized applications
+ %h1= _("Your authorized applications")
%main{ :role => "main" }
.table-holder
%table.table.table-striped
%thead
%tr
- %th Application
- %th Created At
+ %th= _('Application')
+ %th= _('Created At')
%th
%th
%tbody
diff --git a/app/views/explore/_head.html.haml b/app/views/explore/_head.html.haml
index a3b0709e261..eefc797cf03 100644
--- a/app/views/explore/_head.html.haml
+++ b/app/views/explore/_head.html.haml
@@ -1,6 +1,6 @@
.explore-title.text-center
%h2
- Explore GitLab
+ = _("Explore GitLab")
%p.lead
- Discover projects, groups and snippets. Share your projects with others
+ = _("Discover projects, groups and snippets. Share your projects with others")
%br
diff --git a/app/views/explore/groups/_nav.html.haml b/app/views/explore/groups/_nav.html.haml
index ab4787c6d05..c337149a2f3 100644
--- a/app/views/explore/groups/_nav.html.haml
+++ b/app/views/explore/groups/_nav.html.haml
@@ -2,7 +2,7 @@
%ul.nav-links.nav.nav-tabs
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path do
- Explore Groups
+ = _("Explore Groups")
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 0643b9cfbc5..387c37b7a91 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Groups"
-- header_title "Groups", dashboard_groups_path
+- page_title _("Groups")
+- header_title _("Groups"), dashboard_groups_path
- if current_user
= render 'dashboard/groups_head'
@@ -10,14 +10,14 @@
- if cookies[:explore_groups_landing_dismissed] != 'true'
.explore-groups.landing.content-block.js-explore-groups-landing.hide
- %button.dismiss-button{ type: 'button', 'aria-label' => 'Dismiss' }= icon('times')
+ %button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= icon('times')
.svg-container
= custom_icon('icon_explore_groups_splash')
.inner-content
- %p Below you will find all the groups that are public.
- %p You can easily contribute to them by requesting to join these groups.
+ %p= _("Below you will find all the groups that are public.")
+ %p= _("You can easily contribute to them by requesting to join these groups.")
- if params[:filter].blank? && @groups.empty?
- .nothing-here-block No public groups
+ .nothing-here-block= _("No public groups")
- else
= render 'groups'
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 6abb56ba6d2..b694103ccaf 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -2,16 +2,16 @@
.dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe')
- %span.light Visibility:
+ %span.light= _("Visibility:")
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
- Any
+ = _('Any')
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right
%li
= link_to filter_projects_path(visibility_level: nil) do
- Any
+ = _('Any')
- Gitlab::VisibilityLevel.values.each do |level|
%li{ class: active_when(level.to_s == params[:visibility_level]) || 'light' }
= link_to filter_projects_path(visibility_level: level) do
diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml
index 558cd26f1e0..bf65c19b720 100644
--- a/app/views/explore/projects/_nav.html.haml
+++ b/app/views/explore/projects/_nav.html.haml
@@ -2,13 +2,13 @@
%ul.nav-links.nav.nav-tabs
= nav_link(page: [trending_explore_projects_path, explore_root_path]) do
= link_to trending_explore_projects_path do
- Trending
+ = _('Trending')
= nav_link(page: starred_explore_projects_path) do
= link_to starred_explore_projects_path do
- Most stars
+ = _('Most stars')
= nav_link(page: explore_projects_path) do
= link_to explore_projects_path do
- All
+ = _('All')
.nav-controls
- unless current_user
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index f00802e0af7..452f390695c 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Projects"
-- header_title "Projects", dashboard_projects_path
+- page_title _("Projects")
+- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index f00802e0af7..452f390695c 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Projects"
-- header_title "Projects", dashboard_projects_path
+- page_title _("Projects")
+- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index f00802e0af7..452f390695c 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,6 +1,6 @@
- @hide_top_links = true
-- page_title "Projects"
-- header_title "Projects", dashboard_projects_path
+- page_title _("Projects")
+- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index 577c63503a8..82a497289f3 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -1,8 +1,8 @@
.nav-block.activities
+ = render 'shared/event_filter'
.controls
= link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
%i.fa.fa-rss
- = render 'shared/event_filter'
.content_list
= spinner
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index f7094375023..f0d1e837317 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -21,26 +21,16 @@
%th= _('Status')
%tbody
- @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
%td
= provider_project_link(provider, project.import_source)
%td
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- - if project.import_status == 'finished'
- %span
- %i.fa.fa-check
- = _('Done')
- - elsif project.import_status == 'started'
- %i.fa.fa-spinner.fa-spin
- = _('Started')
- - elsif project.import_status == 'failed'
- = _('Failed')
- - else
- = project.human_import_status_name
+ = render 'import/project_status', project: project
- @repos.each do |repo|
- %tr{ id: "repo_#{repo.id}" }
+ %tr{ id: "repo_#{repo.id}", data: { qa: { repo_path: repo.full_name } } }
%td
= provider_project_link(provider, repo.full_name)
%td.import-target
@@ -50,7 +40,7 @@
- if current_user.can_select_namespace?
- selected = params[:namespace_id] || :current_user
- opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {}
- = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'input-group-text select2 js-select-namespace', tabindex: 1 }
+ = select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'input-group-text select2 js-select-namespace qa-project-namespace-select', tabindex: 1 }
- else
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
@@ -61,6 +51,6 @@
= has_ci_cd_only_params? ? _('Connect') : _('Import')
= icon("spinner spin", class: "loading-icon")
-.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}",
- import_path: "#{url_for([:import, provider])}",
- ci_cd_only: "#{has_ci_cd_only_params?}" } }
+.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
+ import_path: url_for([:import, provider]),
+ ci_cd_only: has_ci_cd_only_params?.to_s } }
diff --git a/app/views/import/_project_status.html.haml b/app/views/import/_project_status.html.haml
new file mode 100644
index 00000000000..280bcbc1e63
--- /dev/null
+++ b/app/views/import/_project_status.html.haml
@@ -0,0 +1,11 @@
+- case project.import_status
+- when 'finished'
+ = icon('check')
+ = _('Done')
+- when 'started'
+ = icon("spinner spin")
+ = _('Started')
+- when 'failed'
+ = _('Failed')
+- else
+ = project.human_import_status_name
diff --git a/app/views/import/bitbucket/deploy_key.js.haml b/app/views/import/bitbucket/deploy_key.js.haml
index 81b34ab5c9d..99e8ac1afa1 100644
--- a/app/views/import/bitbucket/deploy_key.js.haml
+++ b/app/views/import/bitbucket/deploy_key.js.haml
@@ -1,3 +1,3 @@
:plain
job = $("tr#repo_#{@repo_id}")
- job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>")
+ job.find(".import-actions").html("<p class='alert alert-danger'>#{_('Access denied! Please verify you can add deploy keys to this repository.')}</p>")
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 4e8f715db4f..a75b7aa9dd2 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -1,22 +1,22 @@
-- page_title 'Bitbucket import'
-- header_title 'Projects', root_path
+- page_title _('Bitbucket import')
+- header_title _('Projects'), root_path
%h3.page-title
%i.fa.fa-bitbucket
- Import projects from Bitbucket
+ = _('Import projects from Bitbucket')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%hr
%p
- if @incompatible_repos.any?
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all compatible projects
+ = _('Import all compatible projects')
= icon('spinner spin', class: 'loading-icon')
- else
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all projects
+ = _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
.table-responsive
@@ -26,9 +26,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From Bitbucket
- %th To GitLab
- %th Status
+ %th= _('From Bitbucket')
+ %th= _('To GitLab')
+ %th= _('Status')
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -40,10 +40,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _('done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _('started')
- else
= project.human_import_status_name
@@ -66,7 +66,7 @@
= text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
- Import
+ = _('Import')
= icon('spinner spin', class: 'loading-icon')
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
@@ -74,16 +74,13 @@
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target
%td.import-actions-job-status
- = label_tag 'Incompatible Project', nil, class: 'label badge-danger'
+ = label_tag _('Incompatible Project'), nil, class: 'label badge-danger'
- if @incompatible_repos.any?
%p
- One or more of your Bitbucket projects cannot be imported into GitLab
- directly because they use Subversion or Mercurial for version control,
- rather than Git. Please convert
- = link_to 'them to Git,', 'https://www.atlassian.com/git/tutorials/migrating-overview'
- and go through the
- = link_to 'import flow', status_import_bitbucket_path
- again.
+ = _("One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
+ - link_to_git = link_to(_('Git'), 'https://www.atlassian.com/git/tutorials/migrating-overview')
+ - link_to_import_flow = link_to(_('import flow'), status_import_bitbucket_path)
+ = _("Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again.").html_safe % { link_to_git: link_to_git, link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_path}", import_path: "#{import_bitbucket_path}" } }
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index 74d686b6703..b54b1af1e0c 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -1,26 +1,24 @@
-- page_title "FogBugz Import"
-- header_title "Projects", root_path
+- page_title _("FogBugz Import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
%hr
= form_tag callback_import_fogbugz_path do
%p
- To get started you enter your FogBugz URL and login information below.
- In the next steps, you'll be able to map users and select the projects
- you want to import.
+ = _("To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import.")
.form-group.row
- = label_tag :uri, 'FogBugz URL', class: 'col-form-label col-md-2'
+ = label_tag :uri, _('FogBugz URL'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control'
.form-group.row
- = label_tag :email, 'FogBugz Email', class: 'col-form-label col-md-2'
+ = label_tag :email, _('FogBugz Email'), class: 'col-form-label col-md-2'
.col-md-4
= text_field_tag :email, nil, class: 'form-control'
.form-group.row
- = label_tag :password, 'FogBugz Password', class: 'col-form-label col-md-2'
+ = label_tag :password, _('FogBugz Password'), class: 'col-form-label col-md-2'
.col-md-4
= password_field_tag :password, nil, class: 'form-control'
.form-actions
- = submit_tag 'Continue to the next step', class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index d27c5d3c36d..ff2f989c509 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -1,39 +1,33 @@
-- page_title 'User map', 'FogBugz import'
-- header_title "Projects", root_path
+- page_title _('User map'), _('FogBugz import')
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
%hr
= form_tag create_user_map_import_fogbugz_path do
%p
- Customize how FogBugz email addresses and usernames are imported into GitLab.
- In the next step, you'll be able to select the projects you want to import.
+ = _("Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import.")
%p
- The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below.
+ = _("The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below.")
%ul
%li
- %strong Default: Map a FogBugz account ID to a full name
+ %strong= _("Default: Map a FogBugz account ID to a full name")
%p
- An empty GitLab User field will add the FogBugz user's full name
- (e.g. "By John Smith") in the description of all issues and comments.
- It will also associate and/or assign these issues and comments with
- the project creator.
+ = _("An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator.")
%li
- %strong Map a FogBugz account ID to a GitLab user
+ %strong= _("Map a FogBugz account ID to a GitLab user")
%p
- Selecting a GitLab user will add a link to the GitLab user in the descriptions
- of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
- associate and/or assign these issues and comments with the selected user.
+ = _('Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also associate and/or assign these issues and comments with the selected user.').html_safe
.table-holder
%table.table
%thead
%tr
- %th ID
- %th Name
- %th Email
- %th GitLab User
+ %th= _("ID")
+ %th= _("Name")
+ %th= _("Email")
+ %th= _("GitLab User")
%tbody
- @user_map.each do |id, user|
%tr
@@ -45,4 +39,4 @@
scope: :all, email_user: true, selected: user[:gitlab_user])
.form-actions
- = submit_tag 'Continue to the next step', class: 'btn btn-create'
+ = submit_tag _('Continue to the next step'), class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index 7b832c6a23a..830d141ebea 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -1,20 +1,19 @@
-- page_title "FogBugz import"
-- header_title "Projects", root_path
+- page_title _("FogBugz import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-bug
- Import projects from FogBugz
+ = _('Import projects from FogBugz')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%p.light
- Optionally, you can
- = link_to 'customize', new_user_map_import_fogbugz_path
- how FogBugz email addresses and usernames are imported into GitLab.
+ - link_to_customize = link_to('customize', new_user_map_import_fogbugz_path)
+ = _('Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab.').html_safe % { link_to_customize: link_to_customize }
%hr
%p
= button_tag class: 'btn btn-import btn-success js-import-all' do
- Import all projects
+ = _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -24,9 +23,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From FogBugz
- %th To GitLab
- %th Status
+ %th= _("From FogBugz")
+ %th= _("To GitLab")
+ %th= _("Status")
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -38,10 +37,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _("done")
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _("started")
- else
= project.human_import_status_name
@@ -53,7 +52,7 @@
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _("Import")
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_fogbugz_path}", import_path: "#{import_fogbugz_path}" } }
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index 581576a8a3d..2b3102f9af9 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -1,23 +1,22 @@
-- page_title "Gitea Import"
-- header_title "Projects", root_path
+- page_title _("Gitea Import")
+- header_title _("Projects"), root_path
%h3.page-title
= custom_icon('go_logo')
- Import Projects from Gitea
+ = _('Import Projects from Gitea')
%p
- To get started, please enter your Gitea Host URL and a
- = succeed '.' do
- = link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token'
+ - link_to_personal_token = link_to(_('Personal Access Token'), 'https://github.com/gogits/go-gogs-client/wiki#access-token')
+ = _('To get started, please enter your Gitea Host URL and a %{link_to_personal_token}.').html_safe % { link_to_personal_token: link_to_personal_token }
= form_tag personal_access_token_import_gitea_path do
.form-group.row
- = label_tag :gitea_host_url, 'Gitea Host URL', class: 'col-form-label col-sm-2'
+ = label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
.form-group.row
- = label_tag :personal_access_token, 'Personal Access Token', class: 'col-form-label col-sm-2'
+ = label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2'
.col-sm-4
= text_field_tag :personal_access_token, nil, class: 'form-control'
.form-actions
- = submit_tag 'List Your Gitea Repositories', class: 'btn btn-create'
+ = submit_tag _('List Your Gitea Repositories'), class: 'btn btn-create'
diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml
index 589ca27e45d..88244fde16b 100644
--- a/app/views/import/gitea/status.html.haml
+++ b/app/views/import/gitea/status.html.haml
@@ -1,7 +1,7 @@
-- page_title "Gitea Import"
-- header_title "Projects", root_path
+- page_title _("Gitea Import")
+- header_title _("Projects"), root_path
%h3.page-title
= custom_icon('go_logo')
- Import Projects from Gitea
+ = _('Import Projects from Gitea')
= render 'import/githubish_status', provider: 'gitea'
diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml
index b9ebb1a39d9..6ff25f2c842 100644
--- a/app/views/import/github/new.html.haml
+++ b/app/views/import/github/new.html.haml
@@ -1,7 +1,7 @@
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
- page_title title
- breadcrumb_title title
-- header_title "Projects", root_path
+- header_title _("Projects"), root_path
%h3.page-title
= icon 'github', text: import_github_title
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index b00b972d9c9..be057be6d1a 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -1,7 +1,7 @@
- title = has_ci_cd_only_params? ? _('Connect repositories from GitHub') : _('GitHub import')
- page_title title
- breadcrumb_title title
-- header_title "Projects", root_path
+- header_title _("Projects"), root_path
%h3.page-title
= icon 'github', text: import_github_title
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 37734414835..b7bfbae5edf 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -1,15 +1,15 @@
-- page_title "GitLab.com import"
-- header_title "Projects", root_path
+- page_title _("GitLab.com import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-heart
- Import projects from GitLab.com
+ = _('Import projects from GitLab.com')
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%hr
%p
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all projects
+ = _('Import all projects')
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -19,9 +19,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From GitLab.com
- %th To this GitLab instance
- %th Status
+ %th= _('From GitLab.com')
+ %th= _('To this GitLab instance')
+ %th= _('Status')
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -33,10 +33,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _('done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _('started')
- else
= project.human_import_status_name
@@ -48,7 +48,7 @@
= import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _('Import')
= icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_gitlab_path}", import_path: "#{import_gitlab_path}" } }
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index cc672a5ea7c..a258fc64b1e 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -1,9 +1,9 @@
-- page_title "GitLab Import"
-- header_title "Projects", root_path
+- page_title _("GitLab Import")
+- header_title _("Projects"), root_path
%h3.page-title
= icon('gitlab')
- Import an exported GitLab project
+ = _('Import an exported GitLab project')
%hr
= form_tag import_gitlab_project_path, class: 'new_project', multipart: true do
@@ -24,19 +24,19 @@
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
- = label_tag :path, 'Project name', class: 'label-light'
+ = label_tag :path, _('Project name'), class: 'label-light'
= text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
.row
.form-group.col-md-12
- To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.
+ = _("To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.")
.row
.form-group.col-sm-12
= hidden_field_tag :namespace_id, @namespace.id
- = label_tag :file, 'GitLab project export', class: 'label-light'
+ = label_tag :file, _('GitLab project export'), class: 'label-light'
.form-group
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = submit_tag 'Import project', class: 'btn btn-create'
- = link_to 'Cancel', new_project_path, class: 'btn btn-cancel'
+ = submit_tag _('Import project'), class: 'btn btn-create'
+ = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index 2f1fb8d9c56..fd6e4726fc5 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -1,62 +1,62 @@
-- page_title "Google Code import"
-- header_title "Projects", root_path
+- page_title _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
%hr
= form_tag callback_import_google_code_path, multipart: true do
%p
- Follow the steps below to export your Google Code project data.
- In the next step, you'll be able to select the projects you want to import.
+ = _('Follow the steps below to export your Google Code project data.')
+ = _("In the next step, you'll be able to select the projects you want to import.")
%ol
%li
%p
- Go to
- #{link_to "Google Takeout", "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer'}.
+ - link_to_google_takeout = link_to(_("Google Takeout"), "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer')
+ = _("Go to %{link_to_google_takeout}.").html_safe % { link_to_google_takeout: link_to_google_takeout }
%li
%p
- Make sure you're logged into the account that owns the projects you'd like to import.
+ = _("Make sure you're logged into the account that owns the projects you'd like to import.")
%li
%p
- Click the <strong>Select none</strong> button on the right, since we only need "Google Code Project Hosting".
+ = _('Click the <strong>Select none</strong> button on the right, since we only need "Google Code Project Hosting".').html_safe
%li
%p
- Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right.
+ = _('Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right.').html_safe
%li
%p
- Choose <strong>Next</strong> at the bottom of the page.
+ = _('Choose <strong>Next</strong> at the bottom of the page.').html_safe
%li
%p
- Leave the "File type" and "Delivery method" options on their default values.
+ = _('Leave the "File type" and "Delivery method" options on their default values.')
%li
%p
- Choose <strong>Create archive</strong> and wait for archiving to complete.
+ = _('Choose <strong>Create archive</strong> and wait for archiving to complete.').html_safe
%li
%p
- Click the <strong>Download</strong> button and wait for downloading to complete.
+ = _('Click the <strong>Download</strong> button and wait for downloading to complete.').html_safe
%li
%p
- Find the downloaded ZIP file and decompress it.
+ = _('Find the downloaded ZIP file and decompress it.')
%li
%p
- Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file.
+ = _('Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file.').html_safe
%li
%p
- Upload <code>GoogleCodeProjectHosting.json</code> here:
+ = _('Upload <code>GoogleCodeProjectHosting.json</code> here:').html_safe
%p
%input{ type: "file", name: "dump_file", id: "dump_file" }
%li
%p
- Do you want to customize how Google Code email addresses and usernames are imported into GitLab?
+ = _('Do you want to customize how Google Code email addresses and usernames are imported into GitLab?')
%p
= label_tag :create_user_map_0 do
= radio_button_tag :create_user_map, 0, true
- No, directly import the existing email addresses and usernames.
+ = _('No, directly import the existing email addresses and usernames.')
%p
= label_tag :create_user_map_1 do
= radio_button_tag :create_user_map, 1, false
- Yes, let me map Google Code users to full names or GitLab users.
+ = _('Yes, let me map Google Code users to full names or GitLab users.')
%li
%p
- = submit_tag 'Continue to the next step', class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-create"
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index 91c774f575c..baaaf6bdc63 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -1,44 +1,36 @@
-- page_title "User map", "Google Code import"
-- header_title "Projects", root_path
+- page_title _("User map"), _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
%hr
= form_tag create_user_map_import_google_code_path do
%p
- Customize how Google Code email addresses and usernames are imported into GitLab.
- In the next step, you'll be able to select the projects you want to import.
+ = _("Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import.")
%p
- The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.
+ = _("The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.").html_safe
%ul
%li
- %strong Default: Directly import the Google Code email address or username
+ %strong= _("Default: Directly import the Google Code email address or username")
%p
- <code>"johnsmith@example.com": "johnsm...@example.com"</code>
- will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com.
- The email address or username is masked to ensure the user's privacy.
+ = _('<code>"johnsmith@example.com": "johnsm...@example.com"</code> will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user\'s privacy.').html_safe
%li
- %strong Map a Google Code user to a GitLab user
+ %strong= _("Map a Google Code user to a GitLab user")
%p
- <code>"johnsmith@example.com": "@johnsmith"</code>
- will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com,
- and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.
+ = _('<code>"johnsmith@example.com": "@johnsmith"</code> will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com, and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.').html_safe
%li
- %strong Map a Google Code user to a full name
+ %strong= _("Map a Google Code user to a full name")
%p
- <code>"johnsmith@example.com": "John Smith"</code>
- will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.
+ = _('<code>"johnsmith@example.com": "John Smith"</code> will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.').html_safe
%li
- %strong Map a Google Code user to a full email address
+ %strong= _("Map a Google Code user to a full email address")
%p
- <code>"johnsmith@example.com": "johnsmith@example.com"</code>
- will add "By <a href="#">johnsmith@example.com</a>" to all issues and comments originally created by johnsmith@example.com.
- By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address.
+ = _('<code>"johnsmith@example.com": "johnsmith@example.com"</code> will add "By <a href="#">johnsmith@example.com</a>" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user\'s privacy. Use this option if you want to show the full email address.').html_safe
.form-group.row
.col-sm-12
= text_area_tag :user_map, JSON.pretty_generate(@user_map), class: 'form-control', rows: 15
.form-actions
- = submit_tag 'Continue to the next step', class: "btn btn-create"
+ = submit_tag _('Continue to the next step'), class: "btn btn-create"
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index acf7a108cb0..347e2820f94 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -1,25 +1,24 @@
-- page_title "Google Code import"
-- header_title "Projects", root_path
+- page_title _("Google Code import")
+- header_title _("Projects"), root_path
%h3.page-title
%i.fa.fa-google
- Import projects from Google Code
+ = _('Import projects from Google Code')
- if @repos.any?
%p.light
- Select projects you want to import.
+ = _('Select projects you want to import.')
%p.light
- Optionally, you can
- = link_to "customize", new_user_map_import_google_code_path
- how Google Code email addresses and usernames are imported into GitLab.
+ - link_to_customize = link_to(_("customize"), new_user_map_import_google_code_path)
+ = _("Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab.").html_safe % { link_to_customize: link_to_customize }
%hr
%p
- if @incompatible_repos.any?
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all compatible projects
+ = _("Import all compatible projects")
= icon("spinner spin", class: "loading-icon")
- else
= button_tag class: "btn btn-import btn-success js-import-all" do
- Import all projects
+ = _("Import all projects")
= icon("spinner spin", class: "loading-icon")
.table-responsive
@@ -29,9 +28,9 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th From Google Code
- %th To GitLab
- %th Status
+ %th= _("From Google Code")
+ %th= _("To GitLab")
+ %th= _("Status")
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
@@ -43,10 +42,10 @@
- if project.import_status == 'finished'
%span
%i.fa.fa-check
- done
+ = _("done")
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
- started
+ = _("started")
- else
= project.human_import_status_name
@@ -58,7 +57,7 @@
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
- Import
+ = _("Import")
= icon("spinner spin", class: "loading-icon")
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
@@ -66,15 +65,12 @@
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target
%td.import-actions-job-status
- = label_tag "Incompatible Project", nil, class: "label badge-danger"
+ = label_tag _("Incompatible Project"), nil, class: "label badge-danger"
- if @incompatible_repos.any?
%p
- One or more of your Google Code projects cannot be imported into GitLab
- directly because they use Subversion or Mercurial for version control,
- rather than Git. Please convert them to Git on Google Code, and go
- through the
- = link_to "import flow", new_import_google_code_path
- again.
+ = _("One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
+ - link_to_import_flow = link_to(_("import flow"), new_import_google_code_path)
+ = _("Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again.").html_safe % { link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_google_code_path}", import_path: "#{import_google_code_path}" } }
diff --git a/app/views/import/manifest/_form.html.haml b/app/views/import/manifest/_form.html.haml
new file mode 100644
index 00000000000..763beb5958f
--- /dev/null
+++ b/app/views/import/manifest/_form.html.haml
@@ -0,0 +1,23 @@
+= form_tag upload_import_manifest_path, multipart: true do
+ .form-group
+ = label_tag :group_id, nil, class: 'label-light' do
+ = _('Group')
+ .input-group
+ .input-group-prepend.has-tooltip{ title: root_url }
+ .input-group-text
+ = root_url
+ = select_tag :group_id, namespaces_options(nil, display_path: true, groups_only: true), { class: 'select2 js-select-namespace' }
+ .form-text.text-muted
+ = _('Choose the top-level group for your repository imports.')
+
+ .form-group
+ = label_tag :manifest, class: 'label-light' do
+ = _('Manifest')
+ = file_field_tag :manifest, class: 'form-control-file', required: true
+ .form-text.text-muted
+ = _('Import multiple repositories by uploading a manifest file.')
+ = link_to icon('question-circle'), help_page_path('user/project/import/manifest')
+
+ .append-bottom-10
+ = submit_tag _('List available repositories'), class: 'btn btn-success'
+ = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/manifest/new.html.haml b/app/views/import/manifest/new.html.haml
new file mode 100644
index 00000000000..056e4922b9e
--- /dev/null
+++ b/app/views/import/manifest/new.html.haml
@@ -0,0 +1,12 @@
+- page_title "Manifest file import"
+- header_title "Projects", root_path
+
+%h3.page-title
+ = _('Manifest file import')
+
+- if @errors.present?
+ .alert.alert-danger
+ - @errors.each do |error|
+ = error
+
+= render 'form'
diff --git a/app/views/import/manifest/status.html.haml b/app/views/import/manifest/status.html.haml
new file mode 100644
index 00000000000..5b2e1005398
--- /dev/null
+++ b/app/views/import/manifest/status.html.haml
@@ -0,0 +1,42 @@
+- page_title "Manifest import"
+- header_title "Projects", root_path
+- provider = 'manifest'
+
+%h3.page-title
+ = _('Manifest file import')
+
+%p
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ = import_all_githubish_repositories_button_label
+ = icon("spinner spin", class: "loading-icon")
+
+.table-responsive
+ %table.table.import-jobs
+ %thead
+ %tr
+ %th= _('Repository URL')
+ %th= _('To GitLab')
+ %th= _('Status')
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
+ %td
+ = project.import_url
+ %td
+ = link_to_project project
+ %td.job-status
+ = render 'import/project_status', project: project
+
+ - @pending_repositories.each do |repository|
+ %tr{ id: "repo_#{repository[:id]}" }
+ %td
+ = repository[:url]
+ %td.import-target
+ = import_project_target(@group.full_path, repository[:path])
+ %td.import-actions.job-status
+ = button_tag class: "btn btn-import js-add-to-import" do
+ = _('Import')
+ = icon("spinner spin", class: "loading-icon")
+
+.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
+ import_path: url_for([:import, provider]) } }
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index d8e32651b36..3aa8eb18bf3 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -61,7 +61,9 @@
- if header_link?(:sign_in)
%li.nav-item
%div
- = link_to "Sign in / Register", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
+ - sign_in_text = allow_signup? ? 'Sign in / Register' : 'Sign in'
+ = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
+
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
%span.sr-only Toggle navigation
diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml
index d35df706036..792291bde75 100644
--- a/app/views/layouts/header/_new_dropdown.haml
+++ b/app/views/layouts/header/_new_dropdown.haml
@@ -37,7 +37,7 @@
%li.dropdown-bold-header GitLab
- if current_user.can_create_project?
%li
- = link_to 'New project', new_project_path
+ = link_to 'New project', new_project_path, class: 'qa-global-new-project-link'
- if current_user.can_create_group?
%li
= link_to 'New group', new_group_path
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 7647e25e804..4029287fc0e 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,16 +1,19 @@
%ul.list-unstyled.navbar-sub-nav
- if dashboard_nav_link?(:projects)
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown" }) do
- %a{ href: "#", data: { toggle: "dropdown" } }
+ %button{ type: 'button', data: { toggle: "dropdown" } }
Projects
= sprite_icon('angle-down', css_class: 'caret-down')
- .dropdown-menu.projects-dropdown-menu
+ .dropdown-menu.frequent-items-dropdown-menu
= render "layouts/nav/projects_dropdown/show"
- if dashboard_nav_link?(:groups)
- = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "d-none d-sm-block" }) do
- = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups qa-groups-link', title: 'Groups' do
+ = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown" }) do
+ %button{ type: 'button', data: { toggle: "dropdown" } }
Groups
+ = sprite_icon('angle-down', css_class: 'caret-down')
+ .dropdown-menu.frequent-items-dropdown-menu
+ = render "layouts/nav/groups_dropdown/show"
- if dashboard_nav_link?(:activity)
= nav_link(path: 'dashboard#activity', html_options: { class: "d-none d-lg-block d-xl-block" }) do
@@ -34,11 +37,6 @@
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu
%ul
- - if dashboard_nav_link?(:groups)
- = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "d-block d-sm-none" }) do
- = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
- Groups
-
- if dashboard_nav_link?(:activity)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, title: 'Activity' do
diff --git a/app/views/layouts/nav/groups_dropdown/_show.html.haml b/app/views/layouts/nav/groups_dropdown/_show.html.haml
new file mode 100644
index 00000000000..3ce1fa6bcca
--- /dev/null
+++ b/app/views/layouts/nav/groups_dropdown/_show.html.haml
@@ -0,0 +1,12 @@
+- group_meta = { id: @group.id, name: @group.name, namespace: @group.full_name, web_url: group_path(@group), avatar_url: @group.avatar_url } if @group&.persisted?
+.frequent-items-dropdown-container
+ .frequent-items-dropdown-sidebar.qa-groups-dropdown-sidebar
+ %ul
+ = nav_link(path: 'dashboard/groups#index') do
+ = link_to dashboard_groups_path, class: 'qa-your-groups-link' do
+ = _('Your groups')
+ = nav_link(path: 'groups#explore') do
+ = link_to explore_groups_path do
+ = _('Explore groups')
+ .frequent-items-dropdown-content
+ #js-groups-dropdown{ data: { user_name: current_user.username, group: group_meta } }
diff --git a/app/views/layouts/nav/projects_dropdown/_show.html.haml b/app/views/layouts/nav/projects_dropdown/_show.html.haml
index 5809d6f7fea..f2170f71532 100644
--- a/app/views/layouts/nav/projects_dropdown/_show.html.haml
+++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml
@@ -1,6 +1,6 @@
- project_meta = { id: @project.id, name: @project.name, namespace: @project.full_name, web_url: project_path(@project), avatar_url: @project.avatar_url } if @project&.persisted?
-.projects-dropdown-container
- .project-dropdown-sidebar.qa-projects-dropdown-sidebar
+.frequent-items-dropdown-container
+ .frequent-items-dropdown-sidebar.qa-projects-dropdown-sidebar
%ul
= nav_link(path: 'dashboard/projects#index') do
= link_to dashboard_projects_path, class: 'qa-your-projects-link' do
@@ -11,5 +11,5 @@
= nav_link(path: 'projects#trending') do
= link_to explore_root_path do
= _('Explore projects')
- .project-dropdown-content
+ .frequent-items-dropdown-content
#js-projects-dropdown{ data: { user_name: current_user.username, project: project_meta } }
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 62402c32e08..4c73da4c75b 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -6,14 +6,14 @@
= sprite_icon('admin', size: 24)
.sidebar-context-title Admin Area
%ul.sidebar-top-level-items
- = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: {class: 'home'}) do
+ = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers cohorts conversational_development_index), html_options: {class: 'home'}) do
= link_to admin_root_path, class: 'shortcuts-tree' do
.nav-icon-container
= sprite_icon('overview')
%span.nav-item-name
Overview
%ul.sidebar-sub-level-items
- = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do
= link_to admin_root_path do
%strong.fly-out-top-item-name
#{ _('Overview') }
@@ -42,6 +42,10 @@
= link_to admin_runners_path, title: 'Runners' do
%span
Runners
+ = nav_link(controller: :gitaly_servers) do
+ = link_to admin_gitaly_servers_path, title: 'Gitaly Servers' do
+ %span
+ Gitaly Servers
= nav_link path: 'cohorts#index' do
= link_to admin_cohorts_path, title: 'Cohorts' do
%span
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 00d75b3399b..33de74dbaa2 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -122,7 +122,7 @@
= render_if_exists 'projects/sidebar/issues_service_desk'
= nav_link(controller: :milestones) do
- = link_to project_milestones_path(@project), title: 'Milestones' do
+ = link_to project_milestones_path(@project), title: 'Milestones', class: 'qa-milestones-link' do
%span
= _('Milestones')
- if project_nav_tab? :external_issue_tracker
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index c14700794ce..43a2d53b84d 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -5,11 +5,18 @@
.form-group
= f.label :key, class: 'label-light'
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.")
- = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: 'Typically starts with "ssh-rsa …"'
+ = f.text_area :key, class: "form-control js-add-ssh-key-validation-input", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
.form-group
= f.label :title, class: 'label-light'
- = f.text_field :title, class: "form-control", required: true, placeholder: 'e.g. My MacBook key'
+ = f.text_field :title, class: "form-control input-lg", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
%p.form-text.text-muted= _('Name your individual key via a title')
+ .js-add-ssh-key-validation-warning.hide
+ .bs-callout.bs-callout-warning{ role: 'alert', aria_live: 'assertive' }
+ %strong= _('Oops, are you sure?')
+ %p= s_("Profiles|This doesn't look like a public SSH key, are you sure you want to add it?")
+
+ %button.btn.btn-create.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
+
.prepend-top-default
- = f.submit 'Add key', class: "btn btn-create"
+ = f.submit s_('Profiles|Add key'), class: "btn btn-create js-add-ssh-key-validation-original-submit"
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 1f701f2aa1b..6bf21570d41 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,10 +1,9 @@
%div{ class: container_class }
.nav-block.activity-filter-block.activities
+ = render 'shared/event_filter'
.controls
= link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn rss-btn has-tooltip' do
= icon('rss')
- = render 'shared/event_filter'
-
.content_list.project-activity{ :"data-href" => activity_project_path(@project) }
= spinner
diff --git a/app/views/projects/_export.html.haml b/app/views/projects/_export.html.haml
index f4d4888bd15..aa980da7e95 100644
--- a/app/views/projects/_export.html.haml
+++ b/app/views/projects/_export.html.haml
@@ -31,7 +31,7 @@
%li Any encrypted tokens
%p
Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page.
- - if project.export_project_path
+ - if project.export_status == :finished
= link_to 'Download export', download_export_project_path(project),
rel: 'nofollow', download: '', method: :get, class: "btn btn-default"
= link_to 'Generate new export', generate_new_export_project_path(project),
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 8f535b9d789..3da6db08580 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -1,49 +1,62 @@
- active_tab = local_assigns.fetch(:active_tab, 'blank')
-- f = local_assigns.fetch(:f)
.project-import
.form-group.import-btn-container.clearfix
- = f.label :visibility_level, class: 'label-light' do #the label here seems wrong
+ %h5
Import project from
.import-buttons
- if gitlab_project_import_enabled?
.import_gitlab_project.has-tooltip{ data: { container: 'body' } }
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
- %div
- - if github_import_enabled?
+
+ - if github_import_enabled?
+ %div
= link_to new_import_github_path, class: 'btn js-import-github' do
= icon('github', text: 'GitHub')
- %div
- - if bitbucket_import_enabled?
+
+ - if bitbucket_import_enabled?
+ %div
= link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}" do
= icon('bitbucket', text: 'Bitbucket')
- unless bitbucket_import_configured?
= render 'bitbucket_import_modal'
- %div
- - if gitlab_import_enabled?
+
+ - if gitlab_import_enabled?
+ %div
= link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do
= icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured?
= render 'gitlab_import_modal'
- %div
- - if google_code_import_enabled?
+
+ - if google_code_import_enabled?
+ %div
= link_to new_import_google_code_path, class: 'btn import_google_code' do
= icon('google', text: 'Google Code')
- %div
- - if fogbugz_import_enabled?
+
+ - if fogbugz_import_enabled?
+ %div
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
= icon('bug', text: 'Fogbugz')
- %div
- - if gitea_import_enabled?
+
+ - if gitea_import_enabled?
+ %div
= link_to new_import_gitea_path, class: 'btn import_gitea' do
= custom_icon('go_logo')
Gitea
- %div
- - if git_import_enabled?
+
+ - if git_import_enabled?
+ %div
%button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' } }
= icon('git', text: 'Repo by URL')
+
+ - if manifest_import_enabled?
+ %div
+ = link_to new_import_manifest_path, class: 'btn import_manifest' do
+ = icon('file-text-o', text: 'Manifest file')
+
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
- %hr
+ = form_for @project, html: { class: 'new_project' } do |f|
+ %hr
= render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name"
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 3d97e93c9e9..14a7e84394a 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -11,7 +11,7 @@
- branch_label = s_('ChangeTypeActionLabel|Pick into branch')
- title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
-.modal{ id: "modal-#{type}-commit" }
+.modal{ id: "modal-#{type}-commit", tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml
index e0ecf56525a..f4c91377ecb 100644
--- a/app/views/projects/deployments/_actions.haml
+++ b/app/views/projects/deployments/_actions.haml
@@ -3,13 +3,12 @@
- if actions.present?
.btn-group
.dropdown
- %button.dropdown.dropdown-new.btn.btn-default{ type: 'button', 'data-toggle' => 'dropdown' }
- = custom_icon('icon_play')
+ %button.dropdown.dropdown-new.btn.btn-default.has-tooltip{ type: 'button', 'data-toggle' => 'dropdown', title: s_('Environments|Deploy to...') }
+ = sprite_icon('play')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-right
- actions.each do |action|
- next unless can?(current_user, :update_build, action)
%li
- = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow' do
- = custom_icon('icon_play')
+ = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow', class: 'btn' do
%span= action.name.humanize
diff --git a/app/views/projects/deployments/_rollback.haml b/app/views/projects/deployments/_rollback.haml
index 95f950948ab..281e042c915 100644
--- a/app/views/projects/deployments/_rollback.haml
+++ b/app/views/projects/deployments/_rollback.haml
@@ -1,6 +1,7 @@
- if can?(current_user, :create_deployment, deployment) && deployment.deployable
- = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do
+ - tooltip = deployment.last? ? s_('Environments|Re-deploy to environment') : s_('Environments|Rollback environment')
+ = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do
- if deployment.last?
- = _("Re-deploy")
+ = sprite_icon('repeat')
- else
- = _("Rollback")
+ = sprite_icon('redo')
diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml
index a264252e095..4694bc39d54 100644
--- a/app/views/projects/environments/_external_url.html.haml
+++ b/app/views/projects/environments/_external_url.html.haml
@@ -1,4 +1,4 @@
- if environment.external_url && can?(current_user, :read_environment, environment)
- = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url' do
+ = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip', title: s_('Environments|Open live environment') do
= sprite_icon('external-link')
View deployment
diff --git a/app/views/projects/environments/_stop.html.haml b/app/views/projects/environments/_stop.html.haml
deleted file mode 100644
index c35f9af2873..00000000000
--- a/app/views/projects/environments/_stop.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- if can?(current_user, :create_deployment, environment) && environment.stop_action?
- .inline
- = link_to stop_project_environment_path(@project, environment), method: :post,
- class: 'btn stop-env-link', rel: 'nofollow', data: { confirm: 'Are you sure you want to stop this environment?' } do
- = icon('stop', class: 'stop-env-icon')
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index add394a6356..c7890b37381 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -4,6 +4,33 @@
- page_title "Environments"
%div{ class: container_class }
+ - if can?(current_user, :stop_environment, @environment)
+ #stop-environment-modal.modal.fade{ tabindex: -1 }
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %h4.modal-title.d-flex.mw-100
+ Stopping
+ %span.has-tooltip.text-truncate.ml-1.mr-1.flex-fill{ title: @environment.name, data: { container: '#stop-environment-modal' } }
+ = @environment.name
+ ?
+ .modal-body
+ %p= s_('Environments|Are you sure you want to stop this environment?')
+ - unless @environment.stop_action_available?
+ .warning_message
+ %p= s_('Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file.').html_safe % { emphasis_start: '<strong>'.html_safe,
+ emphasis_end: '</strong>'.html_safe,
+ ci_config_link_start: '<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">'.html_safe,
+ ci_config_link_end: '</a>'.html_safe }
+ %a{ href: 'https://docs.gitlab.com/ee/ci/environments.html#stopping-an-environment',
+ target: '_blank',
+ rel: 'noopener noreferrer' }
+ = s_('Environments|Learn more about stopping environments')
+ .modal-footer
+ = button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' }
+ = button_to stop_project_environment_path(@project, @environment), class: 'btn btn-danger has-tooltip', method: :post do
+ = s_('Environments|Stop environment')
+
.row.top-area.adjust
.col-md-7
%h3.page-title= @environment.name
@@ -15,7 +42,10 @@
- if can?(current_user, :update_environment, @environment)
= link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn'
- if can?(current_user, :stop_environment, @environment)
- = link_to 'Stop', stop_project_environment_path(@project, @environment), data: { confirm: 'Are you sure you want to stop this environment?' }, class: 'btn btn-danger', method: :post
+ = button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal',
+ target: '#stop-environment-modal' } do
+ = sprite_icon('stop')
+ = s_('Environments|Stop')
.environments-container
- if @deployments.blank?
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 16c4f21279d..ca82054d799 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -10,7 +10,7 @@
.card-body
%pre
:preserve
- #{h(sanitize_repo_path(@project, @project.import_error))}
+ #{h(@project.import_error)}
= form_for @project, url: project_import_path(@project), method: :post do |f|
= render "shared/import_form", f: f
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 2c5ffd85372..1e4e9450ffa 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,2 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @issue],
+ html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' },
+ data: { markdown_version: @issue.cached_markdown_version } do |f|
= render 'shared/issuable/form', f: f, issuable: @issue
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index 179c1fcc684..5a59f956cb5 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -1,2 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request],
+ html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' },
+ data: { markdown_version: @merge_request.cached_markdown_version } do |f|
= render 'shared/issuable/form', f: f, issuable: @merge_request
diff --git a/app/views/projects/merge_requests/_mr_box.html.haml b/app/views/projects/merge_requests/_mr_box.html.haml
index 8a390cf8700..1a9ab288683 100644
--- a/app/views/projects/merge_requests/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/_mr_box.html.haml
@@ -1,4 +1,4 @@
-.detail-page-description.content-block
+.detail-page-description
%h2.title
= markdown_field(@merge_request, :title)
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 4cc59718715..28f0a167128 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,16 +1,18 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'} do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @milestone],
+ html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'},
+ data: { markdown_version: @milestone.cached_markdown_version } do |f|
= form_errors(@milestone)
.row
.col-md-6
.form-group.row
= f.label :title, "Title", class: "col-form-label col-sm-2"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control", required: true, autofocus: true
+ = f.text_field :title, maxlength: 255, class: "qa-milestone-title form-control", required: true, autofocus: true
.form-group.row.milestone-description
= f.label :description, "Description", class: "col-form-label col-sm-2"
.col-sm-10
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project) } do
- = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...'
+ = render 'projects/zen', f: f, attr: :description, classes: 'qa-milestone-description note-textarea', placeholder: 'Write milestone description...'
= render 'shared/notes/hints'
.clearfix
.error-alert
@@ -18,7 +20,7 @@
.form-actions
- if @milestone.new_record?
- = f.submit 'Create milestone', class: "btn-create btn"
+ = f.submit 'Create milestone', class: "btn-create btn qa-milestone-create-button"
= link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel"
- else
= f.submit 'Save changes', class: "btn-save btn"
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 5b0197ed58c..26d2ea8447b 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -8,7 +8,7 @@
.nav-controls
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @project)
- = link_to new_project_milestone_path(@project), class: "btn btn-new", title: 'New milestone' do
+ = link_to new_project_milestone_path(@project), class: "btn btn-new qa-new-project-milestone", title: 'New milestone' do
New milestone
.milestones
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index f7b04c436a6..2a9e20c2caa 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -60,7 +60,7 @@
= icon('angle-double-left')
.detail-page-description.milestone-detail
- %h2.title
+ %h2.title.qa-milestone-title
= markdown_field(@milestone, :title)
%div
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 5bb1bfb7059..6c363345e38 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -55,13 +55,12 @@
= render 'project_templates', f: f
.tab-pane.import-project-pane.js-toggle-container{ id: 'import-project-pane', class: active_when(active_tab == 'import'), role: 'tabpanel' }
- = form_for @project, html: { class: 'new_project' } do |f|
- - if import_sources_enabled?
- = render 'import_project_pane', f: f, active_tab: active_tab
- - else
- .nothing-here-block
- %h4 No import options available
- %p Contact an administrator to enable options for importing your project.
+ - if import_sources_enabled?
+ = render 'import_project_pane', active_tab: active_tab
+ - else
+ .nothing-here-block
+ %h4 No import options available
+ %p Contact an administrator to enable options for importing your project.
.save-project-loader.d-none
.center
diff --git a/app/views/projects/protected_branches/_update_protected_branch.html.haml b/app/views/projects/protected_branches/_update_protected_branch.html.haml
index f242459f69b..74bfaa9ff80 100644
--- a/app/views/projects/protected_branches/_update_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_update_protected_branch.html.haml
@@ -6,5 +6,5 @@
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_levels.first.access_level
= dropdown_tag( (protected_branch.push_access_levels.first.humanize || 'Select') ,
- options: { toggle_class: 'js-allowed-to-push qa-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
+ options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: protected_branch.push_access_levels.first.id }})
diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
index 82ef08272d3..05cee483c0e 100644
--- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
@@ -2,7 +2,7 @@
%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } }
%td
- %span.ref-name.qa-protected-branch-name= protected_branch.name
+ %span.ref-name= protected_branch.name
- if @project.root_ref?(protected_branch.name)
%span.badge.badge-info.prepend-left-5 default
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index d6f758608a0..8093cc2c2d7 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -11,7 +11,9 @@
%strong= @tag.name
- = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), html: { class: 'common-note-form release-form js-quick-submit' }) do |f|
+ = form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name),
+ html: { class: 'common-note-form release-form js-quick-submit' },
+ data: { markdown_version: @release.cached_markdown_version }) do |f|
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…"
= render 'shared/notes/hints'
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 26fe1de31fe..de692466fe5 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,7 +1,9 @@
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
- commit_message = commit_message % { page_title: @page.title }
-= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
+ html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' },
+ data: { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION } do |f|
= form_errors(@page)
- if @page.persisted?
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index a23396dc0d8..28353927135 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -11,9 +11,11 @@
.blocks-container
.block.block-first
- %ul.wiki-pages
- = render @sidebar_wiki_entries, context: 'sidebar'
-
+ - if @sidebar_page
+ = render_wiki_content(@sidebar_page)
+ - else
+ %ul.wiki-pages
+ = render @sidebar_wiki_entries, context: 'sidebar'
.block
= link_to project_wikis_pages_path(@project), class: 'btn btn-block' do
= s_("Wiki|More Pages")
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index ecb5b1c6ebc..7afb7b3a93b 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,4 +1,4 @@
-.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller.flex-fill
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.event-filter.scrolling-tabs.nav.nav-tabs
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index ac2ebb701a5..d38d161047b 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,7 +1,7 @@
- if any_projects?(@projects)
.project-item-select-holder.btn-group
- %a.btn.btn-new.new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
+ %a.btn.btn-new.new-project-item-link.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
= icon('spinner spin')
= project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled]
- %button.btn.btn-new.new-project-item-select-button
+ %button.btn.btn-new.new-project-item-select-button.qa-new-project-item-select-button
= icon('caret-down')
diff --git a/app/views/shared/hook_logs/_content.html.haml b/app/views/shared/hook_logs/_content.html.haml
index 532712ee6d1..f3b56df0c96 100644
--- a/app/views/shared/hook_logs/_content.html.haml
+++ b/app/views/shared/hook_logs/_content.html.haml
@@ -30,7 +30,7 @@
%h5 Request body:
%pre
- :plain
+ :escaped
#{JSON.pretty_generate(hook_log.request_data)}
%h5 Response headers:
%pre
@@ -40,5 +40,5 @@
%h5 Response body:
%pre
- :plain
+ :escaped
#{hook_log.response_body}
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 37625a4a163..c2da363b8c6 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -7,7 +7,7 @@
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
- if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
-= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
+= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "qa-issuable-milestone-dropdown js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "qa-issuable-dropdown-menu-milestone dropdown-menu-selectable dropdown-menu-milestone",
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected_text, project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if project
%ul.dropdown-footer-list
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index bd87bb38e77..3b017c62a80 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -18,7 +18,7 @@
= form.label :milestone_id, "Milestone", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
.col-sm-10{ class: ("col-md-8" if has_due_date) }
.issuable-form-select-holder
- = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, show_started: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
+ = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, show_started: false, extra_class: "qa-issuable-milestone-dropdown js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group.row
- has_labels = @labels && @labels.any?
= form.label :label_ids, "Labels", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index 6b2715b47a7..c360f1ffe2a 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -40,5 +40,5 @@
= yield(:note_actions)
- %a.btn.btn-cancel.js-note-discard{ role: "button", data: {cancel_text: "Discard draft" } }
+ %a.btn.btn-cancel.js-note-discard{ role: "button", data: {cancel_text: "Cancel" } }
Discard draft
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index d4e8f30e458..f5464058bc0 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -52,7 +52,7 @@
.note-text.md
= markdown_field(note, :note)
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
- .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
+ .original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore, markdown_version: note.cached_markdown_version } }
#{note.note}
- if note_editable
= render 'shared/notes/edit', note: note
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 858adc8be37..5e5c050d5c3 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -2,7 +2,9 @@
= page_specific_javascript_tag('lib/ace.js')
.snippet-form-holder
- = form_for @snippet, url: url, html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" } do |f|
+ = form_for @snippet, url: url,
+ html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" },
+ data: { markdown_version: @snippet.cached_markdown_version } do |f|
= form_errors(@snippet)
.form-group.row
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index b8b854853b7..d4be1ccfcfa 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -46,7 +46,6 @@
- mail_scheduler:mail_scheduler_issue_due
- mail_scheduler:mail_scheduler_notification_service
-- object_storage_upload
- object_storage:object_storage_background_move
- object_storage:object_storage_migrate_uploads
diff --git a/app/workers/ci/build_trace_chunk_flush_worker.rb b/app/workers/ci/build_trace_chunk_flush_worker.rb
index 6376c6d32cf..9dbf2e5e1ac 100644
--- a/app/workers/ci/build_trace_chunk_flush_worker.rb
+++ b/app/workers/ci/build_trace_chunk_flush_worker.rb
@@ -7,7 +7,7 @@ module Ci
def perform(build_trace_chunk_id)
::Ci::BuildTraceChunk.find_by(id: build_trace_chunk_id).try do |build_trace_chunk|
- build_trace_chunk.use_database!
+ build_trace_chunk.persist_data!
end
end
end
diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb
index f9f0efb302a..12706613ac2 100644
--- a/app/workers/email_receiver_worker.rb
+++ b/app/workers/email_receiver_worker.rb
@@ -15,14 +15,14 @@ class EmailReceiverWorker
private
- def handle_failure(raw, e)
- Rails.logger.warn("Email can not be processed: #{e}\n\n#{raw}")
+ def handle_failure(raw, error)
+ Rails.logger.warn("Email can not be processed: #{error}\n\n#{raw}")
return unless raw.present?
can_retry = false
reason =
- case e
+ case error
when Gitlab::Email::UnknownIncomingEmail
"We couldn't figure out what the email is for. Please create your issue or comment through the web interface."
when Gitlab::Email::SentNotificationNotFoundError
@@ -42,7 +42,7 @@ class EmailReceiverWorker
"The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member."
when Gitlab::Email::InvalidRecordError
can_retry = true
- e.message
+ error.message
end
if reason
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index fd49bc18161..2d381c6fd6c 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -65,10 +65,10 @@ class GitGarbageCollectWorker
client.repack_incremental
end
rescue GRPC::NotFound => e
- Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
+ Gitlab::GitLogger.error("#{__method__} failed:\nRepository not found")
raise Gitlab::Git::Repository::NoRepository.new(e)
rescue GRPC::BadStatus => e
- Gitlab::GitLogger.error("#{method} failed:\n#{e}")
+ Gitlab::GitLogger.error("#{__method__} failed:\n#{e}")
raise Gitlab::Git::CommandError.new(e)
end
diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb
index a3ecfa8e711..01d03ec7888 100644
--- a/app/workers/object_storage/migrate_uploads_worker.rb
+++ b/app/workers/object_storage/migrate_uploads_worker.rb
@@ -1,6 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
-# rubocop:disable Style/Documentation
module ObjectStorage
class MigrateUploadsWorker
diff --git a/app/workers/object_storage_upload_worker.rb b/app/workers/object_storage_upload_worker.rb
deleted file mode 100644
index f17980a83d8..00000000000
--- a/app/workers/object_storage_upload_worker.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-# @Deprecated - remove once the `object_storage_upload` queue is empty
-# The queue has been renamed `object_storage:object_storage_background_upload`
-#
-class ObjectStorageUploadWorker
- include ApplicationWorker
-
- sidekiq_options retry: 5
-
- def perform(uploader_class_name, subject_class_name, file_field, subject_id)
- uploader_class = uploader_class_name.constantize
- subject_class = subject_class_name.constantize
-
- return unless uploader_class < ObjectStorage::Concern
- return unless uploader_class.object_store_enabled?
- return unless uploader_class.background_upload_enabled?
-
- subject = subject_class.find(subject_id)
- uploader = subject.public_send(file_field) # rubocop:disable GitlabSecurity/PublicSend
- uploader.migrate!(ObjectStorage::Store::REMOTE)
- end
-end
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index ed39b4a1ea8..c9f6df9b56d 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -79,9 +79,10 @@ class ProcessCommitWorker
# Avoid reprocessing commits that already exist in the upstream
# when project is forked. This will also prevent duplicated system notes.
def commit_exists_in_upstream?(project, commit_hash)
- return false unless project.forked?
+ upstream_project = project.fork_source
+
+ return false unless upstream_project
- upstream_project = project.forked_from_project
commit_id = commit_hash.with_indifferent_access[:id]
upstream_project.commit(commit_id).present?
end
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index 051382a08a9..07559ea479b 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -4,9 +4,11 @@ module RepositoryCheck
class BatchWorker
include ApplicationWorker
include RepositoryCheckQueue
+ include ExclusiveLeaseGuard
RUN_TIME = 3600
BATCH_SIZE = 10_000
+ LEASE_TIMEOUT = 1.hour
attr_reader :shard_name
@@ -16,6 +18,20 @@ module RepositoryCheck
return unless Gitlab::CurrentSettings.repository_checks_enabled
return unless Gitlab::ShardHealthCache.healthy_shard?(shard_name)
+ try_obtain_lease do
+ perform_repository_checks
+ end
+ end
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
+
+ def lease_key
+ "repository_check_batch_worker:#{shard_name}"
+ end
+
+ def perform_repository_checks
start = Time.now
# This loop will break after a little more than one hour ('a little
@@ -26,7 +42,7 @@ module RepositoryCheck
project_ids.each do |project_id|
break if Time.now - start >= RUN_TIME
- next unless try_obtain_lease(project_id)
+ next unless try_obtain_lease_for_project(project_id)
SingleRepositoryWorker.new.perform(project_id)
end
@@ -60,7 +76,7 @@ module RepositoryCheck
Project.where(repository_storage: shard_name)
end
- def try_obtain_lease(id)
+ def try_obtain_lease_for_project(id)
# Use a 24-hour timeout because on servers/projects where 'git fsck' is
# super slow we definitely do not want to run it twice in parallel.
Gitlab::ExclusiveLease.new(
diff --git a/app/workers/repository_check/dispatch_worker.rb b/app/workers/repository_check/dispatch_worker.rb
index 891a273afd7..96634f09a15 100644
--- a/app/workers/repository_check/dispatch_worker.rb
+++ b/app/workers/repository_check/dispatch_worker.rb
@@ -3,13 +3,22 @@ module RepositoryCheck
include ApplicationWorker
include CronjobQueue
include ::EachShardWorker
+ include ExclusiveLeaseGuard
+
+ LEASE_TIMEOUT = 1.hour
def perform
return unless Gitlab::CurrentSettings.repository_checks_enabled
- each_eligible_shard do |shard_name|
- RepositoryCheck::BatchWorker.perform_async(shard_name)
+ try_obtain_lease do
+ each_eligible_shard do |shard_name|
+ RepositoryCheck::BatchWorker.perform_async(shard_name)
+ end
end
end
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
end
end
diff --git a/bin/changelog b/bin/changelog
index d7b2a1a2de9..758c036161e 100755
--- a/bin/changelog
+++ b/bin/changelog
@@ -23,6 +23,8 @@ module ChangelogHelpers
Abort = Class.new(StandardError)
Done = Class.new(StandardError)
+ MAX_FILENAME_LENGTH = 140 # ecryptfs has a limit of 140 characters
+
def capture_stdout(cmd)
output = IO.popen(cmd, &:read)
fail_with "command failed: #{cmd.join(' ')}" unless $?.success?
@@ -142,7 +144,9 @@ class ChangelogEntry
def initialize(options)
@options = options
+ end
+ def execute
assert_feature_branch!
assert_title!
assert_new_file!
@@ -221,10 +225,12 @@ class ChangelogEntry
end
def file_path
- File.join(
+ base_path = File.join(
unreleased_path,
- branch_name.gsub(/[^\w-]/, '-') << '.yml'
- )
+ branch_name.gsub(/[^\w-]/, '-'))
+
+ # Add padding for .yml extension
+ base_path[0..MAX_FILENAME_LENGTH - 5] + '.yml'
end
def unreleased_path
@@ -250,7 +256,7 @@ end
if $0 == __FILE__
begin
options = ChangelogOptionParser.parse(ARGV)
- ChangelogEntry.new(options)
+ ChangelogEntry.new(options).execute
rescue ChangelogHelpers::Abort => ex
$stderr.puts ex.message
exit 1
diff --git a/changelogs/custom_wiki_sidebar.yml b/changelogs/custom_wiki_sidebar.yml
new file mode 100644
index 00000000000..988fccc929c
--- /dev/null
+++ b/changelogs/custom_wiki_sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: "Custom Wiki Sidebar Support Issue 14995"
+merge_request:
+author: Josh Sooter
+type: added
diff --git a/changelogs/unreleased/36234-nav-add-groups-dropdown.yml b/changelogs/unreleased/36234-nav-add-groups-dropdown.yml
new file mode 100644
index 00000000000..86a24102665
--- /dev/null
+++ b/changelogs/unreleased/36234-nav-add-groups-dropdown.yml
@@ -0,0 +1,5 @@
+---
+title: Add dropdown to Groups link in top bar
+merge_request: 18280
+author:
+type: added
diff --git a/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml b/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml
new file mode 100644
index 00000000000..c6a0dc4f129
--- /dev/null
+++ b/changelogs/unreleased/41671-fixing-milestone-date-change-when-editing.yml
@@ -0,0 +1,5 @@
+---
+title: "Fixing milestone date change when editing"
+merge_request: 20279
+author: Orlando Del Aguila
+type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml b/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml
new file mode 100644
index 00000000000..cabe5216045
--- /dev/null
+++ b/changelogs/unreleased/42415-omit-projects-from-get-group-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Adds with_projects optional parameter to GET /groups/:id API endpoint
+merge_request: 20494
+author:
+type: changed
diff --git a/changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml b/changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml
new file mode 100644
index 00000000000..908c7a238fd
--- /dev/null
+++ b/changelogs/unreleased/46246-gitlab-project-export-should-use-object-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Add Object Storage to project export
+merge_request: 20105
+author:
+type: added
diff --git a/changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key.yml b/changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key.yml
new file mode 100644
index 00000000000..64bbecf3405
--- /dev/null
+++ b/changelogs/unreleased/46396-recognise-when-a-user-is-trying-to-validate-a-private-ssh-key.yml
@@ -0,0 +1,5 @@
+---
+title: Update new SSH key page to improve key input validation
+merge_request: 19997
+author:
+type: other
diff --git a/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml b/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml
new file mode 100644
index 00000000000..d95714a5267
--- /dev/null
+++ b/changelogs/unreleased/46930-fix-updated_at-if-created_at-is-set-note-api.yml
@@ -0,0 +1,5 @@
+---
+title: Fix updated_at if created_at is set for Note API
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/48237-toggle-file-comments.yml b/changelogs/unreleased/48237-toggle-file-comments.yml
new file mode 100644
index 00000000000..2e893aad0b2
--- /dev/null
+++ b/changelogs/unreleased/48237-toggle-file-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes toggle discussion button not expanding collapsed discussions
+merge_request: 20452
+author:
+type: fixed
diff --git a/changelogs/unreleased/48537-update-avatar-only-via-api.yml b/changelogs/unreleased/48537-update-avatar-only-via-api.yml
new file mode 100644
index 00000000000..9b3ab946cc1
--- /dev/null
+++ b/changelogs/unreleased/48537-update-avatar-only-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Allow updating a project's avatar without other params
+merge_request:
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/48578-disable-gcp-free-credit-banner-at-instance-level.yml b/changelogs/unreleased/48578-disable-gcp-free-credit-banner-at-instance-level.yml
new file mode 100644
index 00000000000..575767df912
--- /dev/null
+++ b/changelogs/unreleased/48578-disable-gcp-free-credit-banner-at-instance-level.yml
@@ -0,0 +1,5 @@
+---
+title: Add option to hide third party offers in admin application settings
+merge_request: 20379
+author:
+type: added
diff --git a/changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml b/changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml
new file mode 100644
index 00000000000..36a4b5f754d
--- /dev/null
+++ b/changelogs/unreleased/48661-node-6-and-7-compatibility-broken-by-recent-monaco-editor-upgrade.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve compatibility issues with node 6
+merge_request: 20461
+author:
+type: fixed
diff --git a/changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml b/changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml
new file mode 100644
index 00000000000..f4267582f89
--- /dev/null
+++ b/changelogs/unreleased/48670-application-settings-may-not-be-invalidated-if-migrations-are-run.yml
@@ -0,0 +1,6 @@
+---
+title: Stop relying on migrations in the CacheableAttributes cache key and cache attributes
+ for 1 minute instead
+merge_request: 20389
+author:
+type: fixed
diff --git a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml b/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
new file mode 100644
index 00000000000..7552e0d3878
--- /dev/null
+++ b/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
@@ -0,0 +1,5 @@
+---
+title: Add uploader support to Import/Export uploads
+merge_request: 20484
+author:
+type: added
diff --git a/changelogs/unreleased/48789-remove-event-listeners-scroll.yml b/changelogs/unreleased/48789-remove-event-listeners-scroll.yml
new file mode 100644
index 00000000000..9cc3f7adc36
--- /dev/null
+++ b/changelogs/unreleased/48789-remove-event-listeners-scroll.yml
@@ -0,0 +1,6 @@
+---
+title: Improves performance on Merge Request diff tab by removing the scroll event
+ listeners being added to every file
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/48894-fix-rss-button-interaction.yml b/changelogs/unreleased/48894-fix-rss-button-interaction.yml
new file mode 100644
index 00000000000..546a4233d7e
--- /dev/null
+++ b/changelogs/unreleased/48894-fix-rss-button-interaction.yml
@@ -0,0 +1,5 @@
+---
+title: Fix RSS button interaction on Dashboard, Project and Group activities
+merge_request: 20549
+author:
+type: fixed
diff --git a/changelogs/unreleased/48934.yml b/changelogs/unreleased/48934.yml
new file mode 100644
index 00000000000..8e2e53ed198
--- /dev/null
+++ b/changelogs/unreleased/48934.yml
@@ -0,0 +1,5 @@
+---
+title: Improve danger confirmation modals by focusing input field
+merge_request:
+author: Jamie Schembri
+type: added
diff --git a/changelogs/unreleased/48951-clean-up.yml b/changelogs/unreleased/48951-clean-up.yml
new file mode 100644
index 00000000000..0102cd43f96
--- /dev/null
+++ b/changelogs/unreleased/48951-clean-up.yml
@@ -0,0 +1,5 @@
+---
+title: Removes unused vuex code in mr refactor and removes unneeded dependencies
+merge_request: 20499
+author:
+type: other
diff --git a/changelogs/unreleased/48976-fix-sti-background-migration.yml b/changelogs/unreleased/48976-fix-sti-background-migration.yml
new file mode 100644
index 00000000000..e95536b213c
--- /dev/null
+++ b/changelogs/unreleased/48976-fix-sti-background-migration.yml
@@ -0,0 +1,6 @@
+---
+title: "[Rails5] Fix 'Invalid single-table inheritance type: Group is not a subclass
+ of Gitlab::BackgroundMigration::FixCrossProjectLabelLinks::Namespace'"
+merge_request: 20462
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml b/changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml
new file mode 100644
index 00000000000..f786d9e2235
--- /dev/null
+++ b/changelogs/unreleased/48978-fix-helm-installation-on-cluster.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes base command used in Helm installations
+merge_request: 20471
+author:
+type: fixed
diff --git a/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml b/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml
new file mode 100644
index 00000000000..ab320450a1a
--- /dev/null
+++ b/changelogs/unreleased/49114-add-gitaly-servers-to-admin-overview-navigation-menu.yml
@@ -0,0 +1,5 @@
+---
+title: Gitaly Servers link into Admin > Overview navigation menu
+merge_request: 20550
+author:
+type: added
diff --git a/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
new file mode 100644
index 00000000000..f21bd454e84
--- /dev/null
+++ b/changelogs/unreleased/49291-fix-memory-graph-component-typo.yml
@@ -0,0 +1,5 @@
+---
+title: Fix typo in CSS transform property for Memory Graph component
+merge_request: 20650
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml b/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml
new file mode 100644
index 00000000000..08376014ad7
--- /dev/null
+++ b/changelogs/unreleased/add-dst-support-to-pipeline-schedule.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for daylight savings time to pipleline schedules
+merge_request: 20145
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
new file mode 100644
index 00000000000..4f9a551d13e
--- /dev/null
+++ b/changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml
@@ -0,0 +1,5 @@
+---
+title: Add merge request header branch actions left margin
+merge_request: 20643
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/an-no-healthcheck-until-brooklyn.yml b/changelogs/unreleased/an-no-healthcheck-until-brooklyn.yml
new file mode 100644
index 00000000000..4942688d00f
--- /dev/null
+++ b/changelogs/unreleased/an-no-healthcheck-until-brooklyn.yml
@@ -0,0 +1,5 @@
+---
+title: Remove healthchecks from prometheus endpoint
+merge_request: 20565
+author:
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-activerecord-statementinvalid-mysql2-error-expression-1-of-select-list-is-not-in-group-by-clause.yml b/changelogs/unreleased/blackst0ne-rails5-activerecord-statementinvalid-mysql2-error-expression-1-of-select-list-is-not-in-group-by-clause.yml
new file mode 100644
index 00000000000..d9cccc49830
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-activerecord-statementinvalid-mysql2-error-expression-1-of-select-list-is-not-in-group-by-clause.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix milestone GROUP BY query"
+merge_request: 20256
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/build-chunks-on-object-storage.yml b/changelogs/unreleased/build-chunks-on-object-storage.yml
new file mode 100644
index 00000000000..9f36dfee378
--- /dev/null
+++ b/changelogs/unreleased/build-chunks-on-object-storage.yml
@@ -0,0 +1,6 @@
+---
+title: Use object storage as the first class persistable store for new live trace
+ architecture
+merge_request: 19515
+author:
+type: changed
diff --git a/changelogs/unreleased/bvl-preload-parents-after-pagination.yml b/changelogs/unreleased/bvl-preload-parents-after-pagination.yml
new file mode 100644
index 00000000000..ff3d4716d34
--- /dev/null
+++ b/changelogs/unreleased/bvl-preload-parents-after-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce the number of queries when searching for groups
+merge_request: 20398
+author:
+type: performance
diff --git a/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
new file mode 100644
index 00000000000..49648cdfcfc
--- /dev/null
+++ b/changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml
@@ -0,0 +1,5 @@
+---
+title: Close revert and cherry pick modal on escape keypress
+merge_request: 20341
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/dz-manifest-import.yml b/changelogs/unreleased/dz-manifest-import.yml
new file mode 100644
index 00000000000..b0d29b0869f
--- /dev/null
+++ b/changelogs/unreleased/dz-manifest-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to import multiple repositories by uploading a manifest file
+merge_request: 20304
+author:
+type: added
diff --git a/changelogs/unreleased/feature-gb-email-delivery-metrics.yml b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
new file mode 100644
index 00000000000..9d0d08a471d
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-email-delivery-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add emails delivery Prometheus metrics
+merge_request: 20638
+author:
+type: added
diff --git a/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml b/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml
new file mode 100644
index 00000000000..7e9e8c33a71
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-add-missing-before-sha-predefined-variable.yml
@@ -0,0 +1,5 @@
+---
+title: Add missing predefined variable and fix docs
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml b/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml
new file mode 100644
index 00000000000..adf582e34a2
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-fix-project-settings-build-time-validation.yml
@@ -0,0 +1,5 @@
+---
+title: Limit maximum project build timeout setting to 1 month
+merge_request: 20591
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-performance-problem-of-tags-query.yml b/changelogs/unreleased/fix-performance-problem-of-tags-query.yml
new file mode 100644
index 00000000000..4649775be9c
--- /dev/null
+++ b/changelogs/unreleased/fix-performance-problem-of-tags-query.yml
@@ -0,0 +1,5 @@
+---
+title: Fix performance problem of accessing tag list for projects api endpoints
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/fix-project-api-archived.yml b/changelogs/unreleased/fix-project-api-archived.yml
new file mode 100644
index 00000000000..9d119fd3429
--- /dev/null
+++ b/changelogs/unreleased/fix-project-api-archived.yml
@@ -0,0 +1,5 @@
+---
+title: Fix archived parameter for projects API
+merge_request: 20566
+author: Peter Marko
+type: fixed
diff --git a/changelogs/unreleased/fix-search-bar.yml b/changelogs/unreleased/fix-search-bar.yml
new file mode 100644
index 00000000000..d4c0c1efddf
--- /dev/null
+++ b/changelogs/unreleased/fix-search-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Fix search bar text input alignment
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fl-mr-refactor-performance-improvements.yml b/changelogs/unreleased/fl-mr-refactor-performance-improvements.yml
new file mode 100644
index 00000000000..649d1b5da67
--- /dev/null
+++ b/changelogs/unreleased/fl-mr-refactor-performance-improvements.yml
@@ -0,0 +1,5 @@
+---
+title: Structure getters for diff Store properly and adds specs
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/frozen-string-enable-app-services.yml b/changelogs/unreleased/frozen-string-enable-app-services.yml
new file mode 100644
index 00000000000..cfc1f356e3a
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-services.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in apps/uploaders/*.rb
+merge_request: 20401
+author: gfyoung
+type: other
diff --git a/changelogs/unreleased/gitaly-serverservice-info-timeout.yml b/changelogs/unreleased/gitaly-serverservice-info-timeout.yml
new file mode 100644
index 00000000000..7f2fe8b9c93
--- /dev/null
+++ b/changelogs/unreleased/gitaly-serverservice-info-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Use appropriate timeout on Gitaly server info checks, avoid error on timeout
+merge_request: 20552
+author:
+type: fixed
diff --git a/changelogs/unreleased/ide-merge-request-info.yml b/changelogs/unreleased/ide-merge-request-info.yml
new file mode 100644
index 00000000000..104f48ae309
--- /dev/null
+++ b/changelogs/unreleased/ide-merge-request-info.yml
@@ -0,0 +1,5 @@
+---
+title: Display merge request title & description in Web IDE
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/ide-pipeline-icon-open.yml b/changelogs/unreleased/ide-pipeline-icon-open.yml
new file mode 100644
index 00000000000..3a73ff2170f
--- /dev/null
+++ b/changelogs/unreleased/ide-pipeline-icon-open.yml
@@ -0,0 +1,5 @@
+---
+title: Clicking CI icon in Web IDE now opens up pipelines panel
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/ide-row-dropdown-design-update.yml b/changelogs/unreleased/ide-row-dropdown-design-update.yml
new file mode 100644
index 00000000000..e0fe64c944e
--- /dev/null
+++ b/changelogs/unreleased/ide-row-dropdown-design-update.yml
@@ -0,0 +1,5 @@
+---
+title: Updated design of new entry dropdown in Web IDE
+merge_request: 20526
+author:
+type: changed
diff --git a/changelogs/unreleased/improve-metadata-access-performance.yml b/changelogs/unreleased/improve-metadata-access-performance.yml
new file mode 100644
index 00000000000..b16fa99dd3b
--- /dev/null
+++ b/changelogs/unreleased/improve-metadata-access-performance.yml
@@ -0,0 +1,5 @@
+---
+title: Access metadata directly from Object Storage
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/issue_47709.yml b/changelogs/unreleased/issue_47709.yml
new file mode 100644
index 00000000000..c3ef55fd692
--- /dev/null
+++ b/changelogs/unreleased/issue_47709.yml
@@ -0,0 +1,5 @@
+---
+title: 'Allow to toggle notifications for issues due soon'
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/jprovazn-delete-upload-worker.yml b/changelogs/unreleased/jprovazn-delete-upload-worker.yml
new file mode 100644
index 00000000000..52916482d32
--- /dev/null
+++ b/changelogs/unreleased/jprovazn-delete-upload-worker.yml
@@ -0,0 +1,5 @@
+---
+title: Remove deprecated object_storage_upload queue.
+merge_request:
+author:
+type: removed
diff --git a/changelogs/unreleased/jprovazn-upload-symlink.yml b/changelogs/unreleased/jprovazn-upload-symlink.yml
new file mode 100644
index 00000000000..265791d332f
--- /dev/null
+++ b/changelogs/unreleased/jprovazn-upload-symlink.yml
@@ -0,0 +1,5 @@
+---
+title: Add /uploads subdirectory to allowed upload paths.
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/perf-wiki-pattern-once.yml b/changelogs/unreleased/perf-wiki-pattern-once.yml
new file mode 100644
index 00000000000..fb4085a06ae
--- /dev/null
+++ b/changelogs/unreleased/perf-wiki-pattern-once.yml
@@ -0,0 +1,5 @@
+---
+title: Improve render performance of large wiki pages
+merge_request: 20465
+author: Peter Leitzen
+type: performance
diff --git a/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml b/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml
new file mode 100644
index 00000000000..6994a238074
--- /dev/null
+++ b/changelogs/unreleased/process-commits-as-normal-in-forks-with-missing-upstream.yml
@@ -0,0 +1,5 @@
+---
+title: Process commits as normal in forks when the upstream project is deleted
+merge_request: 20534
+author:
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48977.yml b/changelogs/unreleased/rails5-fix-48977.yml
new file mode 100644
index 00000000000..bfd86f20e24
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-48977.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix mysql milliseconds problem in specs
+merge_request: 20464
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml
new file mode 100644
index 00000000000..afd9865ee45
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-fix-pr-importer-spec.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 mysql fix milliseconds problem in pull request importer spec
+merge_request: 20475
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-mysql-rename-column.yml b/changelogs/unreleased/rails5-mysql-rename-column.yml
new file mode 100644
index 00000000000..cbae9250744
--- /dev/null
+++ b/changelogs/unreleased/rails5-mysql-rename-column.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 MySQL fix rename_column as part of cleanup_concurrent_column_type_change
+merge_request: 20514
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-update-gemfile-lock.yml b/changelogs/unreleased/rails5-update-gemfile-lock.yml
new file mode 100644
index 00000000000..58931587fff
--- /dev/null
+++ b/changelogs/unreleased/rails5-update-gemfile-lock.yml
@@ -0,0 +1,5 @@
+---
+title: Update Gemfile.rails5.lock with latest Gemfile.lock changes
+merge_request: 20466
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rosulk-patch-12.yml b/changelogs/unreleased/rosulk-patch-12.yml
deleted file mode 100644
index 9637c88d1a4..00000000000
--- a/changelogs/unreleased/rosulk-patch-12.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Flex issue board columns
-merge_request: 19250
-author: Roman Rosluk
-type: changed
diff --git a/changelogs/unreleased/runners-max-timeout-param.yml b/changelogs/unreleased/runners-max-timeout-param.yml
new file mode 100644
index 00000000000..875f805d849
--- /dev/null
+++ b/changelogs/unreleased/runners-max-timeout-param.yml
@@ -0,0 +1,5 @@
+---
+title: Add missing maximum_timeout parameter
+merge_request: 20355
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-47797-ce.yml b/changelogs/unreleased/sh-fix-issue-47797-ce.yml
new file mode 100644
index 00000000000..456d96acacb
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-47797-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Fix handling of annotated tags when Gitaly is not in use
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml b/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml
new file mode 100644
index 00000000000..b7366cf2569
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-stderr-pipe-consumption.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid process deadlock in popen by consuming input pipes
+merge_request: 20600
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml b/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml
new file mode 100644
index 00000000000..7717d0aab37
--- /dev/null
+++ b/changelogs/unreleased/sh-handle-colons-in-url-passwords.yml
@@ -0,0 +1,5 @@
+---
+title: Properly handle colons in URL passwords
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-optimize-wiki-empty-check.yml b/changelogs/unreleased/sh-optimize-wiki-empty-check.yml
new file mode 100644
index 00000000000..31ca7497b5a
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-wiki-empty-check.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize ProjectWiki#empty? check
+merge_request: 20573
+author:
+type: performance
diff --git a/changelogs/unreleased/tweak-sql-buckets.yml b/changelogs/unreleased/tweak-sql-buckets.yml
new file mode 100644
index 00000000000..00a0f733ee1
--- /dev/null
+++ b/changelogs/unreleased/tweak-sql-buckets.yml
@@ -0,0 +1,5 @@
+---
+title: Add a 10 ms bucket for SQL timings
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/update-issue-closing-pattern.yml b/changelogs/unreleased/update-issue-closing-pattern.yml
new file mode 100644
index 00000000000..95488adf449
--- /dev/null
+++ b/changelogs/unreleased/update-issue-closing-pattern.yml
@@ -0,0 +1,5 @@
+---
+title: Update issue closing pattern
+merge_request: 20554
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml
new file mode 100644
index 00000000000..39e10121507
--- /dev/null
+++ b/changelogs/unreleased/upgrade-hamlit-for-ruby25.yml
@@ -0,0 +1,5 @@
+---
+title: 'Update hamlit to fix ruby 2.5 incompatibilities, fixes #42045'
+merge_request:
+author: Matthew Dawson
+type: fixed
diff --git a/changelogs/unreleased/winh-stop-all-environments.yml b/changelogs/unreleased/winh-stop-all-environments.yml
new file mode 100644
index 00000000000..6e5f2f506d9
--- /dev/null
+++ b/changelogs/unreleased/winh-stop-all-environments.yml
@@ -0,0 +1,5 @@
+---
+title: Support manually stopping any environment from the UI
+merge_request: 20077
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml b/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml
new file mode 100644
index 00000000000..62addff1d0f
--- /dev/null
+++ b/changelogs/unreleased/winh-upgrade-grape-path-helpers.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade grape-path-helpers to 1.0.6
+merge_request: 20601
+author:
+type: other
diff --git a/config/application.rb b/config/application.rb
index 97bc86b3e3a..0304f466734 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../boot', __FILE__)
+require File.expand_path('boot', __dir__)
require 'rails/all'
@@ -211,7 +211,7 @@ module Gitlab
next unless name.include?('namespace_project')
define_method(name.sub('namespace_project', 'project')) do |project, *args|
- send(name, project&.namespace, project, *args) # rubocop:disable GitlabSecurity/PublicSend
+ send(name, project&.namespace, project, *args)
end
end
end
diff --git a/config/environment.rb b/config/environment.rb
index 487a4564b47..5d35937f7c6 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -4,7 +4,7 @@
if %w[1 true].include?(ENV["RAILS5"])
require_relative 'application'
else
- require File.expand_path('../application', __FILE__)
+ require File.expand_path('application', __dir__)
end
# Initialize the rails application
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 45a8c1add3e..23790b84e3c 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -39,7 +39,7 @@ Rails.application.configure do
config.action_mailer.delivery_method = :letter_opener_web
# Don't make a mess when bootstrapping a development environment
config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1')
- config.action_mailer.preview_path = 'spec/mailers/previews'
+ config.action_mailer.preview_path = 'app/mailers/previews'
config.eager_load = false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index e0779112850..ab109f5d04f 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -160,6 +160,9 @@ production: &base
# aws_access_key_id: AWS_ACCESS_KEY_ID
# aws_secret_access_key: AWS_SECRET_ACCESS_KEY
# region: us-east-1
+ # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
+ # endpoint: 'https://s3.amazonaws.com' # default: nil - Useful for S3 compliant services such as DigitalOcean Spaces
+
## Git LFS
lfs:
@@ -180,6 +183,7 @@ production: &base
# Use the following options to configure an AWS compatible host
# host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil
+ # aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
# path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object'
## Uploads (attachments, avatars, etc...)
@@ -197,6 +201,7 @@ production: &base
provider: AWS
aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
region: us-east-1
# host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 693a2934a1b..4b9cc59ec45 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -135,7 +135,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *, *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
diff --git a/config/initializers/action_mailer_hooks.rb b/config/initializers/action_mailer_hooks.rb
new file mode 100644
index 00000000000..f1b3c1f8ae8
--- /dev/null
+++ b/config/initializers/action_mailer_hooks.rb
@@ -0,0 +1,12 @@
+unless Gitlab.config.gitlab.email_enabled
+ ActionMailer::Base.register_interceptor(::Gitlab::Email::Hook::DisableEmailInterceptor)
+ ActionMailer::Base.logger = nil
+end
+
+ActionMailer::Base.register_interceptors(
+ ::Gitlab::Email::Hook::AdditionalHeadersInterceptor,
+ ::Gitlab::Email::Hook::EmailTemplateInterceptor,
+ ::Gitlab::Email::Hook::DeliveryMetricsObserver
+)
+
+ActionMailer::Base.register_observer(::Gitlab::Email::Hook::DeliveryMetricsObserver)
diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb
index 0861544c5a4..21ff323927b 100644
--- a/config/initializers/active_record_locking.rb
+++ b/config/initializers/active_record_locking.rb
@@ -17,7 +17,7 @@ module ActiveRecord
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
+ previous_lock_value = send(lock_col).to_i
# This line is added as a patch
previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
@@ -47,7 +47,7 @@ module ActiveRecord
# If something went wrong, revert the version.
rescue Exception
- send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
+ send(lock_col + '=', previous_lock_value)
raise
end
end
diff --git a/config/initializers/active_record_table_definition.rb b/config/initializers/active_record_table_definition.rb
index 8e3a1c7a62f..a71069f27a3 100644
--- a/config/initializers/active_record_table_definition.rb
+++ b/config/initializers/active_record_table_definition.rb
@@ -29,6 +29,11 @@ module ActiveRecord
def datetime_with_timezone(column_name, **options)
column(column_name, :datetime_with_timezone, options)
end
+
+ # Disable timestamp alias to datetime
+ def aliased_types(name, fallback)
+ fallback
+ end
end
end
end
diff --git a/config/initializers/additional_headers_interceptor.rb b/config/initializers/additional_headers_interceptor.rb
deleted file mode 100644
index b9159e7c06c..00000000000
--- a/config/initializers/additional_headers_interceptor.rb
+++ /dev/null
@@ -1 +0,0 @@
-ActionMailer::Base.register_interceptor(AdditionalEmailHeadersInterceptor)
diff --git a/config/initializers/disable_email_interceptor.rb b/config/initializers/disable_email_interceptor.rb
deleted file mode 100644
index e8770c8d460..00000000000
--- a/config/initializers/disable_email_interceptor.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# Interceptor in lib/disable_email_interceptor.rb
-unless Gitlab.config.gitlab.email_enabled
- ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
- ActionMailer::Base.logger = nil
-end
diff --git a/config/initializers/email_template_interceptor.rb b/config/initializers/email_template_interceptor.rb
deleted file mode 100644
index f195ca9bcd6..00000000000
--- a/config/initializers/email_template_interceptor.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-# Interceptor in lib/email_template_interceptor.rb
-ActionMailer::Base.register_interceptor(EmailTemplateInterceptor)
diff --git a/config/routes/import.rb b/config/routes/import.rb
index c378253bf15..efd0260ff60 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -45,4 +45,10 @@ namespace :import do
resource :gitlab_project, only: [:create, :new] do
post :create
end
+
+ resource :manifest, only: [:create, :new], controller: :manifest do
+ get :status
+ get :jobs
+ post :upload
+ end
end
diff --git a/danger/changelog/Dangerfile b/danger/changelog/Dangerfile
new file mode 100644
index 00000000000..a1f94dc6004
--- /dev/null
+++ b/danger/changelog/Dangerfile
@@ -0,0 +1,68 @@
+# rubocop:disable Style/SignalException
+
+require 'yaml'
+
+NO_CHANGELOG_LABELS = %w[backstage Documentation QA test].freeze
+SEE_DOC = "See [the documentation](https://docs.gitlab.com/ce/development/changelog.html).".freeze
+CREATE_CHANGELOG_MESSAGE = <<~MSG.freeze
+You can create one with:
+
+```
+bin/changelog -m %<mr_iid>s "%<mr_title>s"
+```
+
+If your merge request doesn't warrant a CHANGELOG entry,
+consider adding any of the %<labels>s labels.
+#{SEE_DOC}
+MSG
+
+def ee?
+ ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('../../CHANGELOG-EE.md')
+end
+
+def ee_changelog?(changelog_path)
+ changelog_path =~ /unreleased-ee/
+end
+
+def ce_port_changelog?(changelog_path)
+ ee? && !ee_changelog?(changelog_path)
+end
+
+def check_changelog(path)
+ yaml = YAML.safe_load(File.read(path))
+
+ fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
+ fail "`type` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["type"].nil?
+
+ if yaml["merge_request"].nil?
+ message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}"
+ elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !ce_port_changelog?(path)
+ fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
+ end
+rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
+ # YAML could not be parsed, fail the build.
+ fail "#{gitlab.html_link(path)} isn't valid YAML! #{SEE_DOC}"
+rescue StandardError => e
+ warn "There was a problem trying to check the Changelog. Exception: #{e.name} - #{e.message}"
+end
+
+def presented_no_changelog_labels
+ NO_CHANGELOG_LABELS.map { |label| "~#{label}" }.join(', ')
+end
+
+changelog_needed = (gitlab.mr_labels & NO_CHANGELOG_LABELS).empty?
+changelog_found = git.added_files.find { |path| path =~ %r{\A(ee/)?(changelogs/unreleased)(-ee)?/} }
+
+if git.modified_files.include?("CHANGELOG.md")
+ fail "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n" +
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
+end
+
+if changelog_needed
+ if changelog_found
+ check_changelog(changelog_found)
+ else
+ warn "**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**\n\n" +
+ format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: gitlab.mr_json["title"], labels: presented_no_changelog_labels)
+ end
+end
diff --git a/danger/changes_size/Dangerfile b/danger/changes_size/Dangerfile
new file mode 100644
index 00000000000..8251d0d5cbf
--- /dev/null
+++ b/danger/changes_size/Dangerfile
@@ -0,0 +1,17 @@
+# FIXME: git.info_for_file raises the following error
+# /usr/local/bundle/gems/git-1.4.0/lib/git/lib.rb:956:in `command': (Danger::DSLError)
+# [!] Invalid `Dangerfile` file:
+# [!] Invalid `Dangerfile` file: git '--git-dir=/builds/gitlab-org/gitlab-ce/.git' '--work-tree=/builds/gitlab-org/gitlab-ce' cat-file '-t' '' 2>&1:fatal: Not a valid object name
+# This seems to be the same as https://github.com/danger/danger/issues/535.
+
+# locale_files_updated = git.modified_files.select { |path| path.start_with?('locale') }
+# locale_files_updated.each do |locale_file_updated|
+# git_stats = git.info_for_file(locale_file_updated)
+# message "Git stats for #{locale_file_updated}: #{git_stats[:insertions]} insertions, #{git_stats[:deletions]} insertions"
+# end
+
+if git.lines_of_code > 2_000
+ warn "This merge request is definitely too big (more than #{git.lines_of_code} lines changed), please split it into multiple merge requests."
+elsif git.lines_of_code > 500
+ warn "This merge request is quite big (more than #{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests."
+end
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
new file mode 100644
index 00000000000..6f48994945a
--- /dev/null
+++ b/danger/database/Dangerfile
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+# All the files/directories that should be reviewed by the DB team.
+DB_FILES = [
+ 'db/',
+ 'app/models/project_authorization.rb',
+ 'app/services/users/refresh_authorized_projects_service.rb',
+ 'lib/gitlab/background_migration.rb',
+ 'lib/gitlab/background_migration/',
+ 'lib/gitlab/database.rb',
+ 'lib/gitlab/database/',
+ 'lib/gitlab/github_import.rb',
+ 'lib/gitlab/github_import/',
+ 'lib/gitlab/sql/',
+ 'rubocop/cop/migration',
+ 'ee/db/',
+ 'ee/lib/gitlab/database/'
+].freeze
+
+SCHEMA_NOT_UPDATED_MESSAGE = <<~MSG
+**New %<migrations>s added but %<schema>s wasn't updated.**
+
+Usually, when adding new %<migrations>s, %<schema>s should be
+updated too (unless the migration isn't changing the DB schema
+and isn't the most recent one).
+MSG
+
+def database_paths_requiring_review(files)
+ to_review = []
+
+ files.each do |file|
+ review = DB_FILES.any? do |pattern|
+ file.start_with?(pattern)
+ end
+
+ to_review << file if review
+ end
+
+ to_review
+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_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?
+
+if non_geo_migration_created && !non_geo_db_schema_updated
+ warn format(SCHEMA_NOT_UPDATED_MESSAGE, migrations: 'migrations', schema: gitlab.html_link("db/schema.rb"))
+end
+
+if geo_migration_created && !geo_db_schema_updated
+ warn format(SCHEMA_NOT_UPDATED_MESSAGE, migrations: 'Geo migrations', schema: gitlab.html_link("ee/db/geo/schema.rb"))
+end
+
+db_paths_to_review = database_paths_requiring_review(all_files)
+
+unless db_paths_to_review.empty?
+ message 'This merge request adds or changes files that require a ' \
+ 'review from the Database team.'
+
+ markdown(<<~MARKDOWN.strip)
+## Database Review
+
+The following files require a review from the Database team:
+
+* #{db_paths_to_review.map { |path| "`#{path}`" }.join("\n* ")}
+
+To make sure these changes are reviewed, take the following steps:
+
+1. Edit your merge request, and add `gl-database` to the list of Group
+ approvers.
+1. Mention `@gl-database` in a separate comment, and explain what needs to be
+ reviewed by the team. Please don't mention the team until your changes are
+ ready for review.
+ MARKDOWN
+
+ unless gitlab.mr_labels.include?('database')
+ warn 'This merge request is missing the ~database label.'
+ end
+end
diff --git a/danger/gemfile/Dangerfile b/danger/gemfile/Dangerfile
new file mode 100644
index 00000000000..8ef4a464fe4
--- /dev/null
+++ b/danger/gemfile/Dangerfile
@@ -0,0 +1,24 @@
+GEMFILE_LOCK_NOT_UPDATED_MESSAGE = <<~MSG.freeze
+**%<gemfile>s was updated but %<gemfile_lock>s wasn't updated.**
+
+Usually, when %<gemfile>s is updated, you should run
+```
+bundle install && \
+ BUNDLE_GEMFILE=Gemfile.rails5 bundle install
+```
+
+or
+
+```
+bundle update <the-added-or-updated-gem>
+```
+
+and commit the %<gemfile_lock>s changes.
+MSG
+
+gemfile_modified = git.modified_files.include?("Gemfile")
+gemfile_lock_modified = git.modified_files.include?("Gemfile.lock")
+
+if gemfile_modified && !gemfile_lock_modified
+ warn format(GEMFILE_LOCK_NOT_UPDATED_MESSAGE, gemfile: gitlab.html_link("Gemfile"), gemfile_lock: gitlab.html_link("Gemfile.lock"))
+end
diff --git a/danger/metadata/Dangerfile b/danger/metadata/Dangerfile
new file mode 100644
index 00000000000..3cfaa04e01b
--- /dev/null
+++ b/danger/metadata/Dangerfile
@@ -0,0 +1,25 @@
+# rubocop:disable Style/SignalException
+
+if gitlab.mr_body.size < 5
+ fail "Please provide a proper merge request description."
+end
+
+if gitlab.mr_labels.empty?
+ fail "Please add labels to this merge request."
+end
+
+unless gitlab.mr_json["assignee"]
+ warn "This merge request does not have any assignee yet. Setting an assignee clarifies who needs to take action on the merge request at any given time."
+end
+
+has_milestone = !gitlab.mr_json["milestone"].nil?
+
+unless has_milestone
+ warn "This merge request does not refer to an existing milestone.", sticky: false
+end
+
+has_pick_into_stable_label = gitlab.mr_labels.find { |label| label.start_with?('Pick into') }
+
+if gitlab.branch_for_base != "master" && !has_pick_into_stable_label
+ warn "Most of the time, all merge requests should target `master`. Otherwise, please set the relevant `Pick into X.Y` label."
+end
diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile
new file mode 100644
index 00000000000..97188df8785
--- /dev/null
+++ b/danger/specs/Dangerfile
@@ -0,0 +1,18 @@
+NO_SPECS_LABELS = %w[backstage Documentation QA].freeze
+NO_NEW_SPEC_MESSAGE = <<~MSG.freeze
+You've made some app changes, but didn't add any tests.
+That's OK as long as you're refactoring existing code,
+but please consider adding any of the %<labels>s labels.
+MSG
+
+def presented_no_changelog_labels
+ NO_SPECS_LABELS.map { |label| "~#{label}" }.join(', ')
+end
+
+has_app_changes = !git.modified_files.grep(%r{\A(ee/)?(app|lib|db/(geo/)?(post_)?migrate)/}).empty?
+has_spec_changes = !git.modified_files.grep(%r{\A(ee/)?spec/}).empty?
+new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty?
+
+if has_app_changes && !has_spec_changes && new_specs_needed
+ warn format(NO_NEW_SPEC_MESSAGE, labels: presented_no_changelog_labels), sticky: false
+end
diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb
index 4f3bdba043d..a9f4069a0f8 100644
--- a/db/fixtures/development/12_snippets.rb
+++ b/db/fixtures/development/12_snippets.rb
@@ -17,7 +17,7 @@ class Member < ActiveRecord::Base
scope :guests, -> { where(access_level: GUEST) }
scope :reporters, -> { where(access_level: REPORTER) }
scope :developers, -> { where(access_level: DEVELOPER) }
- scope :masters, -> { where(access_level: MASTER) }
+ scope :maintainers, -> { where(access_level: MAINTAINER) }
scope :owners, -> { where(access_level: OWNER) }
delegate :name, :username, :email, to: :user, prefix: true
diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb
index 00a14f458d1..3e227928a29 100644
--- a/db/fixtures/development/19_environments.rb
+++ b/db/fixtures/development/19_environments.rb
@@ -30,14 +30,14 @@ class Gitlab::Seeder::Environments
def create_merge_request_review_deployments!
@project
.merge_requests
- .select { |mr| mr.source_branch.match(/\p{Alnum}+/) }
+ .select { |mr| mr.source_branch.match?(/[a-zA-Z0-9]+/) }
.sample(4)
.each do |merge_request|
next unless merge_request.diff_head_sha
create_deployment!(
merge_request.source_project,
- "review/#{merge_request.source_branch.gsub(/[^a-zA-Z0-9]/, '')}",
+ "review/#{merge_request.source_branch.gsub(/[^a-zA-Z0-9]+/, '')}",
merge_request.source_branch,
merge_request.diff_head_sha
)
diff --git a/db/migrate/20160705054938_add_protected_branches_push_access.rb b/db/migrate/20160705054938_add_protected_branches_push_access.rb
index 97aaaf9d2c8..de3aefcb1fb 100644
--- a/db/migrate/20160705054938_add_protected_branches_push_access.rb
+++ b/db/migrate/20160705054938_add_protected_branches_push_access.rb
@@ -9,7 +9,7 @@ class AddProtectedBranchesPushAccess < ActiveRecord::Migration
create_table :protected_branch_push_access_levels do |t|
t.references :protected_branch, index: { name: "index_protected_branch_push_access" }, foreign_key: true, null: false
- # Gitlab::Access::MASTER == 40
+ # Gitlab::Access::MAINTAINER == 40
t.integer :access_level, default: 40, null: false
t.timestamps null: false
diff --git a/db/migrate/20160705054952_add_protected_branches_merge_access.rb b/db/migrate/20160705054952_add_protected_branches_merge_access.rb
index 51a52a5ac17..9b18a2061b3 100644
--- a/db/migrate/20160705054952_add_protected_branches_merge_access.rb
+++ b/db/migrate/20160705054952_add_protected_branches_merge_access.rb
@@ -9,7 +9,7 @@ class AddProtectedBranchesMergeAccess < ActiveRecord::Migration
create_table :protected_branch_merge_access_levels do |t|
t.references :protected_branch, index: { name: "index_protected_branch_merge_access" }, foreign_key: true, null: false
- # Gitlab::Access::MASTER == 40
+ # Gitlab::Access::MAINTAINER == 40
t.integer :access_level, default: 40, null: false
t.timestamps null: false
diff --git a/db/migrate/20180608091413_add_group_to_todos.rb b/db/migrate/20180608091413_add_group_to_todos.rb
deleted file mode 100644
index af3ee48b29d..00000000000
--- a/db/migrate/20180608091413_add_group_to_todos.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class AddGroupToTodos < ActiveRecord::Migration
- include Gitlab::Database::MigrationHelpers
-
- DOWNTIME = false
-
- disable_ddl_transaction!
-
- def up
- add_column :todos, :group_id, :integer
- add_concurrent_foreign_key :todos, :namespaces, column: :group_id, on_delete: :cascade
- add_concurrent_index :todos, :group_id
-
- change_column_null :todos, :project_id, true
- end
-
- def down
- return unless group_id_exists?
-
- remove_foreign_key :todos, column: :group_id
- remove_index :todos, :group_id if index_exists?(:todos, :group_id)
- remove_column :todos, :group_id
-
- execute "DELETE FROM todos WHERE project_id IS NULL"
- change_column_null :todos, :project_id, false
- end
-
- private
-
- def group_id_exists?
- column_exists?(:todos, :group_id)
- end
-end
diff --git a/db/migrate/20180625113853_create_import_export_uploads.rb b/db/migrate/20180625113853_create_import_export_uploads.rb
new file mode 100644
index 00000000000..be42304b0ae
--- /dev/null
+++ b/db/migrate/20180625113853_create_import_export_uploads.rb
@@ -0,0 +1,16 @@
+class CreateImportExportUploads < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :import_export_uploads do |t|
+ t.datetime_with_timezone :updated_at, null: false
+
+ t.references :project, index: true, foreign_key: { on_delete: :cascade }, unique: true
+
+ t.text :import_file
+ t.text :export_file
+ end
+
+ add_index :import_export_uploads, :updated_at
+ end
+end
diff --git a/db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb b/db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb
new file mode 100644
index 00000000000..6631c5d1b6c
--- /dev/null
+++ b/db/migrate/20180704204006_add_hide_third_party_offers_to_application_settings.rb
@@ -0,0 +1,18 @@
+class AddHideThirdPartyOffersToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:application_settings, :hide_third_party_offers,
+ :boolean,
+ default: false,
+ allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :hide_third_party_offers)
+ end
+end
diff --git a/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb b/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb
new file mode 100644
index 00000000000..c4d2f5f61a0
--- /dev/null
+++ b/db/post_migrate/20180619121030_enqueue_delete_diff_files_workers.rb
@@ -0,0 +1,26 @@
+class EnqueueDeleteDiffFilesWorkers < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ SCHEDULER = 'ScheduleDiffFilesDeletion'.freeze
+ TMP_INDEX = 'tmp_partial_diff_id_with_files_index'.freeze
+
+ disable_ddl_transaction!
+
+ def up
+ unless index_exists_by_name?(:merge_request_diffs, TMP_INDEX)
+ add_concurrent_index(:merge_request_diffs, :id, where: "(state NOT IN ('without_files', 'empty'))", name: TMP_INDEX)
+ end
+
+ BackgroundMigrationWorker.perform_async(SCHEDULER)
+
+ # We don't remove the index since it's going to be used on DeleteDiffFiles
+ # worker. We should remove it in an upcoming release.
+ end
+
+ def down
+ if index_exists_by_name?(:merge_request_diffs, TMP_INDEX)
+ remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 8880ecf4f5c..d2aa31fae30 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180702120647) do
+ActiveRecord::Schema.define(version: 20180704204006) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -167,6 +167,7 @@ ActiveRecord::Schema.define(version: 20180702120647) do
t.boolean "allow_local_requests_from_hooks_and_services", default: false, null: false
t.boolean "enforce_terms", default: false
t.boolean "mirror_available", default: true, null: false
+ t.boolean "hide_third_party_offers", default: false, null: false
end
create_table "audit_events", force: :cascade do |t|
@@ -949,6 +950,16 @@ ActiveRecord::Schema.define(version: 20180702120647) do
add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
+ create_table "import_export_uploads", force: :cascade do |t|
+ t.datetime_with_timezone "updated_at", null: false
+ t.integer "project_id"
+ t.text "import_file"
+ t.text "export_file"
+ end
+
+ add_index "import_export_uploads", ["project_id"], name: "index_import_export_uploads_on_project_id", using: :btree
+ add_index "import_export_uploads", ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree
+
create_table "internal_ids", id: :bigserial, force: :cascade do |t|
t.integer "project_id"
t.integer "usage", null: false
@@ -1939,7 +1950,7 @@ ActiveRecord::Schema.define(version: 20180702120647) do
create_table "todos", force: :cascade do |t|
t.integer "user_id", null: false
- t.integer "project_id"
+ t.integer "project_id", null: false
t.integer "target_id"
t.string "target_type", null: false
t.integer "author_id", null: false
@@ -1949,12 +1960,10 @@ ActiveRecord::Schema.define(version: 20180702120647) do
t.datetime "updated_at"
t.integer "note_id"
t.string "commit_id"
- t.integer "group_id"
end
add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree
add_index "todos", ["commit_id"], name: "index_todos_on_commit_id", using: :btree
- add_index "todos", ["group_id"], name: "index_todos_on_group_id", using: :btree
add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree
add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree
add_index "todos", ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
@@ -2252,6 +2261,7 @@ ActiveRecord::Schema.define(version: 20180702120647) do
add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade
add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade
+ add_foreign_key "import_export_uploads", "projects", on_delete: :cascade
add_foreign_key "internal_ids", "namespaces", name: "fk_162941d509", on_delete: :cascade
add_foreign_key "internal_ids", "projects", on_delete: :cascade
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
@@ -2327,7 +2337,6 @@ ActiveRecord::Schema.define(version: 20180702120647) do
add_foreign_key "term_agreements", "users", on_delete: :cascade
add_foreign_key "timelogs", "issues", name: "fk_timelogs_issues_issue_id", on_delete: :cascade
add_foreign_key "timelogs", "merge_requests", name: "fk_timelogs_merge_requests_merge_request_id", on_delete: :cascade
- add_foreign_key "todos", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "todos", "notes", name: "fk_91d1f47b13", on_delete: :cascade
add_foreign_key "todos", "projects", name: "fk_45054f9c45", on_delete: :cascade
add_foreign_key "todos", "users", column: "author_id", name: "fk_ccf0373936", on_delete: :cascade
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 922cc45d8c4..88190b2df5f 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -45,6 +45,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Environment variables](environment_variables.md): Supported environment variables that can be used to override their defaults values in order to configure GitLab.
- [Plugins](plugins.md): With custom plugins, GitLab administrators can introduce custom integrations without modifying GitLab's source code.
- [Enforcing Terms of Service](../user/admin_area/settings/terms.md)
+- [Third party offers](../user/admin_area/settings/third_party_offers.md)
#### Customizing GitLab's appearance
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 10228d027e8..8c55c8c4298 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -88,13 +88,12 @@ _The artifacts are stored by default in
### Using object storage
>**Notes:**
-- [Introduced][ee-1762] in [GitLab Premium][eep] 9.4.
-- Since version 9.5, artifacts are [browsable], when object storage is enabled.
- 9.4 lacks this feature.
-> Available in [GitLab Premium](https://about.gitlab.com/pricing/) and
-[GitLab.com Silver](https://about.gitlab.com/gitlab-com/).
-> Since version 10.6, available in [GitLab CE](https://about.gitlab.com/pricing/)
-> Since version 11.0, we support direct_upload to S3.
+- [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in
+ [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
+- Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts),
+ when object storage is enabled. 9.4 lacks this feature.
+- Since version 10.6, available in [GitLab Core](https://about.gitlab.com/pricing/)
+- Since version 11.0, we support `direct_upload` to S3.
If you don't want to use the local disk where GitLab is installed to store the
artifacts, you can use an object storage like AWS S3 instead.
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index f1c5b194f4c..24d1a3fd151 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -77,10 +77,10 @@ cloud-native, for example on Kubernetes.
The data flow is the same as described in the [data flow section](#data-flow)
with one change: _the stored path of the first two phases is different_. This new live
-trace architecture stores chunks of traces in Redis and the database instead of
+trace architecture stores chunks of traces in Redis and a persistent store (object storage or database) instead of
file storage. Redis is used as first-class storage, and it stores up-to 128KB
-of data. Once the full chunk is sent, it is flushed to database. After a while,
-the data in Redis and database will be archived to [object storage](#uploading-traces-to-object-storage).
+of data. Once the full chunk is sent, it is flushed a persistent store, either object storage(temporary directory) or database.
+After a while, the data in Redis and a persitent store will be archived to [object storage](#uploading-traces-to-object-storage).
The data are stored in the following Redis namespace: `Gitlab::Redis::SharedState`.
@@ -89,11 +89,11 @@ Here is the detailed data flow:
1. GitLab Runner picks a job from GitLab
1. GitLab Runner sends a piece of trace to GitLab
1. GitLab appends the data to Redis
-1. Once the data in Redis reach 128KB, the data is flushed to the database.
+1. Once the data in Redis reach 128KB, the data is flushed to a persistent store (object storage or the database).
1. The above steps are repeated until the job is finished.
1. Once the job is finished, GitLab schedules a Sidekiq worker to archive the trace.
1. The Sidekiq worker archives the trace to object storage and cleans up the trace
- in Redis and the database.
+ in Redis and a persistent store (object storage or the database).
### Enabling live trace
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 9b1297ca4ba..b3602bc35ab 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -49,8 +49,8 @@ supporting custom domains a secondary IP is not needed.
Before proceeding with the Pages configuration, you will need to:
-1. Have a separate domain under which the GitLab Pages will be served. In this
- document we assume that to be `example.io`.
+1. Have an exclusive root domain for serving GitLab Pages. Note that you cannot
+ use a subdomain of your GitLab's instance domain.
1. Configure a **wildcard DNS record**.
1. (Optional) Have a **wildcard certificate** for that domain if you decide to
serve Pages under HTTPS.
@@ -259,6 +259,24 @@ verification requirement. Navigate to `Admin area ➔ Settings` and uncheck
**Require users to prove ownership of custom domains** in the Pages section.
This setting is enabled by default.
+## Activate verbose logging for daemon
+
+Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in
+Omnibus GitLab 11.1.
+
+Follow the steps below to configure verbose logging of GitLab Pages daemon.
+
+1. By default the daemon only logs with `INFO` level.
+
+ If you wish to make it log events with level `DEBUG` you must configure this in
+ `/etc/gitlab/gitlab.rb`:
+
+ ```shell
+ gitlab_pages['log_verbose'] = true
+ ```
+
+1. [Reconfigure GitLab][reconfigure]
+
## Change storage path
Follow the steps below to change the default path where GitLab Pages' contents
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index ecc4ac6b29b..7bd765a35e0 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -30,5 +30,12 @@ sudo gitlab-rake gitlab:import_export:data
bundle exec rake gitlab:import_export:data RAILS_ENV=production
```
+In order to enable Object Storage on the Export, you can use the [feature flag][feature-flags]:
+
+```
+import_export_object_storage
+```
+
[ce-3050]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3050
+[feature-flags]: https://docs.gitlab.com/ee/api/features.html
[tmp]: ../../development/shared_files.md
diff --git a/doc/api/README.md b/doc/api/README.md
index 6267618d3bc..4566319ad45 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -388,7 +388,7 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/diaspora%2Fdiaspora
```
-## Branches & tags name encoding
+## Branches and tags name encoding
If your branch or tag contains a `/`, make sure the branch/tag name is
URL-encoded.
@@ -399,6 +399,36 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/1/branches/my%2Fbranch/commits
```
+## Encoding API parameters of `array` and `hash` types
+
+When making an API call with parameters of type `array` and/or `hash`, the parameters may be
+specified as shown below.
+
+### `array`
+
+`import_sources` is a parameter of type `array`:
+
+```
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
+-d "import_sources[]=github" \
+-d "import_sources[]=bitbucket" \
+"https://gitlab.example.com/api/v4/some_endpoint
+```
+
+### `hash`
+
+`override_params` is a parameter of type `hash`:
+
+```
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
+--form "namespace=email" \
+--form "path=impapi" \
+--form "file=@/path/to/somefile.txt"
+--form "override_params[visibility]=private" \
+--form "override_params[some_other_param]=some_value" \
+https://gitlab.example.com/api/v4/projects/import
+```
+
## `id` vs `iid`
When you work with the API, you may notice two similar fields in API entities:
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 53d72509423..11de75039ee 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -210,6 +210,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
+| `with_projects` | boolean | no | Include details from projects that belong to the specified group (defaults to `true`). |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4
@@ -361,6 +362,30 @@ Example response:
}
```
+When adding the parameter `with_projects=false`, projects will not be returned.
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4?with_projects=false
+```
+
+Example response:
+
+```json
+{
+ "id": 4,
+ "name": "Twitter",
+ "path": "twitter",
+ "description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
+ "visibility": "public",
+ "avatar_url": null,
+ "web_url": "https://gitlab.example.com/groups/twitter",
+ "request_access_enabled": false,
+ "full_name": "Twitter",
+ "full_path": "twitter",
+ "parent_id": null
+}
+```
+
## New group
Creates a new project group. Available only for users who can create groups.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 2057ed3588a..34c2dd7b34d 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -358,6 +358,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
+- `render_html` (optional) - If `true` response includes rendered HTML for title and description
```json
{
diff --git a/doc/api/notes.md b/doc/api/notes.md
index d29c5b94915..c271d46688f 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -218,6 +218,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `snippet_id` (required) - The ID of a snippet
- `body` (required) - The content of a note
+- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippet/11/notes?body=note
@@ -340,6 +341,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `merge_request_iid` (required) - The IID of a merge request
- `body` (required) - The content of a note
+- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
### Modify existing merge request note
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index ebae68fe389..22cf9afbcd2 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -151,7 +151,7 @@ POST /projects/:id/pipelines/:pipeline_id/retry
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/retry"
```
Response:
@@ -197,7 +197,7 @@ POST /projects/:id/pipelines/:pipeline_id/cancel
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines/46/cancel"
```
Response:
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index 085437c801a..83e405141f1 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -28,8 +28,11 @@ POST /projects/:id/export
| `upload[url]` | string | yes | The URL to upload the project |
| `upload[http_method]` | string | no | The HTTP method to upload the exported project. Only `PUT` and `POST` methods allowed. Default is `PUT` |
+
```console
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/export --data "description=FooBar&upload[http_method]=PUT&upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/export \
+ --data "upload[http_method]=PUT" \
+ --data-urlencode "upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
```
```json
@@ -125,6 +128,29 @@ by `@`. For example:
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import
```
+cURL doesn't support posting a file from a remote server. Importing a project from a remote server can be accomplished through something like the following:
+
+```python
+import requests
+import urllib
+import json
+import sys
+
+s3_file = urllib.urlopen(presigned_url)
+
+url = 'https://gitlab.example.com/api/v4/projects/import'
+files = {'file': s3_file}
+data = {
+ "path": "example-project",
+ "namespace": "example-group"
+}
+headers = {
+ 'Private-Token': "9koXpg98eAheJpvBs5tK"
+}
+
+requests.post(url, headers=headers, data=data, files=files)
+```
+
```json
{
"id": 1,
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 1e06f6d01f3..a35c2a56992 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -34,7 +34,7 @@ There are currently three options for `merge_method` to choose from:
## List all projects
Get a list of all visible projects across GitLab for the authenticated user.
-When accessed without authentication, only public projects are returned.
+When accessed without authentication, only public projects with "simple" fields are returned.
```
GET /projects
@@ -47,7 +47,7 @@ GET /projects
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
+| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `owned` | boolean | no | Limit by projects owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
@@ -56,6 +56,41 @@ GET /projects
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+When `simple=true` or the user is unauthenticated this returns something like:
+
+```json
+[
+ {
+ "id": 4,
+ "description": null,
+ "default_branch": "master",
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
+ "web_url": "http://example.com/diaspora/diaspora-client",
+ "readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",
+ "tag_list": [
+ "example",
+ "disapora client"
+ ],
+ "name": "Diaspora Client",
+ "name_with_namespace": "Diaspora / Diaspora Client",
+ "path": "diaspora-client",
+ "path_with_namespace": "diaspora/diaspora-client",
+ "created_at": "2013-09-30T13:46:02Z",
+ "last_activity_at": "2013-09-30T13:46:02Z",
+ "forks_count": 0,
+ "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
+ "star_count": 0,
+ },
+ {
+ "id": 6,
+ "description": null,
+ "default_branch": "master",
+...
+```
+
+When the user is authenticated and `simple` is not set this returns something like:
+
```json
[
{
@@ -235,7 +270,7 @@ GET /users/:user_id/projects
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
+| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `owned` | boolean | no | Limit by projects owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
@@ -723,7 +758,7 @@ GET /projects/:id/forks
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
+| `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
| `owned` | boolean | no | Limit by projects owned by the current user |
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
diff --git a/doc/api/settings.md b/doc/api/settings.md
index e6b207d8746..b6f2101fc7b 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -105,7 +105,7 @@ PUT /application/settings
| `housekeeping_gc_period` | integer | no | Number of Git pushes after which 'git gc' is run. |
| `housekeeping_incremental_repack_period` | integer | no | Number of Git pushes after which an incremental 'git repack' is run. |
| `html_emails_enabled` | boolean | no | Enable HTML emails |
-| `import_sources` | Array of strings | no | Sources to allow project import from, possible values: "github bitbucket gitlab google_code fogbugz git gitlab_project |
+| `import_sources` | Array of strings | no | Sources to allow project import from, possible values: "github bitbucket gitlab google_code fogbugz git gitlab_project manifest |
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB |
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 0843e4eedc6..27e623007cc 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -18,7 +18,6 @@ Parameters:
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, `approval_required`, `unmergeable` or `directly_addressed`. |
| `author_id` | integer | no | The ID of an author |
| `project_id` | integer | no | The ID of a project |
-| `group_id` | integer | no | The ID of a group |
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
| `type` | string | no | The type of a todo. Can be either `Issue` or `MergeRequest` |
diff --git a/doc/api/users.md b/doc/api/users.md
index ca5afa04687..72fdaaa2c74 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -33,6 +33,20 @@ GET /users
]
```
+You can also search for users by email or username with: `/users?search=John`
+
+In addition, you can lookup users by username:
+
+```
+GET /users?username=:username
+```
+
+For example:
+
+```
+GET /users?username=jack_smith
+```
+
In addition, you can filter users based on states eg. `blocked`, `active`
This works only to filter users who are `blocked` or `active`.
It does not support `active=false` or `blocked=false`.
@@ -126,21 +140,7 @@ GET /users
]
```
-You can search for users by email or username with: `/users?search=John`
-
-In addition, you can lookup users by username:
-
-```
-GET /users?username=:username
-```
-
-For example:
-
-```
-GET /users?username=jack_smith
-```
-
-You can also lookup users by external UID and provider:
+You can lookup users by external UID and provider:
```
GET /users?extern_uid=:extern_uid&provider=:provider
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index aa31e172641..811f4d1f07a 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -43,9 +43,9 @@ There's also a collection of repositories with [example projects](https://gitlab
- [Using `dpl` as deployment tool](deployment/README.md)
- [The `.gitlab-ci.yml` file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
-## Code quality analysis
+## Code Quality analysis
-[Analyze code quality with the Code Climate CLI](code_climate.md).
+**(Starter)** [Analyze your project's Code Quality](code_quality.md).
## Static Application Security Testing (SAST)
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index 2c8865c6e50..b34637efc8d 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -1,49 +1,6 @@
-# Analyze project code quality with Code Climate CLI
+---
+redirect_from: 'https://docs.gitlab.com/ee/ci/examples/code_climate.html'
+redirect_to: code_quality.md
+---
-This example shows how to run [Code Climate CLI][cli] on your code by using
-GitLab CI and Docker.
-
-First, you need GitLab Runner with [docker-in-docker executor][dind].
-
-Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`:
-
-```yaml
-code_quality:
- image: docker:stable
- variables:
- DOCKER_DRIVER: overlay2
- allow_failure: true
- services:
- - docker:stable-dind
- script:
- - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- - docker run
- --env SOURCE_CODE="$PWD"
- --volume "$PWD":/code
- --volume /var/run/docker.sock:/var/run/docker.sock
- "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
- artifacts:
- paths: [gl-code-quality-report.json]
-```
-
-The above example will create a `code_quality` job in your CI/CD pipeline which
-will scan your source code for code quality issues. The report will be saved
-as an artifact that you can later download and analyze.
-
-TIP: **Tip:**
-Starting with [GitLab Starter][ee] 9.3, this information will
-be automatically extracted and shown right in the merge request widget. To do
-so, the CI/CD job must be named `code_quality` and the artifact path must be
-`gl-code-quality-report.json`.
-[Learn more on code quality diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
-
-CAUTION: **Caution:**
-Code Quality was previously using `codeclimate` and `codequality` for job name and
-`codeclimate.json` for the artifact name. While these old names
-are still maintained they have been deprecated with GitLab 11.0 and may be removed
-in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
-configuration to reflect that change.
-
-[cli]: https://github.com/codeclimate/codeclimate
-[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
-[ee]: https://about.gitlab.com/pricing/
+This document was moved to [another location](code_quality.md).
diff --git a/doc/ci/examples/code_quality.md b/doc/ci/examples/code_quality.md
new file mode 100644
index 00000000000..2a7040ecdeb
--- /dev/null
+++ b/doc/ci/examples/code_quality.md
@@ -0,0 +1,49 @@
+# Analyze your project's Code Quality
+
+This example shows how to run Code Quality on your code by using GitLab CI/CD
+and Docker.
+
+First, you need GitLab Runner with [docker-in-docker executor][dind].
+
+Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `code_quality`:
+
+```yaml
+code_quality:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env SOURCE_CODE="$PWD"
+ --volume "$PWD":/code
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
+ artifacts:
+ paths: [gl-code-quality-report.json]
+```
+
+The above example will create a `code_quality` job in your CI/CD pipeline which
+will scan your source code for code quality issues. The report will be saved
+as an artifact that you can later download and analyze.
+
+TIP: **Tip:**
+Starting with [GitLab Starter][ee] 9.3, this information will
+be automatically extracted and shown right in the merge request widget. To do
+so, the CI/CD job must be named `code_quality` and the artifact path must be
+`gl-code-quality-report.json`.
+[Learn more on Code Quality in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
+
+CAUTION: **Caution:**
+Code Quality was previously using `codeclimate` and `codequality` for job name and
+`codeclimate.json` for the artifact name. While these old names
+are still maintained they have been deprecated with GitLab 11.0 and may be removed
+in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
+configuration to reflect that change.
+
+[cli]: https://github.com/codeclimate/codeclimate
+[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
+[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 2f991d86614..84bd64d50cd 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -47,6 +47,7 @@ future GitLab releases.**
| **CI_COMMIT_REF_NAME** | 9.0 | all | The branch or tag name for which project is built |
| **CI_COMMIT_REF_SLUG** | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in URLs, host names and domain names. |
| **CI_COMMIT_SHA** | 9.0 | all | The commit revision for which project is built |
+| **CI_COMMIT_BEFORE_SHA** | 11.2 | all | The previous latest commit present on a branch before a push request. |
| **CI_COMMIT_TAG** | 9.0 | 0.5 | The commit tag name. Present only when building tags. |
| **CI_COMMIT_MESSAGE** | 10.8 | all | The full commit message. |
| **CI_COMMIT_TITLE** | 10.8 | all | The title of the commit - the full first line of the message |
@@ -64,7 +65,7 @@ future GitLab releases.**
| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry |
-| **CI_JOB_URL** | 11.0 | 0.5 | Job details URL |
+| **CI_JOB_URL** | 11.1 | 0.5 | Job details URL |
| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
@@ -82,7 +83,7 @@ future GitLab releases.**
| **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built |
| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name |
| **CI_PROJECT_PATH_SLUG** | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
-| **CI_PIPELINE_URL** | 11.0 | 0.5 | Pipeline details URL |
+| **CI_PIPELINE_URL** | 11.1 | 0.5 | Pipeline details URL |
| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project |
| **CI_PROJECT_VISIBILITY** | 10.3 | all | The project visibility (internal, private, public) |
| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
@@ -118,6 +119,7 @@ future GitLab releases.**
| `CI_BUILD_ID` | `CI_JOB_ID` |
| `CI_BUILD_REF` | `CI_COMMIT_SHA` |
| `CI_BUILD_TAG` | `CI_COMMIT_TAG` |
+| `CI_BUILD_BEFORE_SHA` | `CI_COMMIT_BEFORE_SHA` |
| `CI_BUILD_REF_NAME` | `CI_COMMIT_REF_NAME` |
| `CI_BUILD_REF_SLUG` | `CI_COMMIT_REF_SLUG` |
| `CI_BUILD_NAME` | `CI_JOB_NAME` |
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 6ca3e9e5a0a..3e417a44ec1 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -197,4 +197,4 @@ Note: It is recommended to log into the `git` user using `sudo -i -u git` or `su
## GitLab.com
-We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/handbook/infrastructure/production-architecture/) but this is probably over the top unless you have millions of users.
+We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/) but this is probably over the top unless you have millions of users.
diff --git a/doc/development/emails.md b/doc/development/emails.md
index 73cac82caf0..35ada35babe 100644
--- a/doc/development/emails.md
+++ b/doc/development/emails.md
@@ -10,12 +10,12 @@ To view rendered emails "sent" in your development instance, visit
Rails provides a way to preview our mailer templates in HTML and plaintext using
dummy data.
-The previews live in [`spec/mailers/previews`][previews] and can be viewed at
+The previews live in [`app/mailers/previews`][previews] and can be viewed at
[`/rails/mailers`](http://localhost:3000/rails/mailers).
See the [Rails guides] for more info.
-[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/spec/mailers/previews
+[previews]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/mailers/previews
[Rails guides]: http://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails
## Incoming email
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index d240dbe8b02..905668eef58 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -1,6 +1,6 @@
# Frontend Development Process
-You can find more about the organization of the frontend team in the [handbook](https://about.gitlab.com/handbook/frontend/).
+You can find more about the organization of the frontend team in the [handbook](https://about.gitlab.com/handbook/engineering/frontend/).
## Development Checklist
@@ -34,7 +34,7 @@ Please use your best judgement when to use it and please contribute new points t
- [ ] **Cookie Mode** Think about hiding the feature behind a cookie flag if the implementation is on top of existing features
- [ ] **New route** Are you refactoring something big then you might consider adding a new route where you implement the new feature and when finished delete the current route and rename the new one. (for example 'merge_request' and 'new_merge_request')
- [ ] **Setup** Is there any specific setup needed for your implementation (for example a kubernetes cluster)? Then let everyone know if it is not already mentioned where they can find documentation (if it doesn't exist - create it)
-- [ ] **Security** Are there any new security relevant implementations? Then please contact the security team for an app security review. If you are not sure ask our [domain expert](https://about.gitlab.com/handbook/frontend/#frontend-domain-experts)
+- [ ] **Security** Are there any new security relevant implementations? Then please contact the security team for an app security review. If you are not sure ask our [domain expert](https://about.gitlab.com/handbook/engineering/frontend/#frontend-domain-experts)
#### During development
@@ -51,7 +51,7 @@ Please use your best judgement when to use it and please contribute new points t
- [ ] **Performance** Have you checked performance? For example do the same thing with 500 comments instead of 1. Document the tests and possible findings in the MR so a reviewer can directly see it.
- [ ] Have you tested with a variety of our [supported browsers](../../install/requirements.md#supported-web-browsers)? You can use [browserstack](https://www.browserstack.com/) to be able to access a wide variety of browsers and operating systems.
- [ ] Did you check the mobile view?
-- [ ] Check the built webpack bundle (For the report run `WEBPACK_REPORT=true gdk run`, then open `webpack-report/index.html`) if we have unnecessary bloat due to wrong references, including libraries multiple times, etc.. If you need help contact the webpack [domain expert](https://about.gitlab.com/handbook/frontend/#frontend-domain-experts)
+- [ ] Check the built webpack bundle (For the report run `WEBPACK_REPORT=true gdk run`, then open `webpack-report/index.html`) if we have unnecessary bloat due to wrong references, including libraries multiple times, etc.. If you need help contact the webpack [domain expert](https://about.gitlab.com/handbook/engineering/frontend/#frontend-domain-experts)
- [ ] **Tests** Not only greenfield tests - Test also all bad cases that come to your mind.
- [ ] If you have multiple MR's then also smoke test against the final merge.
- [ ] Are there any big changes on how and especially how frequently we use the API then let production know about it
diff --git a/doc/development/fe_guide/img/vue_arch.png b/doc/development/fe_guide/img/vue_arch.png
deleted file mode 100644
index a67706c7c1e..00000000000
--- a/doc/development/fe_guide/img/vue_arch.png
+++ /dev/null
Binary files differ
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index e31ee087358..f6cbd11042c 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -2,27 +2,24 @@
To get started with Vue, read through [their documentation][vue-docs].
-## Vue architecture
+## Examples
-All new features built with Vue.js must follow a [Flux architecture][flux].
-The main goal we are trying to achieve is to have only one data flow and only one data entry.
-In order to achieve this goal, you can either use [vuex](#vuex) or use the [store pattern][state-management], explained below:
+What is described in the following sections can be found in these examples:
-Each Vue bundle needs a Store - where we keep all the data -, a Service - that we use to communicate with the server - and a main Vue component.
+- web ide: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/ide/stores
+- security products: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/ee/app/assets/javascripts/vue_shared/security_reports
+- registry: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/registry/stores
-Think of the Main Vue Component as the entry point of your application. This is the only smart
-component that should exist in each Vue feature.
-This component is responsible for:
-1. Calling the Service to get data from the server
-1. Calling the Store to store the data received
-1. Mounting all the other components
+## Vue architecture
-![Vue Architecture](img/vue_arch.png)
+All new features built with Vue.js must follow a [Flux architecture][flux].
+The main goal we are trying to achieve is to have only one data flow and only one data entry.
+In order to achieve this goal we use [vuex](#vuex).
You can also read about this architecture in vue docs about [state management][state-management]
and about [one way data flow][one-way-data-flow].
-### Components, Stores and Services
+### Components and Store
In some features implemented with Vue.js, like the [issue board][issue-boards]
or [environments table][environments-table]
@@ -33,10 +30,8 @@ new_feature
├── components
│ └── component.vue
│ └── ...
-├── stores
+├── store
│ └── new_feature_store.js
-├── services # only when not using vuex
-│ └── new_feature_service.js
├── index.js
```
_For consistency purposes, we recommend you to follow the same structure._
@@ -125,217 +120,6 @@ You can read more about components in Vue.js site, [Component System][component-
#### Vuex
Check this [page](vuex.md) for more details.
-#### Flux like state management
-The Store is a class that allows us to manage the state in a single
-source of truth. It is not aware of the service or the components.
-
-The concept we are trying to follow is better explained by Vue documentation
-itself, please read this guide: [State Management][state-management]
-
-### A folder for the Service
-
-**If you are using Vuex you won't need this step**
-
-The Service is a class used only to communicate with the server.
-It does not store or manipulate any data. It is not aware of the store or the components.
-We use [axios][axios] to communicate with the server.
-Refer to [axios](axios.md) for more details.
-
-Axios instance should only be imported in the service file.
-
-```javascript
-import axios from '~/lib/utils/axios_utils';
-```
-
-### End Result
-
-The following example shows an application:
-
-```javascript
-// store.js
-export default class Store {
-
- /**
- * This is where we will iniatialize the state of our data.
- * Usually in a small SPA you don't need any options when starting the store.
- * In that case you do need guarantee it's an Object and it's documented.
- *
- * @param {Object} options
- */
- constructor(options) {
- this.options = options;
-
- // Create a state object to handle all our data in the same place
- this.todos = [];
- }
-
- setTodos(todos = []) {
- this.todos = todos;
- }
-
- addTodo(todo) {
- this.todos.push(todo);
- }
-
- removeTodo(todoID) {
- const state = this.todos;
-
- const newState = state.filter((element) => {element.id !== todoID});
-
- this.todos = newState;
- }
-}
-
-// service.js
-import axios from '~/lib/utils/axios_utils'
-
-export default class Service {
- constructor(options) {
- this.todos = axios.create({
- baseURL: endpoint.todosEndpoint
- });
-
- }
-
- getTodos() {
- return this.todos.get();
- }
-
- addTodo(todo) {
- return this.todos.put(todo);
- }
-}
-// todo_component.vue
-<script>
-export default {
- props: {
- data: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-<template>
- <div>
- <h1>
- Title: {{data.title}}
- </h1>
- <p>
- {{data.text}}
- </p>
- </div>
-</template>
-
-// todos_main_component.vue
-<script>
-import Store from 'store';
-import Service from 'service';
-import TodoComponent from 'todoComponent';
-export default {
- components: {
- todo: TodoComponent,
- },
- /**
- * Although most data belongs in the store, each component it's own state.
- * We want to show a loading spinner while we are fetching the todos, this state belong
- * in the component.
- *
- * We need to access the store methods through all methods of our component.
- * We need to access the state of our store.
- */
- data() {
- const store = new Store();
-
- return {
- isLoading: false,
- store: store,
- todos: store.todos,
- };
- },
-
- created() {
- this.service = new Service('/todos');
-
- this.getTodos();
- },
-
- methods: {
- getTodos() {
- this.isLoading = true;
-
- this.service
- .getTodos()
- .then(response => {
- this.store.setTodos(response);
- this.isLoading = false;
- })
- .catch(() => {
- this.isLoading = false;
- // Show an error
- });
- },
-
- addTodo(event) {
- this.service
- .addTodo({
- title: 'New entry',
- text: `You clicked on ${event.target.tagName}`,
- })
- .then(response => {
- this.store.addTodo(response);
- })
- .catch(() => {
- // Show an error
- });
- },
- },
-};
-</script>
-<template>
- <div class="container">
- <div v-if="isLoading">
- <i
- class="fa fa-spin fa-spinner"
- aria-hidden="true" />
- </div>
-
- <div
- v-if="!isLoading"
- class="js-todo-list">
- <template v-for='todo in todos'>
- <todo :data="todo" />
- </template>
-
- <button
- @click="addTodo"
- class="js-add-todo">
- Add Todo
- </button>
- </div>
- <div>
-</template>
-
-// index.js
-import todoComponent from 'todos_main_component.vue';
-
-new Vue({
- el: '.js-todo-app',
- components: {
- todoComponent,
- },
- render: createElement => createElement('todo-component' {
- props: {
- someProp: [],
- }
- }),
-});
-
-```
-
-The [issue boards service][issue-boards-service]
-is a good example of this pattern.
-
## Style guide
Please refer to the Vue section of our [style guide](style_guide_js.md#vue-js)
@@ -425,7 +209,7 @@ There is a helper in `spec/javascripts/helpers/vue_mount_component_helper.js` th
```javascript
import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper.js'
+import mountComponent from 'spec/helpers/vue_mount_component_helper'
import component from 'component.vue'
const Component = Vue.extend(component);
@@ -446,6 +230,5 @@ need to test the rendered output. [Vue][vue-test] guide's to unit test show us e
[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
[one-way-data-flow]: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow
[vue-test]: https://vuejs.org/v2/guide/unit-testing.html
-[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
[flux]: https://facebook.github.io/flux
[axios]: https://github.com/axios/axios
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index f7d703b8f0b..6323275426f 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -281,7 +281,7 @@ Now that the new content is marked for translation, we need to update the PO
files with the following command:
```sh
-bin/rake gettext:find
+bin/rake gettext:regenerate
```
This command will update the `locale/gitlab.pot` file with the newly externalized
@@ -292,16 +292,6 @@ file in. Once the changes are on master, they will be picked up by
If there are merge conflicts in the `gitlab.pot` file, you can delete the file
and regenerate it using the same command. Confirm that you are not deleting any strings accidentally by looking over the diff.
-The command also updates the translation files for each language: `locale/*/gitlab.po`
-These changes can be discarded, the language files will be updated by Crowdin
-automatically.
-
-Discard all of them at once like this:
-
-```sh
-git checkout locale/*/gitlab.po
-```
-
### Validating PO files
To make sure we keep our translation files up to date, there's a linter that is
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 9d0d7348df9..ca8ebcdc984 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -11,6 +11,7 @@ are very appreciative of the work done by translators and proofreaders!
- Chinese Traditional
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
- Weizhe Ding - [GitLab](https://gitlab.com/d.weizhe), [Crowdin](https://crowdin.com/profile/d.weizhe)
+ - Yi-Jyun Pan - [GitLab](https://gitlab.com/pan93412), [Crowdin](https://crowdin.com/profile/pan93412)
- Chinese Traditional, Hong Kong
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
- Dutch
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index c06bc0d4731..ddaf636a742 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -60,7 +60,7 @@ Libraries with the following licenses are acceptable for use:
## Unacceptable Licenses
-Libraries with the following licenses are unacceptable for use:
+Libraries with the following licenses require legal approval for use:
- [GNU GPL][GPL] (version 1, [version 2][GPLv2], [version 3][GPLv3], or any future versions): GPL-licensed libraries cannot be linked to from non-GPL projects.
- [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects.
@@ -68,6 +68,26 @@ Libraries with the following licenses are unacceptable for use:
- [Facebook BSD + PATENTS][Facebook]: is a 3-clause BSD license with a patent grant that has been deemed [Category X][x-list] by the Apache foundation.
- [WTFPL][WTFPL]: is a public domain dedication [rejected by the OSI (3.2)][WTFPL-OSI]. Also has a strong language which is not in accordance with our diversity policy.
+## GPL Cooperation Commitment
+
+Before filing or continuing to prosecute any legal proceeding or claim (other than a Defensive Action) arising from termination of a Covered License, GitLab commits to extend to the person or entity (“you”) accused of violating the Covered License the following provisions regarding cure and reinstatement, taken from GPL version 3. As used here, the term ‘this License’ refers to the specific Covered License being enforced.
+
+However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
+
+GitLab intends this Commitment to be irrevocable, and binding and enforceable against GitLab and assignees of or successors to GitLab’s copyrights.
+
+GitLab may modify this Commitment by publishing a new edition on this page or a successor location.
+
+Definitions
+
+‘Covered License’ means the GNU General Public License, version 2 (GPLv2), the GNU Lesser General Public License, version 2.1 (LGPLv2.1), or the GNU Library General Public License, version 2 (LGPLv2), all as published by the Free Software Foundation.
+
+‘Defensive Action’ means a legal proceeding or claim that GitLab brings against you in response to a prior proceeding or claim initiated by you or your affiliate.
+
+GitLab means GitLab Inc. and its affiliates and subsidiaries.
+
## Requesting Approval for Licenses
Libraries that are not listed in the [Acceptable Licenses][Acceptable-Licenses] or [Unacceptable Licenses][Unacceptable-Licenses] list can be submitted to the legal team for review. Please email `legal@gitlab.com` with the details. After a decision has been made, the original requestor is responsible for updating this document.
diff --git a/doc/development/sql.md b/doc/development/sql.md
index 974b1d99dff..e1e1d31a85f 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -243,3 +243,45 @@ WHERE EXISTS (
```
[gin-index]: http://www.postgresql.org/docs/current/static/gin.html
+
+## `.find_or_create_by` is not atomic
+
+The inherent pattern with methods like `.find_or_create_by` and
+`.first_or_create` and others is that they are not atomic. This means,
+it first runs a `SELECT`, and if there are no results an `INSERT` is
+performed. With concurrent processes in mind, there is a race condition
+which may lead to trying to insert two similar records. This may not be
+desired, or may cause one of the queries to fail due to a constraint
+violation, for example.
+
+Using transactions does not solve this problem.
+
+The following pattern should be used to avoid the problem:
+
+```ruby
+Project.transaction do
+ begin
+ User.find_or_create_by(username: "foo")
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
+```
+
+If the above block is run inside a transaction and hits the race
+condition, the transaction is aborted and we cannot simply retry (any
+further queries inside the aborted transaction are going to fail). We
+can employ [nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions)
+here to only rollback the "inner transaction". Note that `requires_new: true` is required here.
+
+```ruby
+Project.transaction do
+ begin
+ User.transaction(requires_new: true) do
+ User.find_or_create_by(username: "foo")
+ end
+ rescue ActiveRecord::RecordNotUnique
+ retry
+ end
+end
+```
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 3b2b9c8c947..f8993653aec 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -172,6 +172,10 @@ object which can be treated like any other jasmine spy object.
Further documentation on the babel rewire pluign API can be found on
[its repository Readme doc](https://github.com/speedskater/babel-plugin-rewire#babel-plugin-rewire).
+#### Waiting in tests
+
+If you cannot avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) in tests, please use the [Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html).
+
### Vue.js unit tests
See this [section][vue-test].
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index 47396666879..b668c9de6a0 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -198,14 +198,14 @@ And that's it, we're done!
## Changing The Schema For Large Tables
While `change_column_type_concurrently` and `rename_column_concurrently` can be
-used for changing the schema of a table without downtime it doesn't work very
+used for changing the schema of a table without downtime, it doesn't work very
well for large tables. Because all of the work happens in sequence the migration
can take a very long time to complete, preventing a deployment from proceeding.
They can also produce a lot of pressure on the database due to it rapidly
updating many rows in sequence.
To reduce database pressure you should instead use
-`change_column_type_using_background_migration` or `rename_column_concurrently`
+`change_column_type_using_background_migration` or `rename_column_using_background_migration`
when migrating a column in a large table (e.g. `issues`). These methods work
similarly to the concurrent counterparts but uses background migration to spread
the work / load over a longer time period, without slowing down deployments.
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 10e8059756d..dd8d95a3bca 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -30,6 +30,9 @@
idea to fill this in.
- Changing the **Visibility Level** modifies the project's
[viewing and access rights](../public_access/public_access.md) for users.
+ - Selecting the **Initialize repository with a README** option creates a
+ README so that the Git repository is initialized, has a default branch and
+ can be cloned.
1. Click **Create project**.
diff --git a/doc/gitlab-basics/img/create_new_project_info.png b/doc/gitlab-basics/img/create_new_project_info.png
index ce4f7d1204b..b4119dc046a 100644
--- a/doc/gitlab-basics/img/create_new_project_info.png
+++ b/doc/gitlab-basics/img/create_new_project_info.png
Binary files differ
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 259d8f73a22..8c7f80fd8e8 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -92,9 +92,9 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source
cd /tmp
- curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.16.3.tar.gz
- echo 'dda229e9c73f4fbb7d4324e0d993e11311673df03f73b194c554c2e9451e17cd git-2.16.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.16.3.tar.gz
- cd git-2.16.3/
+ curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.18.0.tar.gz
+ echo '94faf2c0b02a7920b0b46f4961d8e9cad08e81418614102898a55f980fa3e7e4 git-2.18.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.18.0.tar.gz
+ cd git-2.18.0/
./configure
make prefix=/usr/local all
@@ -447,6 +447,15 @@ You can specify a different Git repository by providing it as an extra parameter
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse,https://example.com/gitlab-workhorse.git]" RAILS_ENV=production
+### Install gitlab-pages
+
+GitLab-Pages uses [GNU Make](https://www.gnu.org/software/make/). This step is optional and only needed if you wish to host static sites from within GitLab. The following commands will install GitLab-Pages in `/home/git/gitlab-pages`. For additional setup steps, please consult the [administration guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/administration/pages/source.md) for your version of GitLab as the GitLab Pages daemon can be ran several different ways.
+
+ cd /home/git
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git
+ cd gitlab-pages
+ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+ sudo -u git -H make
### Initialize Database and Activate Advanced Features
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index 2a14c0397ca..2afcb052536 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -1,5 +1,8 @@
# Integrate your GitLab server with Bitbucket
+NOTE: **Note:**
+You need to [enable OmniAuth](omniauth.md) in order to use this.
+
Import projects from Bitbucket.org and login to your GitLab instance with your
Bitbucket.org account.
@@ -19,8 +22,8 @@ Bitbucket.org.
> **Note:**
GitLab 8.15 significantly simplified the way to integrate Bitbucket.org with
-GitLab. You are encouraged to upgrade your GitLab instance if you haven't done
-already. If you're using GitLab 8.14 and below, [use the previous integration
+GitLab. You are encouraged to upgrade your GitLab instance if you haven't done so
+already. If you're using GitLab 8.14 or below, [use the previous integration
docs][bb-old].
To enable the Bitbucket OmniAuth provider you must register your application
@@ -61,7 +64,7 @@ you to use.
1. Select **Save**.
1. Select your newly created OAuth consumer and you should now see a Key and
- Secret in the list of OAuth customers. Keep this page open as you continue
+ Secret in the list of OAuth consumers. Keep this page open as you continue
the configuration.
![Bitbucket OAuth key](img/bitbucket_oauth_keys.png)
@@ -76,13 +79,13 @@ you to use.
sudo -u git -H editor /home/git/gitlab/config/gitlab.yml
```
-1. Follow the [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration)
- for initial settings.
1. Add the Bitbucket provider configuration:
For Omnibus packages:
```ruby
+ gitlab_rails['omniauth_enabled'] = true
+
gitlab_rails['omniauth_providers'] = [
{
"name" => "bitbucket",
@@ -96,10 +99,13 @@ you to use.
For installations from source:
```yaml
- - { name: 'bitbucket',
- app_id: 'BITBUCKET_APP_KEY',
- app_secret: 'BITBUCKET_APP_SECRET',
- url: 'https://bitbucket.org/' }
+ omniauth:
+ enabled: true
+ providers:
+ - { name: 'bitbucket',
+ app_id: 'BITBUCKET_APP_KEY',
+ app_secret: 'BITBUCKET_APP_SECRET',
+ url: 'https://bitbucket.org/' }
```
---
@@ -108,8 +114,8 @@ you to use.
from the Bitbucket application page.
1. Save the configuration file.
-1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+1. For the changes to take effect, [reconfigure GitLab][] if you installed via
+ Omnibus, or [restart][] if installed from source.
On the sign in page there should now be a Bitbucket icon below the regular sign
in form. Click the icon to begin the authentication process. Bitbucket will ask
@@ -121,9 +127,12 @@ well, the user will be returned to GitLab and will be signed in.
Once the above configuration is set up, you can use Bitbucket to sign into
GitLab and [start importing your projects][bb-import].
+If you want to import projects from Bitbucket, but don't want to enable signing in,
+you can [disable Sign-Ins in the admin panel](omniauth.md#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources).
+
[init-oauth]: omniauth.md#initial-omniauth-configuration
[bb-import]: ../workflow/importing/import_projects_from_bitbucket.md
[bb-old]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/doc/integration/bitbucket.md
[bitbucket-docs]: https://confluence.atlassian.com/bitbucket/use-the-ssh-protocol-with-bitbucket-cloud-221449711.html#UsetheSSHprotocolwithBitbucketCloud-KnownhostorBitbucket%27spublickeyfingerprints
-[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
-[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
+[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart]: ../administration/restart_gitlab.md#installations-from-source
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index db06efdae53..25f396bc9c4 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -1,5 +1,8 @@
# SAML OmniAuth Provider
+NOTE: **Note:**
+You need to [enable OmniAuth](omniauth.md) in order to use this.
+
GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows
GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as
Microsoft ADFS to authenticate users.
@@ -15,33 +18,33 @@ in your SAML IdP:
For omnibus package:
```sh
- sudo editor /etc/gitlab/gitlab.rb
+ sudo editor /etc/gitlab/gitlab.rb
```
For installations from source:
```sh
- cd /home/git/gitlab
+ cd /home/git/gitlab
- sudo -u git -H editor config/gitlab.yml
+ sudo -u git -H editor config/gitlab.yml
```
-1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration)
- for initial settings.
-
1. To allow your users to use SAML to sign up without having to manually create
an account first, don't forget to add the following values to your configuration:
For omnibus package:
```ruby
- gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
- gitlab_rails['omniauth_block_auto_created_users'] = false
+ gitlab_rails['omniauth_enabled'] = true
+ gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
+ gitlab_rails['omniauth_block_auto_created_users'] = false
```
For installations from source:
```yaml
+ omniauth:
+ enabled: true
allow_single_sign_on: ["saml"]
block_auto_created_users: false
```
@@ -52,13 +55,13 @@ in your SAML IdP:
For omnibus package:
```ruby
- gitlab_rails['omniauth_auto_link_saml_user'] = true
+ gitlab_rails['omniauth_auto_link_saml_user'] = true
```
For installations from source:
```yaml
- auto_link_saml_user: true
+ auto_link_saml_user: true
```
1. Add the provider configuration:
@@ -66,35 +69,37 @@ in your SAML IdP:
For omnibus package:
```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- name: 'saml',
- args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
- },
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
- ]
- ```
-
- For installations from source:
-
- ```yaml
- - {
- name: 'saml',
- args: {
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: 'saml',
+ args: {
assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
idp_sso_target_url: 'https://login.example.com/idp',
issuer: 'https://gitlab.example.com',
name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
},
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
+ ]
+ ```
+
+ For installations from source:
+
+ ```yaml
+ omniauth:
+ providers:
+ - {
+ name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
+ },
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
```
1. Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint
@@ -140,8 +145,8 @@ This setting is only available on GitLab 8.7 and above.
SAML login includes support for automatically identifying whether a user should
be considered an [external](../user/permissions.md) user based on the user's group
membership in the SAML identity provider. This feature **does not** allow you to
-automatically add users to GitLab [Groups](../user/group/index.md), it simply
-allows you to mark users as External if they are members of certain groups in the
+automatically add users to GitLab [Groups](../user/group/index.md), it simply
+allows you to mark users as External if they are members of certain groups in the
Identity Provider.
### Requirements
@@ -189,28 +194,28 @@ If you want some SAML authentication methods to count as 2FA on a per session ba
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- name: 'saml',
- args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
- upstream_two_factor_authn_contexts:
- %w(
- urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport
- urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS
- urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN
- )
-
- },
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
- ]
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ upstream_two_factor_authn_contexts:
+ %w(
+ urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport
+ urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS
+ urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN
+ )
+
+ },
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
+ ]
```
-
+
1. Save the file and [reconfigure][] GitLab for the changes to take effect.
---
@@ -218,40 +223,41 @@ If you want some SAML authentication methods to count as 2FA on a per session ba
**For installations from source:**
1. Edit `config/gitlab.yml`:
-
- ```yaml
- - {
- name: 'saml',
- args: {
- assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
- idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
- idp_sso_target_url: 'https://login.example.com/idp',
- issuer: 'https://gitlab.example.com',
- name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
- upstream_two_factor_authn_contexts:
- [
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN'
- ]
-
- },
- label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
- }
+
+ ```yaml
+ omniauth:
+ providers:
+ - {
+ name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
+ upstream_two_factor_authn_contexts:
+ [
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport',
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS',
+ 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN'
+ ]
+ },
+ label: 'Company Login' # optional label for SAML login button, defaults to "Saml"
+ }
```
-
+
1. Save the file and [restart GitLab][] for the changes ot take effect
-
+
In addition to the changes in GitLab, make sure that your Idp is returning the
`AuthnContext`. For example:
```xml
- <saml:AuthnStatement>
- <saml:AuthnContext>
- <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef>
- </saml:AuthnContext>
- </saml:AuthnStatement>
+<saml:AuthnStatement>
+ <saml:AuthnContext>
+ <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:MediumStrongCertificateProtectedTransport</saml:AuthnContextClassRef>
+ </saml:AuthnContext>
+</saml:AuthnStatement>
```
## Customization
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index e8f4c73120c..81ee7338e4e 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -30,6 +30,12 @@ in users.
Any logged in user will have [Guest](../user/permissions.md) permissions
on the repository.
+### Private projects
+
+Private projects can only be cloned and viewed by project members, and
+they will only appear to project members on the public access directory
+(`https://gitlab.example.com/public`).
+
### How to change project visibility
1. Go to your project's **Settings**
diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index 5554a0c8b78..e1b1912ed47 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -14,7 +14,7 @@ bundle exec rake gitlab:import:user_to_projects[username@domain.tld] RAILS_ENV=p
Notes:
-- admin users are added as masters
+- admin users are added as maintainers
```bash
# omnibus-gitlab
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index bab196e7609..63f0a654fcf 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -77,6 +77,8 @@ Note that Public SSH key may also be named as follows:
If you want to change the password of your SSH key pair, you can use
`ssh-keygen -p <keyname>`.
+## Adding a SSH key to your GitLab account
+
1. The next step is to copy the public SSH key as we will need it afterwards.
To copy your public SSH key to the clipboard, use the appropriate code below:
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index bb323705ab3..de1d366adc3 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -297,7 +297,7 @@ out.
In GitLab Starter, differences between the source and
target branches are also
-[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
+[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
### Auto SAST **[ULTIMATE]**
@@ -527,7 +527,7 @@ repo or by specifying a project variable:
- **Bundled chart** - If your project has a `./chart` directory with a `Chart.yaml`
file in it, Auto DevOps will detect the chart and use it instead of the [default
- one](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app).
+ one](https://gitlab.com/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use.
diff --git a/doc/update/10.6-to-10.7.md b/doc/update/10.6-to-10.7.md
index 4efbb8c65cf..b9c14395a3a 100644
--- a/doc/update/10.6-to-10.7.md
+++ b/doc/update/10.6-to-10.7.md
@@ -340,7 +340,7 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations, the upgrade is complete!
-## Things went south? Revert to previous version (10.5)
+## Things went south? Revert to previous version (10.6)
### 1. Revert the code to the previous version
diff --git a/doc/update/11.0-to-11.1.md b/doc/update/11.0-to-11.1.md
index 306bd417ebf..3f10a7edb8a 100644
--- a/doc/update/11.0-to-11.1.md
+++ b/doc/update/11.0-to-11.1.md
@@ -187,7 +187,24 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
sudo -u git -H make
```
-### 10. Update MySQL permissions
+### 10. Update gitlab-pages
+
+#### Only needed if you use GitLab Pages.
+
+Install and compile gitlab-pages. GitLab-Pages uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-pages
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
+sudo -u git -H make
+```
+
+### 11. Update MySQL permissions
If you are using MySQL you need to grant the GitLab user the necessary
permissions on the database:
@@ -209,7 +226,7 @@ You can make this setting permanent by adding it to your `my.cnf`:
log_bin_trust_function_creators=1
```
-### 11. Update configuration files
+### 12. Update configuration files
#### New configuration options for `gitlab.yml`
@@ -283,7 +300,7 @@ For Ubuntu 16.04.1 LTS:
sudo systemctl daemon-reload
```
-### 12. Install libs, migrations, etc.
+### 13. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
@@ -313,14 +330,14 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
-### 13. Start application
+### 14. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
-### 14. Check application status
+### 15. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/user/admin_area/settings/third_party_offers.md b/doc/user/admin_area/settings/third_party_offers.md
new file mode 100644
index 00000000000..177251b52b9
--- /dev/null
+++ b/doc/user/admin_area/settings/third_party_offers.md
@@ -0,0 +1,6 @@
+# Third party offers
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20379)
+> in [GitLab Core](https://about.gitlab.com/pricing/) 11.1
+
+The display of third party offers can be controlled in the Admin Area -> Settings page.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 8e87c896a72..bd199b55a61 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -9,7 +9,7 @@
> For the best result, we encourage you to check this document out as rendered
by GitLab: [markdown.md]
-_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content and Markdown files `.md` in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
+_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content, wiki pages and Markdown files (`.md`) in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
_Where there are significant differences, we will try to call them out in this document._
@@ -22,7 +22,7 @@ You can use GFM in the following areas:
- merge requests
- milestones
- snippets (the snippet must be named with a `.md` extension)
-- wiki pages
+- wiki pages (currently only rendered by Redcarpet)
- markdown documents inside the repository (currently only rendered by Redcarpet)
You can also use other rich text files in GitLab. You might have to install a
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index 2d8fdf0d1da..ec8467da14f 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -62,7 +62,7 @@ Click on `Add Kubernetes cluster`, the cluster is now connected to GitLab. At th
If you would like to utilize your own CI/CD scripts to deploy to the cluster, you can stop here.
-## Disable Role Based-Access Control (RBAC)
+## Disable Role-Based Access Control (RBAC)
Presently, Auto DevOps and one-click app installs do not support [Kubernetes role-based access control](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). Support is [being worked on](https://gitlab.com/groups/gitlab-org/-/epics/136), but in the interim RBAC must be disabled to utilize for these features.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index b25b09f7b1f..cc1d65e4e6c 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -96,8 +96,9 @@ To add an existing Kubernetes cluster to your project:
you can follow the
[Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
to create one. You can also view or create service tokens in the
- [Kubernetes dashboard](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#config)
- (under **Config > Secrets**).
+ [Kubernetes dashboard](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/)
+ (under **Config > Secrets**). **The account that will issue the service token
+ must have admin privileges on the cluster.**
- **Project namespace** (optional) - You don't have to fill it in; by leaving
it blank, GitLab will create one for you. Also:
- Each project should have a unique namespace.
@@ -153,6 +154,13 @@ GitLab provides a one-click install for various applications which will be
added directly to your configured cluster. Those applications are needed for
[Review Apps](../../../ci/review_apps/index.md) and [deployments](../../../ci/environments.md).
+NOTE: **Note:**
+The applications will be installed in a dedicated namespace called
+`gitlab-managed-apps`. In case you have added an existing Kubernetes cluster
+with Tiller already installed, you should be careful as GitLab cannot
+detect it. By installing it via the applications will result into having it
+twice, which can lead to confusion during deployments.
+
| Application | GitLab version | Description |
| ----------- | :------------: | ----------- |
| [Helm Tiller](https://docs.helm.sh/) | 10.2+ | Helm is a package manager for Kubernetes and is required to install all the other applications. It is installed in its own pod inside the cluster which can run the `helm` CLI in a safe environment. |
@@ -206,7 +214,7 @@ kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalanc
> **Note**: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
> ```bash
-> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}"`.
+> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
> ```
The output is the external IP address of your cluster. This information can then
diff --git a/doc/user/project/import/img/manifest_status.png b/doc/user/project/import/img/manifest_status.png
new file mode 100644
index 00000000000..b706116a2ac
--- /dev/null
+++ b/doc/user/project/import/img/manifest_status.png
Binary files differ
diff --git a/doc/user/project/import/img/manifest_upload.png b/doc/user/project/import/img/manifest_upload.png
new file mode 100644
index 00000000000..d6bf4b157dd
--- /dev/null
+++ b/doc/user/project/import/img/manifest_upload.png
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 72cc58546b7..b55435e5b4f 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -11,6 +11,7 @@
1. [From SVN](svn.md)
1. [From TFS](tfs.md)
1. [From repo by URL](repo_by_url.md)
+1. [By uploading a manifest file](manifest.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
new file mode 100644
index 00000000000..06171f11e12
--- /dev/null
+++ b/doc/user/project/import/manifest.md
@@ -0,0 +1,49 @@
+# Import multiple repositories by uploading a manifest file
+
+GitLab allows you to import all the required git repositories
+based a manifest file like the one used by the [Android repository](https://android.googlesource.com/platform/manifest/+/2d6f081a3b05d8ef7a2b1b52b0d536b2b74feab4/default.xml).
+This feature can be very handy when you need to import a project with many repositories like Android Open Source Project (AOSP).
+
+
+>**Note:**
+This feature requires [subgroups](../../group/subgroups/index.md) to be supported by your database.
+
+You can do it by following next steps:
+
+1. From your GitLab dashboard click **New project**
+1. Switch to the **Import project** tab
+1. Click on the **Manifest file** button
+1. Provide GitLab with a manifest xml file
+1. Select a group you want to import to (you need to create a group first if you don't have one)
+1. Click **List available repositories**
+1. You will be redirected to the import status page with projects list based on manifest file
+1. Check the list and click 'Import all repositories' to start import.
+
+![Manifest upload](img/manifest_upload.png)
+
+![Manifest status](img/manifest_status.png)
+
+### Manifest format
+
+A manifest must be an XML file. There must be one `remote` tag with `review` attribute
+that contains a URL to a git server. Each `project` tag must have `name` and `path` attribute.
+GitLab will build URL to the repository by combining URL from `remote` tag with a project name.
+A path attribute will be used to represent project path in GitLab system.
+
+Below is a valid example of manifest file.
+
+```xml
+<manifest>
+ <remote review="https://android-review.googlesource.com/" />
+
+ <project path="build/make" name="platform/build" />
+ <project path="build/blueprint" name="platform/build/blueprint" />
+</manifest>
+```
+
+As result next projects will be created:
+
+| GitLab | Import URL |
+|---|---|
+| https://gitlab/YOUR_GROUP/build/make | https://android-review.googlesource.com/platform/build |
+| https://gitlab/YOUR_GROUP/build/blueprint | https://android-review.googlesource.com/platform/build/blueprint |
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index e97b5d05529..860edb8e6f7 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -364,12 +364,12 @@ When dragging issues between lists, different behavior occurs depending on the s
Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/), as shown in the following table:
-| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Project Issue Boards | Configurable Group Issue Boards | Assignee Lists
+| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Issue Boards | Assignee Lists
| --- | --- | --- | --- | --- | --- |
-| Core | 1 | 1 | No | No | No |
-| Starter | Multiple | 1 | Yes | No | No |
-| Premium | Multiple | Multiple | Yes | Yes | Yes |
-| Ultimate | Multiple | Multiple | Yes | Yes | Yes |
+| Core | 1 | 1 | No | No |
+| Starter | Multiple | 1 | Yes | No |
+| Premium | Multiple | Multiple | Yes | Yes |
+| Ultimate | Multiple | Multiple | Yes | Yes |
## Tips
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 483a54051d7..86ecf33ed31 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -35,7 +35,7 @@ With **[GitLab Enterprise Edition][ee]**, you can also:
- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) **[PREMIUM]**
- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers **[STARTER]**
-- Analyze the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) **[STARTER]**
+- Analyze the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]**
## Use cases
@@ -43,7 +43,7 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
-1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) **[STARTER]**
+1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]**
1. You build and test your changes with GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter)
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index d084ee41d8a..ad0ef60373c 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -107,3 +107,10 @@ On the right sidebar, click on **Clone repository** and follow the on-screen
instructions.
[permissions]: ../../permissions.md
+
+## Customizing sidebar
+
+By default, the wiki would render a sidebar which lists all the pages for the
+wiki. You could as well provide a `_sidebar` page to replace this default
+sidebar. When this customized sidebar page is provided, the default sidebar
+would not be rendered, but the customized one.
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index dda82352c67..760cd87d4cc 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -109,7 +109,6 @@ There are four kinds of filters you can use on your Todos dashboard.
| Filter | Description |
| ------- | ----------- |
| Project | Filter by project |
-| Group | Filter by group |
| Author | Filter by the author that triggered the Todo |
| Type | Filter by issue or merge request |
| Action | Filter by the action that triggered the Todo |
diff --git a/lib/additional_email_headers_interceptor.rb b/lib/additional_email_headers_interceptor.rb
deleted file mode 100644
index 3cb1694b9f1..00000000000
--- a/lib/additional_email_headers_interceptor.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class AdditionalEmailHeadersInterceptor
- def self.delivering_email(message)
- message.header['Auto-Submitted'] ||= 'auto-generated'
- message.header['X-Auto-Response-Suppress'] ||= 'All'
- end
-end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 964780cba6a..92329465b2c 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -6,6 +6,18 @@ module API
before { authorize! :download_code, user_project }
+ helpers do
+ def user_access
+ @user_access ||= Gitlab::UserAccess.new(current_user, project: user_project)
+ end
+
+ def authorize_push_to_branch!(branch)
+ unless user_access.can_push_to_branch?(branch)
+ forbidden!("You are not allowed to push into this branch")
+ end
+ end
+ end
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
@@ -67,7 +79,7 @@ module API
optional :author_name, type: String, desc: 'Author name for commit'
end
post ':id/repository/commits' do
- authorize! :push_code, user_project
+ authorize_push_to_branch!(params[:branch])
attrs = declared_params
attrs[:branch_name] = attrs.delete(:branch)
@@ -142,7 +154,7 @@ module API
requires :branch, type: String, desc: 'The name of the branch'
end
post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
- authorize! :push_code, user_project
+ authorize_push_to_branch!(params[:branch])
commit = user_project.commit(params[:sha])
not_found!('Commit') unless commit
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index b7aadc27e71..6769855b899 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -112,9 +112,9 @@ module API
can_push = params[:can_push].nil? ? deploy_keys_project.can_push : params[:can_push]
title = params[:title] || deploy_keys_project.deploy_key.title
- result = deploy_keys_project.update_attributes(can_push: can_push,
- deploy_key_attributes: { id: params[:key_id],
- title: title })
+ result = deploy_keys_project.update(can_push: can_push,
+ deploy_key_attributes: { id: params[:key_id],
+ title: title })
if result
present deploy_keys_project, with: Entities::DeployKeysProject
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 3a6e707fd5b..b256c33c631 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -135,10 +135,13 @@ module API
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
def self.preload_relation(projects_relation, options = {})
+ # Preloading tags, should be done with using only `:tags`,
+ # as `:tags` are defined as: `has_many :tags, through: :taggings`
+ # N+1 is solved then by using `subject.tags.map(&:name)`
+ # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
projects_relation.preload(:project_feature, :route)
- .preload(:import_state)
- .preload(namespace: [:route, :owner],
- tags: :taggings)
+ .preload(:import_state, :tags)
+ .preload(namespace: [:route, :owner])
end
end
@@ -212,11 +215,15 @@ module API
expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
def self.preload_relation(projects_relation, options = {})
+ # Preloading tags, should be done with using only `:tags`,
+ # as `:tags` are defined as: `has_many :tags, through: :taggings`
+ # N+1 is solved then by using `subject.tags.map(&:name)`
+ # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
super(projects_relation).preload(:group)
.preload(project_group_links: :group,
fork_network: :root_project,
forked_project_link: :forked_from_project,
- forked_from_project: [:route, :forks, namespace: :route, tags: :taggings])
+ forked_from_project: [:route, :forks, :tags, namespace: :route])
end
def self.forks_counting_projects(projects_relation)
@@ -532,6 +539,12 @@ module API
end
class MergeRequestBasic < ProjectEntity
+ expose :title_html, if: -> (_, options) { options[:render_html] } do |entity|
+ MarkupHelper.markdown_field(entity, :title)
+ end
+ expose :description_html, if: -> (_, options) { options[:render_html] } do |entity|
+ MarkupHelper.markdown_field(entity, :description)
+ end
expose :target_branch, :source_branch
expose :upvotes do |merge_request, options|
if options[:issuable_metadata]
@@ -769,33 +782,28 @@ module API
class Todo < Grape::Entity
expose :id
- expose :project, using: Entities::ProjectIdentity, if: -> (todo, _) { todo.project_id }
- expose :group, using: 'API::Entities::NamespaceBasic', if: -> (todo, _) { todo.group_id }
+ expose :project, using: Entities::BasicProjectDetails
expose :author, using: Entities::UserBasic
expose :action_name
expose :target_type
expose :target do |todo, options|
- todo_target_class(todo.target_type).represent(todo.target, options)
+ Entities.const_get(todo.target_type).represent(todo.target, options)
end
expose :target_url do |todo, options|
target_type = todo.target_type.underscore
- target_url = "#{todo.parent.class.to_s.underscore}_#{target_type}_url"
+ target_url = "namespace_project_#{target_type}_url"
target_anchor = "note_#{todo.note_id}" if todo.note_id?
Gitlab::Routing
.url_helpers
- .public_send(target_url, todo.parent, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
+ .public_send(target_url, todo.project.namespace, todo.project, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
end
expose :body
expose :state
expose :created_at
-
- def todo_target_class(target_type)
- ::API::Entities.const_get(target_type)
- end
end
class NamespaceBasic < Grape::Entity
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 5c63ec028d9..fa828f43001 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -89,9 +89,10 @@ module API
requires :environment_id, type: Integer, desc: 'The environment ID'
end
post ':id/environments/:environment_id/stop' do
- authorize! :create_deployment, user_project
+ authorize! :read_environment, user_project
environment = user_project.environments.find(params[:environment_id])
+ authorize! :stop_environment, environment
environment.stop_with_action!(current_user)
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index f633dd88d06..797b04df059 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -150,12 +150,13 @@ module API
end
params do
use :with_custom_attributes
+ optional :with_projects, type: Boolean, default: true, desc: 'Omit project details'
end
get ":id" do
group = find_group!(params[:id])
options = {
- with: Entities::GroupDetail,
+ with: params[:with_projects] ? Entities::GroupDetail : Entities::Group,
current_user: current_user
}
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 9c53b7c3fe7..f7737468148 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -385,7 +385,7 @@ module API
finder_params[:non_public] = true if params[:membership].present?
finder_params[:starred] = true if params[:starred].present?
finder_params[:visibility_level] = Gitlab::VisibilityLevel.level_value(params[:visibility]) if params[:visibility]
- finder_params[:archived] = params[:archived]
+ finder_params[:archived] = archived_param unless params[:archived].nil?
finder_params[:search] = params[:search] if params[:search]
finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
@@ -496,5 +496,11 @@ module API
exception.status == 500
end
+
+ def archived_param
+ return 'only' if params[:archived]
+
+ params[:archived]
+ end
end
end
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index b4bfb677d72..e2984b08eca 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -97,6 +97,8 @@ module API
current_user.admin? || parent.owned_by?(current_user)
end
+ opts[:updated_at] = opts[:created_at] if opts[:created_at]
+
project = parent if parent.is_a?(Project)
::Notes::CreateService.new(project, current_user, opts).execute
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 0f46bc4c98e..2621c9f8fc2 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -232,6 +232,7 @@ module API
params do
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
+ optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
end
desc 'Get a single merge request' do
success Entities::MergeRequest
@@ -239,7 +240,7 @@ module API
get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html]
end
desc 'Get the participants of a merge request' do
diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb
index 5ef4e9d530c..15c57a2fc02 100644
--- a/lib/api/project_export.rb
+++ b/lib/api/project_export.rb
@@ -23,9 +23,13 @@ module API
get ':id/export/download' do
path = user_project.export_project_path
- render_api_error!('404 Not found or has expired', 404) unless path
-
- present_disk_file!(path, File.basename(path), 'application/gzip')
+ if path
+ present_disk_file!(path, File.basename(path), 'application/gzip')
+ elsif user_project.export_project_object_exists?
+ present_carrierwave_file!(user_project.import_export_upload.export_file)
+ else
+ render_api_error!('404 Not found or has expired', 404)
+ end
end
desc 'Start export' do
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 68921ae439b..4760a1c08d7 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -80,7 +80,7 @@ module API
update_params = declared_params(include_missing: false)
- if hook.update_attributes(update_params)
+ if hook.update(update_params)
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index b83da00502d..0888e3befac 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -30,7 +30,7 @@ module API
end
params :filter_params do
- optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+ optional :archived, type: Boolean, desc: 'Limit by archived status'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of projects matching the search criteria'
@@ -260,7 +260,8 @@ module API
:snippets_enabled,
:tag_list,
:visibility,
- :wiki_enabled
+ :wiki_enabled,
+ :avatar
]
optional :name, type: String, desc: 'The name of the project'
optional :default_branch, type: String, desc: 'The default branch of the project'
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index 2071c5a62c1..51242341dba 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -58,7 +58,7 @@ module API
optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
desc: 'The access_level of the runner'
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
- at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level
+ at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level, :maximum_timeout
end
put ':id' do
runner = get_runner(params.delete(:id))
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 794fdab8f2b..553e8dff4b9 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -787,7 +787,7 @@ module API
service = user_project.find_or_initialize_service(service_slug.underscore)
service_params = declared_params(include_missing: false).merge(active: true)
- if service.update_attributes(service_params)
+ if service.update(service_params)
present service, with: Entities::ProjectService
else
render_api_error!('400 Bad Request', 400)
@@ -807,7 +807,7 @@ module API
hash.merge!(key => nil)
end
- unless service.update_attributes(attrs.merge(active: false))
+ unless service.update(attrs.merge(active: false))
render_api_error!('400 Bad Request', 400)
end
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 02ef89f997f..1ca7d23203b 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -25,7 +25,7 @@ module API
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
- optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
+ optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project manifest],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources'
optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
diff --git a/lib/api/users.rb b/lib/api/users.rb
index e8df2c5a74a..5aaaf104dff 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -186,7 +186,7 @@ module API
identity = user.identities.find_by(provider: identity_attrs[:provider])
if identity
- identity.update_attributes(identity_attrs)
+ identity.update(identity_attrs)
else
identity = user.identities.build(identity_attrs)
identity.save
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 60a12dca9d3..b39b11009b3 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -100,6 +100,11 @@ module Banzai
ref_pattern = object_class.reference_pattern
link_pattern = object_class.link_reference_pattern
+ # Compile often used regexps only once outside of the loop
+ ref_pattern_anchor = /\A#{ref_pattern}\z/
+ link_pattern_start = /\A#{link_pattern}/
+ link_pattern_anchor = /\A#{link_pattern}\z/
+
nodes.each do |node|
if text_node?(node) && ref_pattern
replace_text_when_pattern_matches(node, ref_pattern) do |content|
@@ -108,7 +113,7 @@ module Banzai
elsif element_node?(node)
yield_valid_link(node) do |link, inner_html|
- if ref_pattern && link =~ /\A#{ref_pattern}\z/
+ if ref_pattern && link =~ ref_pattern_anchor
replace_link_node_with_href(node, link) do
object_link_filter(link, ref_pattern, link_content: inner_html)
end
@@ -118,7 +123,7 @@ module Banzai
next unless link_pattern
- if link == inner_html && inner_html =~ /\A#{link_pattern}/
+ if link == inner_html && inner_html =~ link_pattern_start
replace_link_node_with_text(node, link) do
object_link_filter(inner_html, link_pattern, link_reference: true)
end
@@ -126,7 +131,7 @@ module Banzai
next
end
- if link =~ /\A#{link_pattern}\z/
+ if link =~ link_pattern_anchor
replace_link_node_with_href(node, link) do
object_link_filter(link, link_pattern, link_content: inner_html, link_reference: true)
end
diff --git a/lib/banzai/filter/markdown_engines/common_mark.rb b/lib/banzai/filter/markdown_engines/common_mark.rb
index bc9597df894..dbb25280849 100644
--- a/lib/banzai/filter/markdown_engines/common_mark.rb
+++ b/lib/banzai/filter/markdown_engines/common_mark.rb
@@ -18,7 +18,7 @@ module Banzai
PARSE_OPTIONS = [
:FOOTNOTES, # parse footnotes.
:STRIKETHROUGH_DOUBLE_TILDE, # parse strikethroughs by double tildes (as redcarpet does).
- :VALIDATE_UTF8 # replace illegal sequences with the replacement character U+FFFD.
+ :VALIDATE_UTF8 # replace illegal sequences with the replacement character U+FFFD.
].freeze
# The `:GITHUB_PRE_LANG` option is not used intentionally because
diff --git a/lib/declarative_policy.rb b/lib/declarative_policy.rb
index 1dd2855063d..dda6cd38dcd 100644
--- a/lib/declarative_policy.rb
+++ b/lib/declarative_policy.rb
@@ -21,7 +21,17 @@ module DeclarativePolicy
cache = opts[:cache] || {}
key = Cache.policy_key(user, subject)
- cache[key] ||= class_for(subject).new(user, subject, opts)
+ cache[key] ||=
+ if Gitlab.rails5?
+ # to avoid deadlocks in multi-threaded environment when
+ # autoloading is enabled, we allow concurrent loads,
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/48263
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ class_for(subject).new(user, subject, opts)
+ end
+ else
+ class_for(subject).new(user, subject, opts)
+ end
end
def class_for(subject)
diff --git a/lib/declarative_policy/base.rb b/lib/declarative_policy/base.rb
index 47542194497..da3fabba39b 100644
--- a/lib/declarative_policy/base.rb
+++ b/lib/declarative_policy/base.rb
@@ -119,8 +119,8 @@ module DeclarativePolicy
# a PolicyDsl which is used for registering the rule with
# this class. PolicyDsl will call back into Base.enable_when,
# Base.prevent_when, and Base.prevent_all_when.
- def rule(&b)
- rule = RuleDsl.new(self).instance_eval(&b)
+ def rule(&block)
+ rule = RuleDsl.new(self).instance_eval(&block)
PolicyDsl.new(self, rule)
end
@@ -222,8 +222,8 @@ module DeclarativePolicy
# computes the given ability and prints a helpful debugging output
# showing which
- def debug(ability, *a)
- runner(ability).debug(*a)
+ def debug(ability, *args)
+ runner(ability).debug(*args)
end
desc "Unknown user"
@@ -274,7 +274,7 @@ module DeclarativePolicy
#
# NOTE we can't use ||= here because the value might be the
# boolean `false`
- def cache(key, &b)
+ def cache(key)
return @cache[key] if cached?(key)
@cache[key] = yield
diff --git a/lib/declarative_policy/delegate_dsl.rb b/lib/declarative_policy/delegate_dsl.rb
index f544dffe888..ca2eb98e3e8 100644
--- a/lib/declarative_policy/delegate_dsl.rb
+++ b/lib/declarative_policy/delegate_dsl.rb
@@ -7,10 +7,10 @@ module DeclarativePolicy
@delegate_name = delegate_name
end
- def method_missing(m, *a, &b)
- return super unless a.empty? && !block_given?
+ def method_missing(msg, *args)
+ return super unless args.empty? && !block_given?
- @rule_dsl.delegate(@delegate_name, m)
+ @rule_dsl.delegate(@delegate_name, msg)
end
end
end
diff --git a/lib/declarative_policy/policy_dsl.rb b/lib/declarative_policy/policy_dsl.rb
index f11b6e9f730..c96049768a1 100644
--- a/lib/declarative_policy/policy_dsl.rb
+++ b/lib/declarative_policy/policy_dsl.rb
@@ -15,8 +15,8 @@ module DeclarativePolicy
@rule = rule
end
- def policy(&b)
- instance_eval(&b)
+ def policy(&block)
+ instance_eval(&block)
end
def enable(*abilities)
@@ -31,14 +31,14 @@ module DeclarativePolicy
@context_class.prevent_all_when(@rule)
end
- def method_missing(m, *a, &b)
- return super unless @context_class.respond_to?(m)
+ def method_missing(msg, *args, &block)
+ return super unless @context_class.respond_to?(msg)
- @context_class.__send__(m, *a, &b) # rubocop:disable GitlabSecurity/PublicSend
+ @context_class.__send__(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
- def respond_to_missing?(m)
- @context_class.respond_to?(m) || super
+ def respond_to_missing?(msg)
+ @context_class.respond_to?(msg) || super
end
end
end
diff --git a/lib/declarative_policy/preferred_scope.rb b/lib/declarative_policy/preferred_scope.rb
index 5c214408dd0..c77784cb49d 100644
--- a/lib/declarative_policy/preferred_scope.rb
+++ b/lib/declarative_policy/preferred_scope.rb
@@ -2,7 +2,7 @@ module DeclarativePolicy # rubocop:disable Naming/FileName
PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
class << self
- def with_preferred_scope(scope, &b)
+ def with_preferred_scope(scope)
Thread.current[PREFERRED_SCOPE_KEY], old_scope = scope, Thread.current[PREFERRED_SCOPE_KEY]
yield
ensure
@@ -13,12 +13,12 @@ module DeclarativePolicy # rubocop:disable Naming/FileName
Thread.current[PREFERRED_SCOPE_KEY]
end
- def user_scope(&b)
- with_preferred_scope(:user, &b)
+ def user_scope(&block)
+ with_preferred_scope(:user, &block)
end
- def subject_scope(&b)
- with_preferred_scope(:subject, &b)
+ def subject_scope(&block)
+ with_preferred_scope(:subject, &block)
end
def preferred_scope=(scope)
diff --git a/lib/declarative_policy/rule.rb b/lib/declarative_policy/rule.rb
index e309244a3b3..407398cc770 100644
--- a/lib/declarative_policy/rule.rb
+++ b/lib/declarative_policy/rule.rb
@@ -8,8 +8,8 @@ module DeclarativePolicy
# how that affects the actual ability decision - for that, a
# `Step` is used.
class Base
- def self.make(*a)
- new(*a).simplify
+ def self.make(*args)
+ new(*args).simplify
end
# true or false whether this rule passes.
diff --git a/lib/declarative_policy/rule_dsl.rb b/lib/declarative_policy/rule_dsl.rb
index e948b7f2de1..7254b08eda5 100644
--- a/lib/declarative_policy/rule_dsl.rb
+++ b/lib/declarative_policy/rule_dsl.rb
@@ -32,13 +32,13 @@ module DeclarativePolicy
Rule::DelegatedCondition.new(delegate_name, condition)
end
- def method_missing(m, *a, &b)
- return super unless a.empty? && !block_given?
+ def method_missing(msg, *args)
+ return super unless args.empty? && !block_given?
- if @context_class.delegations.key?(m)
- DelegateDsl.new(self, m)
+ if @context_class.delegations.key?(msg)
+ DelegateDsl.new(self, msg)
else
- cond(m.to_sym)
+ cond(msg.to_sym)
end
end
end
diff --git a/lib/declarative_policy/runner.rb b/lib/declarative_policy/runner.rb
index 87f14b3b0d2..fec672f4b8c 100644
--- a/lib/declarative_policy/runner.rb
+++ b/lib/declarative_policy/runner.rb
@@ -127,7 +127,7 @@ module DeclarativePolicy
#
# For each step, we yield the step object along with the computed score
# for debugging purposes.
- def steps_by_score(&b)
+ def steps_by_score
flatten_steps!
if @steps.size > 50
diff --git a/lib/disable_email_interceptor.rb b/lib/disable_email_interceptor.rb
deleted file mode 100644
index cee664b8951..00000000000
--- a/lib/disable_email_interceptor.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
-class DisableEmailInterceptor
- def self.delivering_email(message)
- message.perform_deliveries = false
- Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
- end
-end
diff --git a/lib/email_template_interceptor.rb b/lib/email_template_interceptor.rb
deleted file mode 100644
index 3978a6d9fe4..00000000000
--- a/lib/email_template_interceptor.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
-class EmailTemplateInterceptor
- def self.delivering_email(message)
- # Remove HTML part if HTML emails are disabled.
- unless Gitlab::CurrentSettings.html_emails_enabled
- message.parts.delete_if do |part|
- part.content_type.start_with?('text/html')
- end
- end
- end
-end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index a9b04c183ad..e8dbde176ef 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -139,6 +139,11 @@ module ExtractsPath
def lfs_blob_ids
blob_ids = tree.blobs.map(&:id)
+
+ # When current endpoint is a Blob then `tree.blobs` will be empty, it means we need to analyze
+ # the current Blob in order to determine if it's a LFS object
+ blob_ids = Array.wrap(@repo.blob_at(@commit.id, @path)&.id) if blob_ids.empty? # rubocop:disable Gitlab/ModuleWithInstanceVariables
+
@lfs_blob_ids = Gitlab::Git::Blob.batch_lfs_pointers(@project.repository, blob_ids).map(&:id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
index 2760211fee8..f95e423ef22 100644
--- a/lib/gitaly/server.rb
+++ b/lib/gitaly/server.rb
@@ -50,7 +50,7 @@ module Gitaly
@info ||=
begin
Gitlab::GitalyClient::ServerService.new(@storage).info
- rescue GRPC::Unavailable, GRPC::GRPC::DeadlineExceeded
+ rescue GRPC::Unavailable, GRPC::DeadlineExceeded
# This will show the server as being out of date
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index b9a148f35bf..ab6b609d099 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -9,10 +9,6 @@ module Gitlab
Settings
end
- def self.migrations_hash
- @_migrations_hash ||= Digest::MD5.hexdigest(ActiveRecord::Migrator.get_all_versions.to_s)
- end
-
def self.revision
@_revision ||= begin
if File.exist?(root.join("REVISION"))
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 87e377de4d3..b170145f013 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -7,12 +7,14 @@ module Gitlab
module Access
AccessDeniedError = Class.new(StandardError)
- NO_ACCESS = 0
- GUEST = 10
- REPORTER = 20
- DEVELOPER = 30
- MASTER = 40
- OWNER = 50
+ NO_ACCESS = 0
+ GUEST = 10
+ REPORTER = 20
+ DEVELOPER = 30
+ MAINTAINER = 40
+ # @deprecated
+ MASTER = MAINTAINER
+ OWNER = 50
# Branch protection settings
PROTECTION_NONE = 0
@@ -32,7 +34,7 @@ module Gitlab
"Guest" => GUEST,
"Reporter" => REPORTER,
"Developer" => DEVELOPER,
- "Maintainer" => MASTER
+ "Maintainer" => MAINTAINER
}
end
@@ -44,10 +46,10 @@ module Gitlab
def sym_options
{
- guest: GUEST,
- reporter: REPORTER,
- developer: DEVELOPER,
- master: MASTER
+ guest: GUEST,
+ reporter: REPORTER,
+ developer: DEVELOPER,
+ maintainer: MAINTAINER
}
end
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index e7283b2f9e8..589e8062226 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -48,7 +48,7 @@ module Gitlab
gl_user
rescue ActiveRecord::RecordInvalid => e
log.info "(#{provider}) Error saving user #{auth_hash.uid} (#{auth_hash.email}): #{gl_user.errors.full_messages}"
- return self, e.record.errors
+ [self, e.record.errors]
end
def gl_user
diff --git a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb
index d5cf9e0d53a..cb2bdea755c 100644
--- a/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb
+++ b/lib/gitlab/background_migration/add_merge_request_diff_commits_count.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Style/Documentation
-# rubocop:disable Metrics/LineLength
module Gitlab
module BackgroundMigration
diff --git a/lib/gitlab/background_migration/archive_legacy_traces.rb b/lib/gitlab/background_migration/archive_legacy_traces.rb
index 5a4e5b2c471..92096e29ef1 100644
--- a/lib/gitlab/background_migration/archive_legacy_traces.rb
+++ b/lib/gitlab/background_migration/archive_legacy_traces.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
index 1b4a9e8a194..ccd1f9b4dba 100644
--- a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
+++ b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb b/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb
index c2bf42f846d..da8265a3a5f 100644
--- a/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb
+++ b/lib/gitlab/background_migration/create_gpg_key_subkeys_from_gpg_keys.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
class Gitlab::BackgroundMigration::CreateGpgKeySubkeysFromGpgKeys
diff --git a/lib/gitlab/background_migration/delete_diff_files.rb b/lib/gitlab/background_migration/delete_diff_files.rb
index 0b785e1b056..664ead1af44 100644
--- a/lib/gitlab/background_migration/delete_diff_files.rb
+++ b/lib/gitlab/background_migration/delete_diff_files.rb
@@ -1,45 +1,80 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
class DeleteDiffFiles
- def perform(merge_request_diff_id)
- merge_request_diff = MergeRequestDiff.find_by(id: merge_request_diff_id)
+ class MergeRequestDiff < ActiveRecord::Base
+ self.table_name = 'merge_request_diffs'
- return unless merge_request_diff
- return unless should_delete_diff_files?(merge_request_diff)
+ belongs_to :merge_request
+ has_many :merge_request_diff_files
+ end
- MergeRequestDiff.transaction do
- merge_request_diff.update_column(:state, 'without_files')
-
- # explain (analyze, buffers) when deleting 453 diff files:
- #
- # Delete on merge_request_diff_files (cost=0.57..8487.35 rows=4846 width=6) (actual time=43.265..43.265 rows=0 loops=1)
- # Buffers: shared hit=2043 read=259 dirtied=254
- # -> Index Scan using index_merge_request_diff_files_on_mr_diff_id_and_order on merge_request_diff_files (cost=0.57..8487.35 rows=4846 width=6) (actu
- # al time=0.466..26.317 rows=453 loops=1)
- # Index Cond: (merge_request_diff_id = 463448)
- # Buffers: shared hit=17 read=84
- # Planning time: 0.107 ms
- # Execution time: 43.287 ms
- #
- MergeRequestDiffFile.where(merge_request_diff_id: merge_request_diff.id).delete_all
+ class MergeRequestDiffFile < ActiveRecord::Base
+ self.table_name = 'merge_request_diff_files'
+ end
+
+ DEAD_TUPLES_THRESHOLD = 50_000
+ VACUUM_WAIT_TIME = 5.minutes
+
+ def perform(ids)
+ @ids = ids
+
+ # We should reschedule until deadtuples get in a desirable
+ # state (e.g. < 50_000). That may take more than one reschedule.
+ #
+ if should_wait_deadtuple_vacuum?
+ reschedule
+ return
end
+
+ prune_diff_files
+ end
+
+ def should_wait_deadtuple_vacuum?
+ return false unless Gitlab::Database.postgresql?
+
+ diff_files_dead_tuples_count >= DEAD_TUPLES_THRESHOLD
end
private
- def should_delete_diff_files?(merge_request_diff)
- return false if merge_request_diff.state == 'without_files'
+ def reschedule
+ BackgroundMigrationWorker.perform_in(VACUUM_WAIT_TIME, self.class.name.demodulize, [@ids])
+ end
+
+ def diffs_collection
+ MergeRequestDiff.where(id: @ids)
+ end
+
+ def diff_files_dead_tuples_count
+ dead_tuple =
+ execute_statement("SELECT n_dead_tup FROM pg_stat_all_tables "\
+ "WHERE relname = 'merge_request_diff_files'")[0]
- merge_request = merge_request_diff.merge_request
+ dead_tuple&.fetch('n_dead_tup', 0).to_i
+ end
+
+ def prune_diff_files
+ removed = 0
+ updated = 0
- return false unless merge_request.state == 'merged'
- return false if merge_request_diff.id == merge_request.latest_merge_request_diff_id
+ MergeRequestDiff.transaction do
+ updated = diffs_collection.update_all(state: 'without_files')
+ removed = MergeRequestDiffFile.where(merge_request_diff_id: @ids).delete_all
+ end
+
+ log_info("Removed #{removed} merge_request_diff_files rows, "\
+ "updated #{updated} merge_request_diffs rows")
+ end
+
+ def execute_statement(sql)
+ ActiveRecord::Base.connection.execute(sql)
+ end
- true
+ def log_info(message)
+ Rails.logger.info("BackgroundMigration::DeleteDiffFiles - #{message}")
end
end
end
diff --git a/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb b/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb
index a357538a885..58df74cfa9b 100644
--- a/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb
+++ b/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/LineLength
# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
diff --git a/lib/gitlab/background_migration/fill_file_store_job_artifact.rb b/lib/gitlab/background_migration/fill_file_store_job_artifact.rb
index 22b0ac71920..103bd98af14 100644
--- a/lib/gitlab/background_migration/fill_file_store_job_artifact.rb
+++ b/lib/gitlab/background_migration/fill_file_store_job_artifact.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/fill_file_store_lfs_object.rb b/lib/gitlab/background_migration/fill_file_store_lfs_object.rb
index d0816ae3ed5..77c1f1ffaf0 100644
--- a/lib/gitlab/background_migration/fill_file_store_lfs_object.rb
+++ b/lib/gitlab/background_migration/fill_file_store_lfs_object.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/fill_store_upload.rb b/lib/gitlab/background_migration/fill_store_upload.rb
index 94c65459a67..cba3e21cea6 100644
--- a/lib/gitlab/background_migration/fill_store_upload.rb
+++ b/lib/gitlab/background_migration/fill_store_upload.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/fix_cross_project_label_links.rb b/lib/gitlab/background_migration/fix_cross_project_label_links.rb
index fa68ba5cca7..0a12401c35f 100644
--- a/lib/gitlab/background_migration/fix_cross_project_label_links.rb
+++ b/lib/gitlab/background_migration/fix_cross_project_label_links.rb
@@ -11,6 +11,7 @@ module Gitlab
end
class Label < ActiveRecord::Base
+ self.inheritance_column = :_type_disabled
self.table_name = 'labels'
end
@@ -27,6 +28,7 @@ module Gitlab
end
class Namespace < ActiveRecord::Base
+ self.inheritance_column = :_type_disabled
self.table_name = 'namespaces'
def self.groups_with_descendants_ids(start_id, stop_id)
diff --git a/lib/gitlab/background_migration/migrate_build_stage.rb b/lib/gitlab/background_migration/migrate_build_stage.rb
index 242e3143e71..268c6083d3c 100644
--- a/lib/gitlab/background_migration/migrate_build_stage.rb
+++ b/lib/gitlab/background_migration/migrate_build_stage.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/AbcSize
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb b/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
index 7088aa0860a..38fecac1bfe 100644
--- a/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
+++ b/lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb b/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
index 7f243073fd0..ef50fe4adb1 100644
--- a/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
+++ b/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/move_personal_snippet_files.rb b/lib/gitlab/background_migration/move_personal_snippet_files.rb
index a4ef51fd0e8..5b2b2af718a 100644
--- a/lib/gitlab/background_migration/move_personal_snippet_files.rb
+++ b/lib/gitlab/background_migration/move_personal_snippet_files.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb b/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
index d9d3d2e667b..698f5e46c0c 100644
--- a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
+++ b/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/LineLength
# rubocop:disable Metrics/ClassLength
# rubocop:disable Metrics/BlockLength
# rubocop:disable Style/Documentation
diff --git a/lib/gitlab/background_migration/populate_fork_networks_range.rb b/lib/gitlab/background_migration/populate_fork_networks_range.rb
index a976cb4c243..aa4f130538c 100644
--- a/lib/gitlab/background_migration/populate_fork_networks_range.rb
+++ b/lib/gitlab/background_migration/populate_fork_networks_range.rb
@@ -19,7 +19,7 @@ module Gitlab
create_fork_networks_for_missing_projects(start_id, end_id)
create_fork_networks_memberships_for_root_projects(start_id, end_id)
- delay = BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY # rubocop:disable Metrics/LineLength
+ delay = BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY
BackgroundMigrationWorker.perform_in(
delay, "CreateForkNetworkMembershipsRange", [start_id, end_id]
)
diff --git a/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb b/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb
index 8a901a9bf39..d89ce358bb9 100644
--- a/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb
+++ b/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data.rb
@@ -1,7 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Metrics/LineLength
-# rubocop:disable Metrics/MethodLength
-# rubocop:disable Metrics/ClassLength
# rubocop:disable Style/Documentation
module Gitlab
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads.rb b/lib/gitlab/background_migration/populate_untracked_uploads.rb
index 9232f20a063..a19dc9747fb 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads.rb
@@ -4,7 +4,7 @@ module Gitlab
module BackgroundMigration
# This class processes a batch of rows in `untracked_files_for_uploads` by
# adding each file to the `uploads` table if it does not exist.
- class PopulateUntrackedUploads # rubocop:disable Metrics/ClassLength
+ class PopulateUntrackedUploads
def perform(start_id, end_id)
return unless migrate?
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
index a2c5acbde71..4a9a62aaeb5 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
@@ -4,7 +4,7 @@ module Gitlab
module PopulateUntrackedUploadsDependencies
# This class is responsible for producing the attributes necessary to
# track an uploaded file in the `uploads` table.
- class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength, Metrics/LineLength
+ class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength
self.table_name = 'untracked_files_for_uploads'
# Ends with /:random_hex/:filename
@@ -134,7 +134,7 @@ module Gitlab
# Not including a leading slash
def path_relative_to_upload_dir
- upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR # rubocop:disable Metrics/LineLength
+ upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR
base = %r{\A#{Regexp.escape(upload_dir)}/}
@path_relative_to_upload_dir ||= path.sub(base, '')
end
diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
index 522c69a0bb1..81ca2b0a9b7 100644
--- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
@@ -144,7 +144,7 @@ module Gitlab
def table_columns_and_values_for_insert(file_paths)
values = file_paths.map do |file_path|
- ActiveRecord::Base.send(:sanitize_sql_array, ['(?)', file_path]) # rubocop:disable GitlabSecurity/PublicSend, Metrics/LineLength
+ ActiveRecord::Base.send(:sanitize_sql_array, ['(?)', file_path]) # rubocop:disable GitlabSecurity/PublicSend
end.join(', ')
"#{UntrackedFile.table_name} (path) VALUES #{values}"
diff --git a/lib/gitlab/background_migration/schedule_diff_files_deletion.rb b/lib/gitlab/background_migration/schedule_diff_files_deletion.rb
new file mode 100644
index 00000000000..609cf19187c
--- /dev/null
+++ b/lib/gitlab/background_migration/schedule_diff_files_deletion.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class ScheduleDiffFilesDeletion
+ class MergeRequestDiff < ActiveRecord::Base
+ self.table_name = 'merge_request_diffs'
+
+ belongs_to :merge_request
+
+ include EachBatch
+ end
+
+ DIFF_BATCH_SIZE = 5_000
+ INTERVAL = 5.minutes
+ MIGRATION = 'DeleteDiffFiles'
+
+ def perform
+ diffs = MergeRequestDiff
+ .from("(#{diffs_collection.to_sql}) merge_request_diffs")
+ .where('merge_request_diffs.id != merge_request_diffs.latest_merge_request_diff_id')
+ .select(:id)
+
+ diffs.each_batch(of: DIFF_BATCH_SIZE) do |relation, index|
+ ids = relation.pluck(:id)
+
+ BackgroundMigrationWorker.perform_in(index * INTERVAL, MIGRATION, [ids])
+ end
+ end
+
+ private
+
+ def diffs_collection
+ MergeRequestDiff
+ .joins(:merge_request)
+ .where("merge_requests.state = 'merged'")
+ .where('merge_requests.latest_merge_request_diff_id IS NOT NULL')
+ .where("merge_request_diffs.state NOT IN ('without_files', 'empty')")
+ .select('merge_requests.latest_merge_request_diff_id, merge_request_diffs.id')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 35eadf6fa93..e780f8c646b 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -29,105 +29,105 @@ module Gitlab
end
class Converter
- def on_0(s) reset() end
+ def on_0(_) reset() end
- def on_1(s) enable(STYLE_SWITCHES[:bold]) end
+ def on_1(_) enable(STYLE_SWITCHES[:bold]) end
- def on_3(s) enable(STYLE_SWITCHES[:italic]) end
+ def on_3(_) enable(STYLE_SWITCHES[:italic]) end
- def on_4(s) enable(STYLE_SWITCHES[:underline]) end
+ def on_4(_) enable(STYLE_SWITCHES[:underline]) end
- def on_8(s) enable(STYLE_SWITCHES[:conceal]) end
+ def on_8(_) enable(STYLE_SWITCHES[:conceal]) end
- def on_9(s) enable(STYLE_SWITCHES[:cross]) end
+ def on_9(_) enable(STYLE_SWITCHES[:cross]) end
- def on_21(s) disable(STYLE_SWITCHES[:bold]) end
+ def on_21(_) disable(STYLE_SWITCHES[:bold]) end
- def on_22(s) disable(STYLE_SWITCHES[:bold]) end
+ def on_22(_) disable(STYLE_SWITCHES[:bold]) end
- def on_23(s) disable(STYLE_SWITCHES[:italic]) end
+ def on_23(_) disable(STYLE_SWITCHES[:italic]) end
- def on_24(s) disable(STYLE_SWITCHES[:underline]) end
+ def on_24(_) disable(STYLE_SWITCHES[:underline]) end
- def on_28(s) disable(STYLE_SWITCHES[:conceal]) end
+ def on_28(_) disable(STYLE_SWITCHES[:conceal]) end
- def on_29(s) disable(STYLE_SWITCHES[:cross]) end
+ def on_29(_) disable(STYLE_SWITCHES[:cross]) end
- def on_30(s) set_fg_color(0) end
+ def on_30(_) set_fg_color(0) end
- def on_31(s) set_fg_color(1) end
+ def on_31(_) set_fg_color(1) end
- def on_32(s) set_fg_color(2) end
+ def on_32(_) set_fg_color(2) end
- def on_33(s) set_fg_color(3) end
+ def on_33(_) set_fg_color(3) end
- def on_34(s) set_fg_color(4) end
+ def on_34(_) set_fg_color(4) end
- def on_35(s) set_fg_color(5) end
+ def on_35(_) set_fg_color(5) end
- def on_36(s) set_fg_color(6) end
+ def on_36(_) set_fg_color(6) end
- def on_37(s) set_fg_color(7) end
+ def on_37(_) set_fg_color(7) end
- def on_38(s) set_fg_color_256(s) end
+ def on_38(stack) set_fg_color_256(stack) end
- def on_39(s) set_fg_color(9) end
+ def on_39(_) set_fg_color(9) end
- def on_40(s) set_bg_color(0) end
+ def on_40(_) set_bg_color(0) end
- def on_41(s) set_bg_color(1) end
+ def on_41(_) set_bg_color(1) end
- def on_42(s) set_bg_color(2) end
+ def on_42(_) set_bg_color(2) end
- def on_43(s) set_bg_color(3) end
+ def on_43(_) set_bg_color(3) end
- def on_44(s) set_bg_color(4) end
+ def on_44(_) set_bg_color(4) end
- def on_45(s) set_bg_color(5) end
+ def on_45(_) set_bg_color(5) end
- def on_46(s) set_bg_color(6) end
+ def on_46(_) set_bg_color(6) end
- def on_47(s) set_bg_color(7) end
+ def on_47(_) set_bg_color(7) end
- def on_48(s) set_bg_color_256(s) end
+ def on_48(stack) set_bg_color_256(stack) end
- def on_49(s) set_bg_color(9) end
+ def on_49(_) set_bg_color(9) end
- def on_90(s) set_fg_color(0, 'l') end
+ def on_90(_) set_fg_color(0, 'l') end
- def on_91(s) set_fg_color(1, 'l') end
+ def on_91(_) set_fg_color(1, 'l') end
- def on_92(s) set_fg_color(2, 'l') end
+ def on_92(_) set_fg_color(2, 'l') end
- def on_93(s) set_fg_color(3, 'l') end
+ def on_93(_) set_fg_color(3, 'l') end
- def on_94(s) set_fg_color(4, 'l') end
+ def on_94(_) set_fg_color(4, 'l') end
- def on_95(s) set_fg_color(5, 'l') end
+ def on_95(_) set_fg_color(5, 'l') end
- def on_96(s) set_fg_color(6, 'l') end
+ def on_96(_) set_fg_color(6, 'l') end
- def on_97(s) set_fg_color(7, 'l') end
+ def on_97(_) set_fg_color(7, 'l') end
- def on_99(s) set_fg_color(9, 'l') end
+ def on_99(_) set_fg_color(9, 'l') end
- def on_100(s) set_bg_color(0, 'l') end
+ def on_100(_) set_bg_color(0, 'l') end
- def on_101(s) set_bg_color(1, 'l') end
+ def on_101(_) set_bg_color(1, 'l') end
- def on_102(s) set_bg_color(2, 'l') end
+ def on_102(_) set_bg_color(2, 'l') end
- def on_103(s) set_bg_color(3, 'l') end
+ def on_103(_) set_bg_color(3, 'l') end
- def on_104(s) set_bg_color(4, 'l') end
+ def on_104(_) set_bg_color(4, 'l') end
- def on_105(s) set_bg_color(5, 'l') end
+ def on_105(_) set_bg_color(5, 'l') end
- def on_106(s) set_bg_color(6, 'l') end
+ def on_106(_) set_bg_color(6, 'l') end
- def on_107(s) set_bg_color(7, 'l') end
+ def on_107(_) set_bg_color(7, 'l') end
- def on_109(s) set_bg_color(9, 'l') end
+ def on_109(_) set_bg_color(9, 'l') end
attr_accessor :offset, :n_open_tags, :fg_color, :bg_color, :style_mask
@@ -188,19 +188,19 @@ module Gitlab
)
end
- def handle_section(s)
- action = s[1]
- timestamp = s[2]
- section = s[3]
- line = s.matched()[0...-5] # strips \r\033[0K
+ def handle_section(scanner)
+ action = scanner[1]
+ timestamp = scanner[2]
+ section = scanner[3]
+ line = scanner.matched()[0...-5] # strips \r\033[0K
@out << %{<div class="hidden" data-action="#{action}" data-timestamp="#{timestamp}" data-section="#{section}">#{line}</div>}
end
- def handle_sequence(s)
- indicator = s[1]
- commands = s[2].split ';'
- terminator = s[3]
+ def handle_sequence(scanner)
+ indicator = scanner[1]
+ commands = scanner[2].split ';'
+ terminator = scanner[3]
# We are only interested in color and text style changes - triggered by
# sequences starting with '\e[' and ending with 'm'. Any other control
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index 0bbd60d8ffe..375d8bc1ff5 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -7,14 +7,15 @@ module Gitlab
module Artifacts
class Metadata
ParserError = Class.new(StandardError)
+ InvalidStreamError = Class.new(StandardError)
VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/
INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}
- attr_reader :file, :path, :full_version
+ attr_reader :stream, :path, :full_version
- def initialize(file, path, **opts)
- @file, @path, @opts = file, path, opts
+ def initialize(stream, path, **opts)
+ @stream, @path, @opts = stream, path, opts
@full_version = read_version
end
@@ -103,7 +104,17 @@ module Gitlab
end
def gzip(&block)
- Zlib::GzipReader.open(@file, &block)
+ raise InvalidStreamError, "Invalid stream" unless @stream
+
+ # restart gzip reading
+ @stream.seek(0)
+
+ gz = Zlib::GzipReader.new(@stream)
+ yield(gz)
+ rescue Zlib::Error => e
+ raise InvalidStreamError, e.message
+ ensure
+ gz&.finish
end
end
end
diff --git a/lib/gitlab/ci/config/entry/configurable.rb b/lib/gitlab/ci/config/entry/configurable.rb
index db47c2f6185..7cddd2c7b7e 100644
--- a/lib/gitlab/ci/config/entry/configurable.rb
+++ b/lib/gitlab/ci/config/entry/configurable.rb
@@ -47,7 +47,7 @@ module Gitlab
Hash[(@nodes || {}).map { |key, factory| [key, factory.dup] }]
end
- private # rubocop:disable Lint/UselessAccessModifier
+ private
def entry(key, entry, metadata)
factory = Entry::Factory.new(entry)
diff --git a/lib/gitlab/ci/trace/http_io.rb b/lib/gitlab/ci/trace/http_io.rb
deleted file mode 100644
index 8788af57a67..00000000000
--- a/lib/gitlab/ci/trace/http_io.rb
+++ /dev/null
@@ -1,197 +0,0 @@
-##
-# This class is compatible with IO class (https://ruby-doc.org/core-2.3.1/IO.html)
-# source: https://gitlab.com/snippets/1685610
-module Gitlab
- module Ci
- class Trace
- class HttpIO
- BUFFER_SIZE = 128.kilobytes
-
- InvalidURLError = Class.new(StandardError)
- FailedToGetChunkError = Class.new(StandardError)
-
- attr_reader :uri, :size
- attr_reader :tell
- attr_reader :chunk, :chunk_range
-
- alias_method :pos, :tell
-
- def initialize(url, size)
- raise InvalidURLError unless ::Gitlab::UrlSanitizer.valid?(url)
-
- @uri = URI(url)
- @size = size
- @tell = 0
- end
-
- def close
- # no-op
- end
-
- def binmode
- # no-op
- end
-
- def binmode?
- true
- end
-
- def path
- nil
- end
-
- def url
- @uri.to_s
- end
-
- def seek(pos, where = IO::SEEK_SET)
- new_pos =
- case where
- when IO::SEEK_END
- size + pos
- when IO::SEEK_SET
- pos
- when IO::SEEK_CUR
- tell + pos
- else
- -1
- end
-
- raise 'new position is outside of file' if new_pos < 0 || new_pos > size
-
- @tell = new_pos
- end
-
- def eof?
- tell == size
- end
-
- def each_line
- until eof?
- line = readline
- break if line.nil?
-
- yield(line)
- end
- end
-
- def read(length = nil, outbuf = "")
- out = ""
-
- length ||= size - tell
-
- until length <= 0 || eof?
- data = get_chunk
- break if data.empty?
-
- chunk_bytes = [BUFFER_SIZE - chunk_offset, length].min
- chunk_data = data.byteslice(0, chunk_bytes)
-
- out << chunk_data
- @tell += chunk_data.bytesize
- length -= chunk_data.bytesize
- end
-
- # If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
- if outbuf
- outbuf.slice!(0, outbuf.bytesize)
- outbuf << out
- end
-
- out
- end
-
- def readline
- out = ""
-
- until eof?
- data = get_chunk
- new_line = data.index("\n")
-
- if !new_line.nil?
- out << data[0..new_line]
- @tell += new_line + 1
- break
- else
- out << data
- @tell += data.bytesize
- end
- end
-
- out
- end
-
- def write(data)
- raise NotImplementedError
- end
-
- def truncate(offset)
- raise NotImplementedError
- end
-
- def flush
- raise NotImplementedError
- end
-
- def present?
- true
- end
-
- private
-
- ##
- # The below methods are not implemented in IO class
- #
- def in_range?
- @chunk_range&.include?(tell)
- end
-
- def get_chunk
- unless in_range?
- response = Net::HTTP.start(uri.hostname, uri.port, proxy_from_env: true, use_ssl: uri.scheme == 'https') do |http|
- http.request(request)
- end
-
- raise FailedToGetChunkError unless response.code == '200' || response.code == '206'
-
- @chunk = response.body.force_encoding(Encoding::BINARY)
- @chunk_range = response.content_range
-
- ##
- # Note: If provider does not return content_range, then we set it as we requested
- # Provider: minio
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # Provider: AWS
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # Provider: GCS
- # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
- # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPOK 200
- @chunk_range ||= (chunk_start...(chunk_start + @chunk.bytesize))
- end
-
- @chunk[chunk_offset..BUFFER_SIZE]
- end
-
- def request
- Net::HTTP::Get.new(uri).tap do |request|
- request.set_range(chunk_start, BUFFER_SIZE)
- end
- end
-
- def chunk_offset
- tell % BUFFER_SIZE
- end
-
- def chunk_start
- (tell / BUFFER_SIZE) * BUFFER_SIZE
- end
-
- def chunk_end
- [chunk_start + BUFFER_SIZE, size].min
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/trace/section_parser.rb b/lib/gitlab/ci/trace/section_parser.rb
index 9bb0166c9e3..c09089d6475 100644
--- a/lib/gitlab/ci/trace/section_parser.rb
+++ b/lib/gitlab/ci/trace/section_parser.rb
@@ -75,19 +75,19 @@ module Gitlab
@beginning_of_section_regex ||= /section_/.freeze
end
- def find_next_marker(s)
+ def find_next_marker(scanner)
beginning_of_section_len = 8
- maybe_marker = s.exist?(beginning_of_section_regex)
+ maybe_marker = scanner.exist?(beginning_of_section_regex)
if maybe_marker.nil?
- s.terminate
+ scanner.terminate
else
# repositioning at the beginning of the match
- s.pos += maybe_marker - beginning_of_section_len
+ scanner.pos += maybe_marker - beginning_of_section_len
if block_given?
- good_marker = yield(s)
+ good_marker = yield(scanner)
# if not a good marker: Consuming the matched beginning_of_section_regex
- s.pos += beginning_of_section_len unless good_marker
+ scanner.pos += beginning_of_section_len unless good_marker
end
end
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 3cf35f499cd..9147ef401da 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -60,7 +60,7 @@ module Gitlab
def in_memory_application_settings
with_fallback_to_fake_application_settings do
- @in_memory_application_settings ||= ::ApplicationSetting.build_from_defaults # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @in_memory_application_settings ||= ::ApplicationSetting.build_from_defaults
end
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 4ad106e7b0a..872e70f9a5d 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -42,6 +42,21 @@ module Gitlab
!self.read_only?
end
+ # check whether the underlying database is in read-only mode
+ def self.db_read_only?
+ if postgresql?
+ ActiveRecord::Base.connection.execute('SELECT pg_is_in_recovery()')
+ .first
+ .fetch('pg_is_in_recovery') == 't'
+ else
+ false
+ end
+ end
+
+ def self.db_read_write?
+ !self.db_read_only?
+ end
+
def self.version
@version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
end
diff --git a/lib/gitlab/diff/image_point.rb b/lib/gitlab/diff/image_point.rb
index 65332dfd239..1f157354ea4 100644
--- a/lib/gitlab/diff/image_point.rb
+++ b/lib/gitlab/diff/image_point.rb
@@ -3,11 +3,11 @@ module Gitlab
class ImagePoint
attr_reader :width, :height, :x, :y
- def initialize(width, height, x, y)
+ def initialize(width, height, new_x, new_y)
@width = width
@height = height
- @x = x
- @y = y
+ @x = new_x
+ @y = new_y
end
def to_h
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index 54783a07919..99970779c67 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -93,7 +93,7 @@ module Gitlab
private
- def longest_common_prefix(a, b)
+ def longest_common_prefix(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName
max_length = [a.length, b.length].max
length = 0
@@ -109,7 +109,7 @@ module Gitlab
length
end
- def longest_common_suffix(a, b)
+ def longest_common_suffix(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName
longest_common_prefix(a.reverse, b.reverse)
end
end
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index 8c72d00c1f3..ee604e66154 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -138,15 +138,23 @@ module Gitlab
def ee_branch_presence_check!
ee_remotes.keys.each do |remote|
- [ce_branch, ee_branch_prefix, ee_branch_suffix].each do |branch|
- _, status = step("Fetching #{remote}/#{branch}", %W[git fetch #{remote} #{branch}])
+ output, _ = step(
+ "Searching #{remote}",
+ %W[git ls-remote #{remote} *#{minimal_ee_branch_name}*])
- if status.zero?
- @ee_remote_with_branch = remote
- @ee_branch_found = branch
- return true
- end
- end
+ branches =
+ output.scan(%r{(?<=refs/heads/|refs/tags/).+}).sort_by(&:size)
+
+ next if branches.empty?
+
+ branch = branches.first
+
+ step("Fetching #{remote}/#{branch}", %W[git fetch #{remote} #{branch}])
+
+ @ee_remote_with_branch = remote
+ @ee_branch_found = branch
+
+ return true
end
puts
@@ -271,6 +279,10 @@ module Gitlab
@ee_patch_full_path ||= patches_dir.join(ee_patch_name)
end
+ def minimal_ee_branch_name
+ @minimal_ee_branch_name ||= ce_branch.sub(/(\Ace\-|\-ce\z)/, '')
+ end
+
def patch_name_from_branch(branch_name)
branch_name.parameterize << '.patch'
end
diff --git a/lib/gitlab/email/hook/additional_headers_interceptor.rb b/lib/gitlab/email/hook/additional_headers_interceptor.rb
new file mode 100644
index 00000000000..064cb5e659a
--- /dev/null
+++ b/lib/gitlab/email/hook/additional_headers_interceptor.rb
@@ -0,0 +1,12 @@
+module Gitlab
+ module Email
+ module Hook
+ class AdditionalHeadersInterceptor
+ def self.delivering_email(message)
+ message.header['Auto-Submitted'] ||= 'auto-generated'
+ message.header['X-Auto-Response-Suppress'] ||= 'All'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/delivery_metrics_observer.rb b/lib/gitlab/email/hook/delivery_metrics_observer.rb
new file mode 100644
index 00000000000..1c2985f6045
--- /dev/null
+++ b/lib/gitlab/email/hook/delivery_metrics_observer.rb
@@ -0,0 +1,31 @@
+module Gitlab
+ module Email
+ module Hook
+ class DeliveryMetricsObserver
+ extend Gitlab::Utils::StrongMemoize
+
+ def self.delivering_email(_message)
+ delivery_attempts_counter.increment
+ end
+
+ def self.delivered_email(_message)
+ delivered_emails_counter.increment
+ end
+
+ def self.delivery_attempts_counter
+ strong_memoize(:delivery_attempts_counter) do
+ Gitlab::Metrics.counter(:gitlab_emails_delivery_attempts_total,
+ 'Counter of total emails delivery attempts')
+ end
+ end
+
+ def self.delivered_emails_counter
+ strong_memoize(:delivered_emails_counter) do
+ Gitlab::Metrics.counter(:gitlab_emails_delivered_total,
+ 'Counter of total emails delievered')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/disable_email_interceptor.rb b/lib/gitlab/email/hook/disable_email_interceptor.rb
new file mode 100644
index 00000000000..7bb8b53f0c8
--- /dev/null
+++ b/lib/gitlab/email/hook/disable_email_interceptor.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Email
+ module Hook
+ class DisableEmailInterceptor
+ def self.delivering_email(message)
+ message.perform_deliveries = false
+
+ Rails.logger.info "Emails disabled! Interceptor prevented sending mail #{message.subject}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/hook/email_template_interceptor.rb b/lib/gitlab/email/hook/email_template_interceptor.rb
new file mode 100644
index 00000000000..be0c4dd862e
--- /dev/null
+++ b/lib/gitlab/email/hook/email_template_interceptor.rb
@@ -0,0 +1,18 @@
+module Gitlab
+ module Email
+ module Hook
+ class EmailTemplateInterceptor
+ ##
+ # Remove HTML part if HTML emails are disabled.
+ #
+ def self.delivering_email(message)
+ unless Gitlab::CurrentSettings.html_emails_enabled
+ message.parts.delete_if do |part|
+ part.content_type.start_with?('text/html')
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index 0b8f6cfe3cb..d1fd5dfe0cb 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -65,17 +65,17 @@ module Gitlab
clean(message)
end
rescue ArgumentError
- return nil
+ nil
end
- def encode_binary(s)
- return "" if s.nil?
+ def encode_binary(str)
+ return "" if str.nil?
- s.dup.force_encoding(Encoding::ASCII_8BIT)
+ str.dup.force_encoding(Encoding::ASCII_8BIT)
end
- def binary_stringio(s)
- StringIO.new(s || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
+ def binary_stringio(str)
+ StringIO.new(str || '').tap { |io| io.set_encoding(Encoding::ASCII_8BIT) }
end
private
diff --git a/lib/gitlab/exclusive_lease_helpers.rb b/lib/gitlab/exclusive_lease_helpers.rb
new file mode 100644
index 00000000000..e998548cff9
--- /dev/null
+++ b/lib/gitlab/exclusive_lease_helpers.rb
@@ -0,0 +1,29 @@
+module Gitlab
+ # This module provides helper methods which are intregrated with GitLab::ExclusiveLease
+ module ExclusiveLeaseHelpers
+ FailedToObtainLockError = Class.new(StandardError)
+
+ ##
+ # This helper method blocks a process/thread until the other process cancel the obrainted lease key.
+ #
+ # Note: It's basically discouraged to use this method in the unicorn's thread,
+ # because it holds the connection until all `retries` is consumed.
+ # This could potentially eat up all connection pools.
+ def in_lock(key, ttl: 1.minute, retries: 10, sleep_sec: 0.01.seconds)
+ lease = Gitlab::ExclusiveLease.new(key, timeout: ttl)
+
+ until uuid = lease.try_obtain
+ # Keep trying until we obtain the lease. To prevent hammering Redis too
+ # much we'll wait for a bit.
+ sleep(sleep_sec)
+ break if (retries -= 1) < 0
+ end
+
+ raise FailedToObtainLockError, 'Failed to obtain a lock' unless uuid
+
+ yield
+ ensure
+ Gitlab::ExclusiveLease.cancel(key, uuid)
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index 8953bc8c148..a91de278cf3 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -191,19 +191,19 @@ module Gitlab
end
end
- def linkify_issues(s)
- s = s.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
- s = s.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2')
- s
+ def linkify_issues(str)
+ str = str.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
+ str = str.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2')
+ str
end
- def escape_for_markdown(s)
- s = s.gsub(/^#/, "\\#")
- s = s.gsub(/^-/, "\\-")
- s = s.gsub("`", "\\~")
- s = s.delete("\r")
- s = s.gsub("\n", " \n")
- s
+ def escape_for_markdown(str)
+ str = str.gsub(/^#/, "\\#")
+ str = str.gsub(/^-/, "\\-")
+ str = str.gsub("`", "\\~")
+ str = str.delete("\r")
+ str = str.gsub("\n", " \n")
+ str
end
def format_content(raw_content)
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 604bb11e712..71857bd2d87 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -50,13 +50,7 @@ module Gitlab
end
def raw(repository, sha)
- Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled|
- if is_enabled
- repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
- else
- rugged_raw(repository, sha, limit: MAX_DATA_DISPLAY_SIZE)
- end
- end
+ repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
end
# Returns an array of Blob instances, specified in blob_references as
@@ -67,17 +61,8 @@ module Gitlab
# Keep in mind that this method may allocate a lot of memory. It is up
# to the caller to limit the number of blobs and blob_size_limit.
#
- # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/798
def batch(repository, blob_references, blob_size_limit: MAX_DATA_DISPLAY_SIZE)
- Gitlab::GitalyClient.migrate(:list_blobs_by_sha_path) do |is_enabled|
- if is_enabled
- repository.gitaly_blob_client.get_blobs(blob_references, blob_size_limit).to_a
- else
- blob_references.map do |sha, path|
- find(repository, sha, path, limit: blob_size_limit)
- end
- end
- end
+ repository.gitaly_blob_client.get_blobs(blob_references, blob_size_limit).to_a
end
# Returns an array of Blob instances just with the metadata, that means
@@ -90,16 +75,8 @@ module Gitlab
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
- repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled|
- if is_enabled
- repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
- else
- blob_ids.lazy
- .select { |sha| possible_lfs_blob?(repository, sha) }
- .map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
- .select(&:lfs_pointer?)
- .force
- end
+ repository.wrapped_gitaly_errors do
+ repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
end
end
@@ -110,72 +87,6 @@ module Gitlab
def size_could_be_lfs?(size)
size.between?(LFS_POINTER_MIN_SIZE, LFS_POINTER_MAX_SIZE)
end
-
- private
-
- # Recursive search of blob id by path
- #
- # Ex.
- # blog/ # oid: 1a
- # app/ # oid: 2a
- # models/ # oid: 3a
- # file.rb # oid: 4a
- #
- #
- # Blob.find_entry_by_path(repo, '1a', 'blog', 'app', 'file.rb') # => '4a'
- #
- def find_entry_by_path(repository, root_id, *path_parts)
- root_tree = repository.lookup(root_id)
-
- entry = root_tree.find do |entry|
- entry[:name] == path_parts[0]
- end
-
- return nil unless entry
-
- if path_parts.size > 1
- return nil unless entry[:type] == :tree
-
- path_parts.shift
- find_entry_by_path(repository, entry[:oid], *path_parts)
- else
- [:blob, :commit].include?(entry[:type]) ? entry : nil
- end
- end
-
- def submodule_blob(blob_entry, path, sha)
- new(
- id: blob_entry[:oid],
- name: blob_entry[:name],
- size: 0,
- data: '',
- path: path,
- commit_id: sha
- )
- end
-
- def rugged_raw(repository, sha, limit:)
- blob = repository.lookup(sha)
-
- return unless blob.is_a?(Rugged::Blob)
-
- new(
- id: blob.oid,
- size: blob.size,
- data: blob.content(limit),
- binary: blob.binary?
- )
- end
-
- # Efficient lookup to determine if object size
- # and type make it a possible LFS blob without loading
- # blob content into memory with repository.lookup(sha)
- def possible_lfs_blob?(repository, sha)
- object_header = repository.rugged.read_header(sha)
-
- object_header[:type] == :blob &&
- size_could_be_lfs?(object_header[:len])
- end
end
def initialize(options)
@@ -207,16 +118,7 @@ module Gitlab
return if @loaded_all_data
- @data = Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled|
- begin
- if is_enabled
- repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data
- else
- repository.lookup(id).content
- end
- end
- end
-
+ @data = repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data
@loaded_all_data = true
@loaded_size = @data.bytesize
end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index c67826da1d2..4e2d817d12c 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -63,12 +63,8 @@ module Gitlab
# This saves us an RPC round trip.
return nil if commit_id.include?(':')
- commit = repo.gitaly_migrate(:find_commit) do |is_enabled|
- if is_enabled
- repo.gitaly_commit_client.find_commit(commit_id)
- else
- rugged_find(repo, commit_id)
- end
+ commit = repo.wrapped_gitaly_errors do
+ repo.gitaly_commit_client.find_commit(commit_id)
end
decorate(repo, commit) if commit
@@ -78,12 +74,6 @@ module Gitlab
nil
end
- def rugged_find(repo, commit_id)
- obj = repo.rev_parse_target(commit_id)
-
- obj.is_a?(Rugged::Commit) ? obj : nil
- end
-
# Get last commit for HEAD
#
# Ex.
@@ -174,13 +164,8 @@ module Gitlab
# relation to each other. The last 10 commits for a branch for example,
# should go through .where
def batch_by_oid(repo, oids)
- repo.gitaly_migrate(:list_commits_by_oid,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- repo.gitaly_commit_client.list_commits_by_oid(oids)
- else
- oids.map { |oid| find(repo, oid) }.compact
- end
+ repo.wrapped_gitaly_errors do
+ repo.gitaly_commit_client.list_commits_by_oid(oids)
end
end
@@ -299,14 +284,7 @@ module Gitlab
def deltas
@deltas ||= begin
- deltas = Gitlab::GitalyClient.migrate(:commit_deltas) do |is_enabled|
- if is_enabled
- @repository.gitaly_commit_client.commit_deltas(self)
- else
- rugged_diff_from_parent.each_delta
- end
- end
-
+ deltas = @repository.gitaly_commit_client.commit_deltas(self)
deltas.map { |delta| Gitlab::Git::Diff.new(delta) }
end
end
diff --git a/lib/gitlab/git/conflict/resolver.rb b/lib/gitlab/git/conflict/resolver.rb
index c3cb0264112..0e4a973301f 100644
--- a/lib/gitlab/git/conflict/resolver.rb
+++ b/lib/gitlab/git/conflict/resolver.rb
@@ -12,14 +12,8 @@ module Gitlab
end
def conflicts
- @conflicts ||= begin
- @target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled|
- if is_enabled
- gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
- else
- rugged_list_conflict_files
- end
- end
+ @conflicts ||= @target_repository.wrapped_gitaly_errors do
+ gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
end
rescue GRPC::FailedPrecondition => e
raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message)
@@ -28,12 +22,8 @@ module Gitlab
end
def resolve_conflicts(source_repository, resolution, source_branch:, target_branch:)
- source_repository.gitaly_migrate(:conflicts_resolve_conflicts) do |is_enabled|
- if is_enabled
- gitaly_conflicts_client(source_repository).resolve_conflicts(@target_repository, resolution, source_branch, target_branch)
- else
- rugged_resolve_conflicts(source_repository, resolution, source_branch, target_branch)
- end
+ source_repository.wrapped_gitaly_errors do
+ gitaly_conflicts_client(source_repository).resolve_conflicts(@target_repository, resolution, source_branch, target_branch)
end
end
@@ -61,57 +51,6 @@ module Gitlab
def gitaly_conflicts_client(repository)
repository.gitaly_conflicts_client(@our_commit_oid, @their_commit_oid)
end
-
- def write_resolved_file_to_index(repository, index, file, params)
- if params[:sections]
- resolved_lines = file.resolve_lines(params[:sections])
- new_file = resolved_lines.map { |line| line[:full_line] }.join("\n")
-
- new_file << "\n" if file.our_blob.data.end_with?("\n")
- elsif params[:content]
- new_file = file.resolve_content(params[:content])
- end
-
- our_path = file.our_path
-
- oid = repository.rugged.write(new_file, :blob)
- index.add(path: our_path, oid: oid, mode: file.our_mode)
- index.conflict_remove(our_path)
- end
-
- def rugged_list_conflict_files
- target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
-
- # We don't need to do `with_repo_branch_commit` here, because the target
- # project always fetches source refs when creating merge request diffs.
- conflict_files(@target_repository, target_index)
- end
-
- def rugged_resolve_conflicts(source_repository, resolution, source_branch, target_branch)
- source_repository.with_repo_branch_commit(@target_repository, target_branch) do
- index = source_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
- conflicts = conflict_files(source_repository, index)
-
- resolution.files.each do |file_params|
- conflict_file = conflict_for_path(conflicts, file_params[:old_path], file_params[:new_path])
-
- write_resolved_file_to_index(source_repository, index, conflict_file, file_params)
- end
-
- unless index.conflicts.empty?
- missing_files = index.conflicts.map { |file| file[:ours][:path] }
-
- raise ResolutionError, "Missing resolutions for the following files: #{missing_files.join(', ')}"
- end
-
- commit_params = {
- message: resolution.commit_message,
- parents: [@our_commit_oid, @their_commit_oid]
- }
-
- source_repository.commit_index(resolution.user, source_branch, index, commit_params)
- end
- end
end
end
end
diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb
index 6a601561c2a..219c69893ad 100644
--- a/lib/gitlab/git/diff_collection.rb
+++ b/lib/gitlab/git/diff_collection.rb
@@ -42,12 +42,10 @@ module Gitlab
return if @overflow
return if @iterator.nil?
- Gitlab::GitalyClient.migrate(:commit_raw_diffs) do |is_enabled|
- if is_enabled && @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher)
- each_gitaly_patch(&block)
- else
- each_rugged_patch(&block)
- end
+ if @iterator.is_a?(Gitlab::GitalyClient::DiffStitcher)
+ each_gitaly_patch(&block)
+ else
+ each_serialized_patch(&block)
end
@populated = true
@@ -118,7 +116,7 @@ module Gitlab
end
end
- def each_rugged_patch
+ def each_serialized_patch
i = @array.length
@iterator.each do |raw|
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb
index f9f24ecc48d..7426688fc55 100644
--- a/lib/gitlab/git/popen.rb
+++ b/lib/gitlab/git/popen.rb
@@ -21,6 +21,10 @@ module Gitlab
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
stdout.set_encoding(Encoding::ASCII_8BIT)
+ # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
+ # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
+ err_reader = Thread.new { stderr.read }
+
yield(stdin) if block_given?
stdin.close
@@ -32,7 +36,7 @@ module Gitlab
cmd_output << stdout.read
end
- cmd_output << stderr.read
+ cmd_output << err_reader.value
cmd_status = wait_thr.value.exitstatus
end
@@ -55,16 +59,20 @@ module Gitlab
rerr, werr = IO.pipe
pid = Process.spawn(vars, *cmd, out: wout, err: werr, chdir: path, pgroup: true)
+ # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
+ # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
+ out_reader = Thread.new { rout.read }
+ err_reader = Thread.new { rerr.read }
begin
- status = process_wait_with_timeout(pid, timeout)
-
# close write ends so we could read them
wout.close
werr.close
- cmd_output = rout.readlines.join
- cmd_output << rerr.readlines.join # Copying the behaviour of `popen` which merges stderr into output
+ status = process_wait_with_timeout(pid, timeout)
+
+ cmd_output = out_reader.value
+ cmd_output << err_reader.value # Copying the behaviour of `popen` which merges stderr into output
[cmd_output, status.exitstatus]
rescue Timeout::Error => e
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index bbfe6ab1d95..2cbd9c218d4 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -86,9 +86,6 @@ module Gitlab
# Relative path of repo
attr_reader :relative_path
- # Rugged repo object
- attr_reader :rugged
-
attr_reader :gitlab_projects, :storage, :gl_repository, :relative_path
# This initializer method is only used on the client side (gitlab-ce).
@@ -112,8 +109,9 @@ module Gitlab
[storage, relative_path] == [other.storage, other.relative_path]
end
+ # This method will be removed when Gitaly reaches v1.1.
def path
- @path ||= File.join(
+ File.join(
Gitlab.config.repositories.storages[@storage].legacy_disk_path, @relative_path
)
end
@@ -127,8 +125,9 @@ module Gitlab
raise Gitlab::Git::CommandError.new(e.message)
end
+ # This method will be removed when Gitaly reaches v1.1.
def rugged
- @rugged ||= circuit_breaker.perform do
+ circuit_breaker.perform do
Rugged::Repository.new(path, alternates: alternate_object_directories)
end
rescue Rugged::RepositoryError, Rugged::OSError
@@ -168,24 +167,9 @@ module Gitlab
# Directly find a branch with a simple name (e.g. master)
#
- # force_reload causes a new Rugged repository to be instantiated
- #
- # This is to work around a bug in libgit2 that causes in-memory refs to
- # be stale/invalid when packed-refs is changed.
- # See https://gitlab.com/gitlab-org/gitlab-ce/issues/15392#note_14538333
- def find_branch(name, force_reload = false)
- gitaly_migrate(:find_branch) do |is_enabled|
- if is_enabled
- gitaly_ref_client.find_branch(name)
- else
- reload_rugged if force_reload
-
- rugged_ref = rugged.branches[name]
- if rugged_ref
- target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
- end
- end
+ def find_branch(name)
+ wrapped_gitaly_errors do
+ gitaly_ref_client.find_branch(name)
end
end
@@ -197,20 +181,8 @@ module Gitlab
# Returns the number of valid branches
def branch_count
- gitaly_migrate(:branch_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.count_branch_names
- else
- rugged.branches.each(:local).count do |ref|
- begin
- ref.name && ref.target # ensures the branch is valid
-
- true
- rescue Rugged::ReferenceError
- false
- end
- end
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.count_branch_names
end
end
@@ -233,12 +205,8 @@ module Gitlab
# Returns the number of valid tags
def tag_count
- gitaly_migrate(:tag_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.count_tag_names
- else
- rugged.tags.count
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.count_tag_names
end
end
@@ -261,13 +229,8 @@ module Gitlab
#
# Ref names must start with `refs/`.
def ref_exists?(ref_name)
- gitaly_migrate(:ref_exists,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_exists?(ref_name)
- else
- rugged_ref_exists?(ref_name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_exists?(ref_name)
end
end
@@ -275,12 +238,8 @@ module Gitlab
#
# name - The name of the tag as a String.
def tag_exists?(name)
- gitaly_migrate(:ref_exists_tags, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_exists?("refs/tags/#{name}")
- else
- rugged_tag_exists?(name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_exists?("refs/tags/#{name}")
end
end
@@ -288,12 +247,8 @@ module Gitlab
#
# name - The name of the branch as a String.
def branch_exists?(name)
- gitaly_migrate(:ref_exists_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_exists?("refs/heads/#{name}")
- else
- rugged_branch_exists?(name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_exists?("refs/heads/#{name}")
end
end
@@ -311,12 +266,8 @@ module Gitlab
end
def delete_all_refs_except(prefixes)
- gitaly_migrate(:ref_delete_refs) do |is_enabled|
- if is_enabled
- gitaly_ref_client.delete_refs(except_with_prefixes: prefixes)
- else
- delete_refs(*all_ref_names_except(prefixes))
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.delete_refs(except_with_prefixes: prefixes)
end
end
@@ -329,12 +280,6 @@ module Gitlab
end.map(&:name)
end
- def rugged_head
- rugged.head
- rescue Rugged::ReferenceError
- nil
- end
-
def archive_metadata(ref, storage_path, project_path, format = "tar.gz", append_sha:)
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
@@ -439,31 +384,11 @@ module Gitlab
raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}")
end
- gitaly_migrate(:find_commits) do |is_enabled|
- if is_enabled
- gitaly_commit_client.find_commits(options)
- else
- raw_log(options).map { |c| Commit.decorate(self, c) }
- end
+ wrapped_gitaly_errors do
+ gitaly_commit_client.find_commits(options)
end
end
- # Used in gitaly-ruby
- def raw_log(options)
- sha =
- unless options[:all]
- actual_ref = options[:ref] || root_ref
- begin
- sha_from_ref(actual_ref)
- rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError
- # Return an empty array if the ref wasn't found
- return []
- end
- end
-
- log_by_shell(sha, options)
- end
-
def count_commits(options)
options = process_count_commits_options(options.dup)
@@ -518,12 +443,8 @@ module Gitlab
# Returns the SHA of the most recent common ancestor of +from+ and +to+
def merge_base(from, to)
- gitaly_migrate(:merge_base) do |is_enabled|
- if is_enabled
- gitaly_repository_client.find_merge_base(from, to)
- else
- rugged_merge_base(from, to)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.find_merge_base(from, to)
end
end
@@ -539,12 +460,8 @@ module Gitlab
return [] unless root_sha
- branches = gitaly_migrate(:merged_branch_names) do |is_enabled|
- if is_enabled
- gitaly_merged_branch_names(branch_names, root_sha)
- else
- git_merged_branch_names(branch_names, root_sha)
- end
+ branches = wrapped_gitaly_errors do
+ gitaly_merged_branch_names(branch_names, root_sha)
end
Set.new(branches)
@@ -555,13 +472,7 @@ module Gitlab
# diff options. The +options+ hash can also include :break_rewrites to
# split larger rewrites into delete/add pairs.
def diff(from, to, options = {}, *paths)
- iterator = gitaly_migrate(:diff_between) do |is_enabled|
- if is_enabled
- gitaly_commit_client.diff(from, to, options.merge(paths: paths))
- else
- diff_patches(from, to, options, *paths)
- end
- end
+ iterator = gitaly_commit_client.diff(from, to, options.merge(paths: paths))
Gitlab::Git::DiffCollection.new(iterator, options)
end
@@ -590,11 +501,6 @@ module Gitlab
@refs_hash
end
- # Lookup for rugged object by oid or ref name
- def lookup(oid_or_ref_name)
- rugged.rev_parse(oid_or_ref_name)
- end
-
# Returns url for submodule
#
# Ex.
@@ -651,7 +557,13 @@ module Gitlab
end
def update_branch(branch_name, user:, newrev:, oldrev:)
- OperationService.new(user, self).update_branch(branch_name, newrev, oldrev)
+ gitaly_migrate(:operation_user_update_branch) do |is_enabled|
+ 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)
+ end
+ end
end
def rm_branch(branch_name, user:)
@@ -733,33 +645,18 @@ module Gitlab
Gitlab::Git.committer_hash(email: user.email, name: user.name)
end
- def create_commit(params = {})
- params[:message].delete!("\r")
-
- Rugged::Commit.create(rugged, params)
- end
-
# Delete the specified branch from the repository
def delete_branch(branch_name)
- gitaly_migrate(:delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.delete_branch(branch_name)
- else
- rugged.branches.delete(branch_name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.delete_branch(branch_name)
end
- rescue Rugged::ReferenceError, CommandError => e
+ rescue CommandError => e
raise DeleteBranchError, e
end
def delete_refs(*ref_names)
- gitaly_migrate(:delete_refs,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_delete_refs(*ref_names)
- else
- git_delete_refs(*ref_names)
- end
+ wrapped_gitaly_errors do
+ gitaly_delete_refs(*ref_names)
end
end
@@ -769,12 +666,8 @@ module Gitlab
# create_branch("feature")
# create_branch("other-feature", "master")
def create_branch(ref, start_point = "HEAD")
- gitaly_migrate(:create_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.create_branch(ref, start_point)
- else
- rugged_create_branch(ref, start_point)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.create_branch(ref, start_point)
end
end
@@ -924,12 +817,8 @@ module Gitlab
end
def fetch_source_branch!(source_repository, source_branch, local_ref)
- Gitlab::GitalyClient.migrate(:fetch_source_branch) do |is_enabled|
- if is_enabled
- gitaly_repository_client.fetch_source_branch(source_repository, source_branch, local_ref)
- else
- rugged_fetch_source_branch(source_repository, source_branch, local_ref)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.fetch_source_branch(source_repository, source_branch, local_ref)
end
end
@@ -951,12 +840,8 @@ module Gitlab
def write_ref(ref_path, ref, old_ref: nil, shell: true)
ref_path = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref_path}" unless ref_path.start_with?("refs/") || ref_path == "HEAD"
- gitaly_migrate(:write_ref) do |is_enabled|
- if is_enabled
- gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell)
- else
- local_write_ref(ref_path, ref, old_ref: old_ref, shell: shell)
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.write_ref(ref_path, ref, old_ref, shell)
end
end
@@ -1008,20 +893,6 @@ module Gitlab
Gitlab::Git::Blob.batch(self, items, blob_size_limit: blob_size_limit)
end
- def commit_index(user, branch_name, index, options)
- committer = user_to_committer(user)
-
- OperationService.new(user, self).with_branch(branch_name) do
- commit_params = options.merge(
- tree: index.write_tree(rugged),
- author: committer,
- committer: committer
- )
-
- create_commit(commit_params)
- end
- end
-
def fsck
msg, status = gitaly_repository_client.fsck
@@ -1084,18 +955,13 @@ module Gitlab
end
def bundle_to_disk(save_path)
- gitaly_migrate(:bundle_to_disk) do |is_enabled|
- if is_enabled
- gitaly_repository_client.create_bundle(save_path)
- else
- run_git!(%W(bundle create #{save_path} --all))
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.create_bundle(save_path)
end
true
end
- # rubocop:disable Metrics/ParameterLists
def multi_action(
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
@@ -1107,7 +973,6 @@ module Gitlab
start_branch_name, start_repository)
end
end
- # rubocop:enable Metrics/ParameterLists
def write_config(full_path:)
return unless full_path.present?
@@ -1115,8 +980,18 @@ module Gitlab
# This guard avoids Gitaly log/error spam
raise NoRepository, 'repository does not exist' unless exists?
+ set_config('gitlab.fullpath' => full_path)
+ end
+
+ def set_config(entries)
+ wrapped_gitaly_errors do
+ gitaly_repository_client.set_config(entries)
+ end
+ end
+
+ def delete_config(*keys)
wrapped_gitaly_errors do
- gitaly_repository_client.write_config(full_path: full_path)
+ gitaly_repository_client.delete_config(keys)
end
end
@@ -1202,7 +1077,7 @@ module Gitlab
end
def can_be_merged?(source_sha, target_branch)
- if target_sha = find_branch(target_branch, true)&.target
+ if target_sha = find_branch(target_branch)&.target
!gitaly_conflicts_client(source_sha, target_sha).conflicts?
else
false
@@ -1219,12 +1094,10 @@ module Gitlab
end
def find_commits_by_message(query, ref, path, limit, offset)
- gitaly_migrate(:commits_by_message) do |is_enabled|
- if is_enabled
- find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- else
- find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- end
+ wrapped_gitaly_errors do
+ gitaly_commit_client
+ .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
+ .map { |c| commit(c) }
end
end
@@ -1234,12 +1107,8 @@ module Gitlab
end
def last_commit_for_path(sha, path)
- gitaly_migrate(:last_commit_for_path) do |is_enabled|
- if is_enabled
- last_commit_for_path_by_gitaly(sha, path)
- else
- last_commit_for_path_by_rugged(sha, path)
- end
+ wrapped_gitaly_errors do
+ gitaly_commit_client.last_commit_for_path(sha, path)
end
end
@@ -1307,37 +1176,6 @@ module Gitlab
end
end
- def local_write_ref(ref_path, ref, old_ref: nil, shell: true)
- if shell
- shell_write_ref(ref_path, ref, old_ref)
- else
- rugged_write_ref(ref_path, ref)
- end
- end
-
- def rugged_write_config(full_path:)
- rugged.config['gitlab.fullpath'] = full_path
- end
-
- def shell_write_ref(ref_path, ref, old_ref)
- raise ArgumentError, "invalid ref_path #{ref_path.inspect}" if ref_path.include?(' ')
- raise ArgumentError, "invalid ref #{ref.inspect}" if ref.include?("\x00")
- raise ArgumentError, "invalid old_ref #{old_ref.inspect}" if !old_ref.nil? && old_ref.include?("\x00")
-
- input = "update #{ref_path}\x00#{ref}\x00#{old_ref}\x00"
- run_git!(%w[update-ref --stdin -z]) { |stdin| stdin.write(input) }
- end
-
- def rugged_write_ref(ref_path, ref)
- rugged.references.create(ref_path, ref, force: true)
- rescue Rugged::ReferenceError => ex
- Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
- rescue Rugged::OSError => ex
- raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
-
- Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
- end
-
def run_git(args, chdir: path, env: {}, nice: false, lazy_block: nil, &block)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
@@ -1408,20 +1246,6 @@ module Gitlab
}
end
- def git_merged_branch_names(branch_names, root_sha)
- git_arguments =
- %W[branch --merged #{root_sha}
- --format=%(refname:short)\ %(objectname)] + branch_names
-
- lines = run_git(git_arguments).first.lines
-
- lines.each_with_object([]) do |line, branches|
- name, sha = line.strip.split(' ', 2)
-
- branches << name if sha != root_sha
- end
- end
-
def gitaly_merged_branch_names(branch_names, root_sha)
qualified_branch_names = branch_names.map { |b| "refs/heads/#{b}" }
@@ -1450,63 +1274,6 @@ module Gitlab
end
end
- # Gitaly note: JV: although #log_by_shell shells out to Git I think the
- # complexity is such that we should migrate it as Ruby before trying to
- # do it in Go.
- def log_by_shell(sha, options)
- limit = options[:limit].to_i
- offset = options[:offset].to_i
- use_follow_flag = options[:follow] && options[:path].present?
-
- # We will perform the offset in Ruby because --follow doesn't play well with --skip.
- # See: https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
- offset_in_ruby = use_follow_flag && options[:offset].present?
- limit += offset if offset_in_ruby
-
- cmd = %w[log]
- cmd << "--max-count=#{limit}"
- cmd << '--format=%H'
- cmd << "--skip=#{offset}" unless offset_in_ruby
- cmd << '--follow' if use_follow_flag
- cmd << '--no-merges' if options[:skip_merges]
- cmd << "--after=#{options[:after].iso8601}" if options[:after]
- cmd << "--before=#{options[:before].iso8601}" if options[:before]
-
- if options[:all]
- cmd += %w[--all --reverse]
- else
- cmd << sha
- end
-
- # :path can be a string or an array of strings
- if options[:path].present?
- cmd << '--'
- cmd += Array(options[:path])
- end
-
- raw_output, _status = run_git(cmd)
- lines = offset_in_ruby ? raw_output.lines.drop(offset) : raw_output.lines
-
- lines.map! { |c| Rugged::Commit.new(rugged, c.strip) }
- end
-
- # We are trying to deprecate this method because it does a lot of work
- # but it seems to be used only to look up submodule URL's.
- # https://gitlab.com/gitlab-org/gitaly/issues/329
- def submodules(ref)
- commit = rev_parse_target(ref)
- return {} unless commit
-
- begin
- content = blob_content(commit, ".gitmodules")
- rescue InvalidBlobName
- return {}
- end
-
- parser = GitmodulesParser.new(content)
- fill_submodule_ids(commit, parser.parse)
- end
-
def gitaly_submodule_url_for(ref, path)
# We don't care about the contents so 1 byte is enough. Can't request 0 bytes, 0 means unlimited.
commit_object = gitaly_commit_client.tree_entry(ref, path, 1)
@@ -1529,68 +1296,6 @@ module Gitlab
Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
end
- # Get the content of a blob for a given commit. If the blob is a commit
- # (for submodules) then return the blob's OID.
- def blob_content(commit, blob_name)
- blob_entry = tree_entry(commit, blob_name)
-
- unless blob_entry
- raise InvalidBlobName.new("Invalid blob name: #{blob_name}")
- end
-
- case blob_entry[:type]
- when :commit
- blob_entry[:oid]
- when :tree
- raise InvalidBlobName.new("#{blob_name} is a tree, not a blob")
- when :blob
- rugged.lookup(blob_entry[:oid]).content
- end
- end
-
- # Fill in the 'id' field of a submodule hash from its values
- # as-of +commit+. Return a Hash consisting only of entries
- # from the submodule hash for which the 'id' field is filled.
- def fill_submodule_ids(commit, submodule_data)
- submodule_data.each do |path, data|
- id = begin
- blob_content(commit, path)
- rescue InvalidBlobName
- nil
- end
- data['id'] = id
- end
- submodule_data.select { |path, data| data['id'] }
- end
-
- # Find the entry for +path+ in the tree for +commit+
- def tree_entry(commit, path)
- pathname = Pathname.new(path)
- first = true
- tmp_entry = nil
-
- pathname.each_filename do |dir|
- if first
- tmp_entry = commit.tree[dir]
- first = false
- elsif tmp_entry.nil?
- return nil
- else
- begin
- tmp_entry = rugged.lookup(tmp_entry[:oid])
- rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError
- return nil
- end
-
- return nil unless tmp_entry.type == :tree
-
- tmp_entry = tmp_entry[dir]
- end
- end
-
- tmp_entry
- end
-
# Return the Rugged patches for the diff between +from+ and +to+.
def diff_patches(from, to, options = {}, *paths)
options ||= {}
@@ -1619,22 +1324,6 @@ module Gitlab
end
end
- def last_commit_for_path_by_rugged(sha, path)
- sha = last_commit_id_for_path_by_shelling_out(sha, path)
- commit(sha)
- end
-
- # Returns true if the given ref name exists
- #
- # Ref names must start with `refs/`.
- def rugged_ref_exists?(ref_name)
- raise ArgumentError, 'invalid refname' unless ref_name.start_with?('refs/')
-
- rugged.references.exist?(ref_name)
- rescue Rugged::ReferenceError
- false
- end
-
# Returns true if the given ref name exists
#
# Ref names must start with `refs/`.
@@ -1642,110 +1331,10 @@ module Gitlab
gitaly_ref_client.ref_exists?(ref_name)
end
- # Returns true if the given tag exists
- #
- # name - The name of the tag as a String.
- def rugged_tag_exists?(name)
- !!rugged.tags[name]
- end
-
- # Returns true if the given branch exists
- #
- # name - The name of the branch as a String.
- def rugged_branch_exists?(name)
- rugged.branches.exists?(name)
-
- # If the branch name is invalid (e.g. ".foo") Rugged will raise an error.
- # Whatever code calls this method shouldn't have to deal with that so
- # instead we just return `false` (which is true since a branch doesn't
- # exist when it has an invalid name).
- rescue Rugged::ReferenceError
- false
- end
-
- def rugged_create_branch(ref, start_point)
- rugged_ref = rugged.branches.create(ref, start_point)
- target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
- rescue Rugged::ReferenceError => e
- raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ %r{'refs/heads/#{ref}'}
-
- raise InvalidRef.new("Invalid reference #{start_point}")
- end
-
def gitaly_copy_gitattributes(revision)
gitaly_repository_client.apply_gitattributes(revision)
end
- def rugged_copy_gitattributes(ref)
- begin
- commit = lookup(ref)
- rescue Rugged::ReferenceError
- raise InvalidRef.new("Ref #{ref} is invalid")
- end
-
- # Create the paths
- info_dir_path = File.join(path, 'info')
- info_attributes_path = File.join(info_dir_path, 'attributes')
-
- begin
- # Retrieve the contents of the blob
- gitattributes_content = blob_content(commit, '.gitattributes')
- rescue InvalidBlobName
- # No .gitattributes found. Should now remove any info/attributes and return
- File.delete(info_attributes_path) if File.exist?(info_attributes_path)
- return
- end
-
- # Create the info directory if needed
- Dir.mkdir(info_dir_path) unless File.directory?(info_dir_path)
-
- # Write the contents of the .gitattributes file to info/attributes
- # Use binary mode to prevent Rails from converting ASCII-8BIT to UTF-8
- File.open(info_attributes_path, "wb") do |file|
- file.write(gitattributes_content)
- end
- end
-
- def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
- OperationService.new(user, self).with_branch(
- branch_name,
- start_branch_name: start_branch_name,
- start_repository: start_repository
- ) do |start_commit|
-
- Gitlab::Git.check_namespace!(commit, start_repository)
-
- cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
- raise CreateTreeError unless cherry_pick_tree_id
-
- committer = user_to_committer(user)
-
- create_commit(message: message,
- author: {
- email: commit.author_email,
- name: commit.author_name,
- time: commit.authored_date
- },
- committer: committer,
- tree: cherry_pick_tree_id,
- parents: [start_commit.sha])
- end
- end
-
- def check_cherry_pick_content(target_commit, source_sha)
- args = [target_commit.sha, source_sha]
- args << 1 if target_commit.merge_commit?
-
- cherry_pick_index = rugged.cherrypick_commit(*args)
- return false if cherry_pick_index.conflicts?
-
- tree_id = cherry_pick_index.write_tree(rugged)
- return false unless diff_exists?(source_sha, tree_id)
-
- tree_id
- 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)
@@ -1765,20 +1354,6 @@ module Gitlab
remote_update(remote_name, url: url)
end
- def git_delete_refs(*ref_names)
- instructions = ref_names.map do |ref|
- "delete #{ref}\x00\x00"
- end
-
- message, status = run_git(%w[update-ref --stdin -z]) do |stdin|
- stdin.write(instructions.join)
- end
-
- unless status.zero?
- raise GitError.new("Could not delete refs #{ref_names}: #{message}")
- end
- end
-
def gitaly_delete_refs(*ref_names)
gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
end
@@ -1816,41 +1391,6 @@ module Gitlab
raise CommandError, @gitlab_projects.output
end
- def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- ref ||= root_ref
-
- args = %W(
- log #{ref} --pretty=%H --skip #{offset}
- --max-count #{limit} --grep=#{query} --regexp-ignore-case
- )
- args = args.concat(%W(-- #{path})) if path.present?
-
- git_log_results = run_git(args).first.lines
-
- git_log_results.map { |c| commit(c.chomp) }.compact
- end
-
- def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- gitaly_commit_client
- .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
- .map { |c| commit(c) }
- end
-
- def last_commit_for_path_by_gitaly(sha, path)
- gitaly_commit_client.last_commit_for_path(sha, path)
- end
-
- def last_commit_id_for_path_by_shelling_out(sha, path)
- args = %W(rev-list --max-count=1 #{sha} -- #{path})
- run_git_with_timeout(args, Gitlab::Git::Popen::FAST_GIT_PROCESS_TIMEOUT).first.strip
- end
-
- def rugged_merge_base(from, to)
- rugged.merge_base(from, to)
- rescue Rugged::ReferenceError
- nil
- end
-
def rev_list_param(spec)
spec == :all ? ['--all'] : spec
end
@@ -1858,35 +1398,6 @@ module Gitlab
def sha_from_ref(ref)
rev_parse_target(ref).oid
end
-
- def build_git_cmd(*args)
- object_directories = alternate_object_directories.join(File::PATH_SEPARATOR)
-
- env = { 'PWD' => self.path }
- env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = object_directories if object_directories.present?
-
- [
- env,
- ::Gitlab.config.git.bin_path,
- *args,
- { chdir: self.path }
- ]
- end
-
- def git_diff_cmd(old_rev, new_rev)
- old_rev = old_rev == ::Gitlab::Git::BLANK_SHA ? ::Gitlab::Git::EMPTY_TREE_ID : old_rev
-
- build_git_cmd('diff', old_rev, new_rev, '--raw')
- end
-
- def git_cat_file_cmd
- format = '%(objectname) %(objectsize) %(rest)'
- build_git_cmd('cat-file', "--batch-check=#{format}")
- end
-
- def format_git_cat_file_script
- File.expand_path('../support/format-git-cat-file-input', __FILE__)
- end
end
end
end
diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb
index 5fdad077eea..2ba68343aa5 100644
--- a/lib/gitlab/git/rev_list.rb
+++ b/lib/gitlab/git/rev_list.rb
@@ -12,35 +12,12 @@ module Gitlab
end
# This method returns an array of new commit references
- def new_refs
- repository.rev_list(including: newrev, excluding: :all).split("\n")
- end
-
- # Finds newly added objects
- # Returns an array of shas
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
#
- # Can skip objects which do not have a path using required_path: true
- # This skips commit objects and root trees, which might not be needed when
- # looking for blobs
- #
- # When given a block it will yield objects as a lazy enumerator so
- # the caller can limit work done instead of processing megabytes of data
- def new_objects(options: [], require_path: nil, not_in: nil, &lazy_block)
- opts = {
- including: newrev,
- options: options,
- excluding: not_in.nil? ? :all : not_in,
- require_path: require_path
- }
-
- get_objects(opts, &lazy_block)
- end
-
- def all_objects(options: [], require_path: nil, &lazy_block)
- get_objects(including: :all,
- options: options,
- require_path: require_path,
- &lazy_block)
+ def new_refs
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rev_list(including: newrev, excluding: :all).split("\n")
+ end
end
private
diff --git a/lib/gitlab/git/support/format-git-cat-file-input b/lib/gitlab/git/support/format-git-cat-file-input
deleted file mode 100755
index 2e93c646d0f..00000000000
--- a/lib/gitlab/git/support/format-git-cat-file-input
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env ruby
-
-# This script formats the output of the `git diff <old_rev> <new_rev> --raw`
-# command so it can be processed by `git cat-file`
-
-# We need to convert this:
-# ":100644 100644 5f53439... 85bc2f9... R060\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee"
-# To:
-# "85bc2f9 R\tfiles/js/commit.js.coffee\tfiles/js/commit.coffee"
-
-ARGF.each do |line|
- _, _, old_blob_id, new_blob_id, rest = line.split(/\s/, 5)
-
- old_blob_id.gsub!(/[^\h]/, '')
- new_blob_id.gsub!(/[^\h]/, '')
-
- # We can't pass '0000000...' to `git cat-file` given it will not return info about the deleted file
- blob_id = new_blob_id =~ /\A0+\z/ ? old_blob_id : new_blob_id
-
- $stdout.puts "#{blob_id} #{rest}"
-end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index db7c29be94b..35808149b90 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -2,6 +2,8 @@
# class return an instance of `GitlabAccessStatus`
module Gitlab
class GitAccess
+ include Gitlab::Utils::StrongMemoize
+
UnauthorizedError = Class.new(StandardError)
NotFoundError = Class.new(StandardError)
ProjectCreationError = Class.new(StandardError)
@@ -26,7 +28,7 @@ module Gitlab
PUSH_COMMANDS = %w{ git-receive-pack }.freeze
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
- attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type
+ attr_reader :actor, :project, :protocol, :authentication_abilities, :namespace_path, :project_path, :redirected_path, :auth_result_type, :changes
def initialize(actor, project, protocol, authentication_abilities:, namespace_path: nil, project_path: nil, redirected_path: nil, auth_result_type: nil)
@actor = actor
@@ -40,6 +42,8 @@ module Gitlab
end
def check(cmd, changes)
+ @changes = changes
+
check_protocol!
check_valid_actor!
check_active_user!
@@ -58,7 +62,7 @@ module Gitlab
when *DOWNLOAD_COMMANDS
check_download_access!
when *PUSH_COMMANDS
- check_push_access!(changes)
+ check_push_access!
end
true
@@ -218,7 +222,7 @@ module Gitlab
end
end
- def check_push_access!(changes)
+ def check_push_access!
if project.repository_read_only?
raise UnauthorizedError, ERROR_MESSAGES[:read_only]
end
@@ -235,17 +239,15 @@ module Gitlab
return if changes.blank? # Allow access this is needed for EE.
- check_change_access!(changes)
+ check_change_access!
end
- def check_change_access!(changes)
+ def check_change_access!
# If there are worktrees with a HEAD pointing to a non-existent object,
# calls to `git rev-list --all` will fail in git 2.15+. This should also
# clear stale lock files.
project.repository.clean_stale_repository_files
- changes_list = Gitlab::ChangesList.new(changes)
-
# Iterate over all changes to find if user allowed all of them to be applied
changes_list.each.with_index do |change, index|
first_change = index == 0
@@ -321,6 +323,10 @@ module Gitlab
protected
+ def changes_list
+ @changes_list ||= Gitlab::ChangesList.new(changes)
+ end
+
def user
return @user if defined?(@user)
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 66e781a8e5b..58a4060cc96 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -401,8 +401,8 @@ module Gitlab
path.read.chomp
end
- def self.timestamp(t)
- Google::Protobuf::Timestamp.new(seconds: t.to_i)
+ def self.timestamp(time)
+ Google::Protobuf::Timestamp.new(seconds: time.to_i)
end
# The default timeout on all Gitaly calls
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 72e1e59d8df..6a97cd8ed17 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -399,8 +399,8 @@ module Gitlab
end
end
- def encode_repeated(a)
- Google::Protobuf::RepeatedField.new(:bytes, a.map { |s| encode_binary(s) } )
+ def encode_repeated(array)
+ Google::Protobuf::RepeatedField.new(:bytes, array.map { |s| encode_binary(s) } )
end
def call_find_commit(revision)
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index b1a01b185e6..aa7e03301f5 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -25,10 +25,12 @@ module Gitlab
def conflicts?
list_conflict_files.any?
- rescue GRPC::FailedPrecondition
- # The server raises this exception when it encounters ConflictSideMissing, which
- # means a conflict exists but its `theirs` or `ours` data is nil due to a non-existent
- # file in one of the trees.
+ rescue GRPC::FailedPrecondition, GRPC::Unknown
+ # The server raises FailedPrecondition when it encounters
+ # ConflictSideMissing, which means a conflict exists but its `theirs` or
+ # `ours` data is nil due to a non-existent file in one of the trees.
+ #
+ # GRPC::Unknown comes from Rugged::ReferenceError and Rugged::OdbError.
true
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index ab2c61f6782..555733d1834 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -68,6 +68,22 @@ module Gitlab
raise Gitlab::Git::Repository::InvalidRef, ex
end
+ def user_update_branch(branch_name, user, newrev, oldrev)
+ request = Gitaly::UserUpdateBranchRequest.new(
+ repository: @gitaly_repo,
+ branch_name: encode_binary(branch_name),
+ user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
+ newrev: encode_binary(newrev),
+ oldrev: encode_binary(oldrev)
+ )
+
+ response = GitalyClient.call(@repository.storage, :operation_service, :user_update_branch, request)
+
+ if pre_receive_error = response.pre_receive_error.presence
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
+ end
+ end
+
def user_delete_branch(branch_name, user)
request = Gitaly::UserDeleteBranchRequest.new(
repository: @gitaly_repo,
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 982f8d0963b..64b9af4d70c 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -265,17 +265,39 @@ module Gitlab
true
end
- def write_config(full_path:)
- request = Gitaly::WriteConfigRequest.new(repository: @gitaly_repo, full_path: full_path)
- response = GitalyClient.call(
+ def set_config(entries)
+ return if entries.empty?
+
+ request = Gitaly::SetConfigRequest.new(repository: @gitaly_repo)
+ entries.each do |key, value|
+ request.entries << build_set_config_entry(key, value)
+ end
+
+ GitalyClient.call(
+ @storage,
+ :repository_service,
+ :set_config,
+ request,
+ timeout: GitalyClient.fast_timeout
+ )
+
+ nil
+ end
+
+ def delete_config(keys)
+ return if keys.empty?
+
+ request = Gitaly::DeleteConfigRequest.new(repository: @gitaly_repo, keys: keys)
+
+ GitalyClient.call(
@storage,
:repository_service,
- :write_config,
+ :delete_config,
request,
timeout: GitalyClient.fast_timeout
)
- raise Gitlab::Git::OSError.new(response.error) unless response.error.empty?
+ nil
end
def license_short_name
@@ -352,6 +374,23 @@ module Gitlab
timeout: timeout
)
end
+
+ def build_set_config_entry(key, value)
+ entry = Gitaly::SetConfigRequest::Entry.new(key: key)
+
+ case value
+ when String
+ entry.value_str = value
+ when Integer
+ entry.value_int32 = value
+ when TrueClass, FalseClass
+ entry.value_bool = value
+ else
+ raise InvalidArgument, "invalid git config value: #{value.inspect}"
+ end
+
+ entry
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/server_service.rb b/lib/gitlab/gitaly_client/server_service.rb
index 2e1076d1f66..ad898278353 100644
--- a/lib/gitlab/gitaly_client/server_service.rb
+++ b/lib/gitlab/gitaly_client/server_service.rb
@@ -9,7 +9,7 @@ module Gitlab
end
def info
- GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new)
+ GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new, timeout: GitalyClient.fast_timeout)
end
end
end
diff --git a/lib/gitlab/gitaly_client/storage_settings.rb b/lib/gitlab/gitaly_client/storage_settings.rb
index 02fcb413abd..8e530de174d 100644
--- a/lib/gitlab/gitaly_client/storage_settings.rb
+++ b/lib/gitlab/gitaly_client/storage_settings.rb
@@ -60,8 +60,8 @@ module Gitlab
private
- def method_missing(m, *args, &block)
- @hash.public_send(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(msg, *args, &block)
+ @hash.public_send(msg, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 46b49128140..5070f4e3cfe 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -200,27 +200,27 @@ module Gitlab
"Status: #{name}"
end
- def linkify_issues(s)
- s = s.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
- s = s.gsub(/([Cc]omment) #([0-9]+)/, '\1 \2')
- s
+ def linkify_issues(str)
+ str = str.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
+ str = str.gsub(/([Cc]omment) #([0-9]+)/, '\1 \2')
+ str
end
- def escape_for_markdown(s)
+ def escape_for_markdown(str)
# No headings and lists
- s = s.gsub(/^#/, "\\#")
- s = s.gsub(/^-/, "\\-")
+ str = str.gsub(/^#/, "\\#")
+ str = str.gsub(/^-/, "\\-")
# No inline code
- s = s.gsub("`", "\\`")
+ str = str.gsub("`", "\\`")
# Carriage returns make me sad
- s = s.delete("\r")
+ str = str.delete("\r")
# Markdown ignores single newlines, but we need them as <br />.
- s = s.gsub("\n", " \n")
+ str = str.gsub("\n", " \n")
- s
+ str
end
def create_label(name)
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 6d2278d0876..2716834f566 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -39,7 +39,7 @@ module Gitlab
def update_signature!(cached_signature)
using_keychain do |gpg_key|
- cached_signature.update_attributes!(attributes(gpg_key))
+ cached_signature.update!(attributes(gpg_key))
end
@signature = cached_signature
diff --git a/lib/gitlab/http_io.rb b/lib/gitlab/http_io.rb
new file mode 100644
index 00000000000..ce24817db54
--- /dev/null
+++ b/lib/gitlab/http_io.rb
@@ -0,0 +1,193 @@
+##
+# This class is compatible with IO class (https://ruby-doc.org/core-2.3.1/IO.html)
+# source: https://gitlab.com/snippets/1685610
+module Gitlab
+ class HttpIO
+ BUFFER_SIZE = 128.kilobytes
+
+ InvalidURLError = Class.new(StandardError)
+ FailedToGetChunkError = Class.new(StandardError)
+
+ attr_reader :uri, :size
+ attr_reader :tell
+ attr_reader :chunk, :chunk_range
+
+ alias_method :pos, :tell
+
+ def initialize(url, size)
+ raise InvalidURLError unless ::Gitlab::UrlSanitizer.valid?(url)
+
+ @uri = URI(url)
+ @size = size
+ @tell = 0
+ end
+
+ def close
+ # no-op
+ end
+
+ def binmode
+ # no-op
+ end
+
+ def binmode?
+ true
+ end
+
+ def path
+ nil
+ end
+
+ def url
+ @uri.to_s
+ end
+
+ def seek(pos, where = IO::SEEK_SET)
+ new_pos =
+ case where
+ when IO::SEEK_END
+ size + pos
+ when IO::SEEK_SET
+ pos
+ when IO::SEEK_CUR
+ tell + pos
+ else
+ -1
+ end
+
+ raise 'new position is outside of file' if new_pos < 0 || new_pos > size
+
+ @tell = new_pos
+ end
+
+ def eof?
+ tell == size
+ end
+
+ def each_line
+ until eof?
+ line = readline
+ break if line.nil?
+
+ yield(line)
+ end
+ end
+
+ def read(length = nil, outbuf = "")
+ out = ""
+
+ length ||= size - tell
+
+ until length <= 0 || eof?
+ data = get_chunk
+ break if data.empty?
+
+ chunk_bytes = [BUFFER_SIZE - chunk_offset, length].min
+ chunk_data = data.byteslice(0, chunk_bytes)
+
+ out << chunk_data
+ @tell += chunk_data.bytesize
+ length -= chunk_data.bytesize
+ end
+
+ # If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
+ if outbuf
+ outbuf.slice!(0, outbuf.bytesize)
+ outbuf << out
+ end
+
+ out
+ end
+
+ def readline
+ out = ""
+
+ until eof?
+ data = get_chunk
+ new_line = data.index("\n")
+
+ if !new_line.nil?
+ out << data[0..new_line]
+ @tell += new_line + 1
+ break
+ else
+ out << data
+ @tell += data.bytesize
+ end
+ end
+
+ out
+ end
+
+ def write(data)
+ raise NotImplementedError
+ end
+
+ def truncate(offset)
+ raise NotImplementedError
+ end
+
+ def flush
+ raise NotImplementedError
+ end
+
+ def present?
+ true
+ end
+
+ private
+
+ ##
+ # The below methods are not implemented in IO class
+ #
+ def in_range?
+ @chunk_range&.include?(tell)
+ end
+
+ def get_chunk
+ unless in_range?
+ response = Net::HTTP.start(uri.hostname, uri.port, proxy_from_env: true, use_ssl: uri.scheme == 'https') do |http|
+ http.request(request)
+ end
+
+ raise FailedToGetChunkError unless response.code == '200' || response.code == '206'
+
+ @chunk = response.body.force_encoding(Encoding::BINARY)
+ @chunk_range = response.content_range
+
+ ##
+ # Note: If provider does not return content_range, then we set it as we requested
+ # Provider: minio
+ # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # Provider: AWS
+ # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # Provider: GCS
+ # - When the file size is larger than requested Content-range, the Content-range is included in responces with Net::HTTPPartialContent 206
+ # - When the file size is smaller than requested Content-range, the Content-range is included in responces with Net::HTTPOK 200
+ @chunk_range ||= (chunk_start...(chunk_start + @chunk.bytesize))
+ end
+
+ @chunk[chunk_offset..BUFFER_SIZE]
+ end
+
+ def request
+ Net::HTTP::Get.new(uri).tap do |request|
+ request.set_range(chunk_start, BUFFER_SIZE)
+ end
+ end
+
+ def chunk_offset
+ tell % BUFFER_SIZE
+ end
+
+ def chunk_start
+ (tell / BUFFER_SIZE) * BUFFER_SIZE
+ end
+
+ def chunk_end
+ [chunk_start + BUFFER_SIZE, size].min
+ end
+ end
+end
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index 53fe2f8e436..be3710c5b7f 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -40,6 +40,10 @@ module Gitlab
"#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
end
+ def object_storage?
+ Feature.enabled?(:import_export_object_storage)
+ end
+
def version
VERSION
end
diff --git a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
index aef371d81eb..83134bb0769 100644
--- a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
@@ -2,6 +2,7 @@ module Gitlab
module ImportExport
module AfterExportStrategies
class BaseAfterExportStrategy
+ extend Gitlab::ImportExport::CommandLineUtil
include ActiveModel::Validations
extend Forwardable
@@ -24,9 +25,10 @@ module Gitlab
end
def execute(current_user, project)
- return unless project&.export_project_path
-
@project = project
+
+ return unless @project.export_status == :finished
+
@current_user = current_user
if invalid?
@@ -51,9 +53,12 @@ module Gitlab
end
def self.lock_file_path(project)
- return unless project&.export_path
+ return unless project.export_path || object_storage?
- File.join(project.export_path, AFTER_EXPORT_LOCK_FILE_NAME)
+ lock_path = project.import_export_shared.archive_path
+
+ mkdir_p(lock_path)
+ File.join(lock_path, AFTER_EXPORT_LOCK_FILE_NAME)
end
protected
@@ -77,6 +82,10 @@ module Gitlab
def log_validation_errors
errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) }
end
+
+ def object_storage?
+ project.export_project_object_exists?
+ end
end
end
end
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index 938664a95a1..dce8f89c0ab 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -38,14 +38,20 @@ module Gitlab
private
def send_file
- export_file = File.open(project.export_project_path)
-
- Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options(export_file)) # rubocop:disable GitlabSecurity/PublicSend
+ Gitlab::HTTP.public_send(http_method.downcase, url, send_file_options) # rubocop:disable GitlabSecurity/PublicSend
ensure
- export_file.close if export_file
+ export_file.close if export_file && !object_storage?
+ end
+
+ def export_file
+ if object_storage?
+ project.import_export_upload.export_file.file.open
+ else
+ File.open(project.export_project_path)
+ end
end
- def send_file_options(export_file)
+ def send_file_options
{
body_stream: export_file,
headers: headers
@@ -53,7 +59,15 @@ module Gitlab
end
def headers
- { 'Content-Length' => File.size(project.export_project_path).to_s }
+ { 'Content-Length' => export_size.to_s }
+ end
+
+ def export_size
+ if object_storage?
+ project.import_export_upload.export_file.file.size
+ else
+ File.size(project.export_project_path)
+ end
end
end
end
diff --git a/lib/gitlab/import_export/avatar_saver.rb b/lib/gitlab/import_export/avatar_saver.rb
index 998c21e2586..31ef0490cb3 100644
--- a/lib/gitlab/import_export/avatar_saver.rb
+++ b/lib/gitlab/import_export/avatar_saver.rb
@@ -11,7 +11,12 @@ module Gitlab
def save
return true unless @project.avatar.exists?
- copy_files(avatar_path, avatar_export_path)
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared,
+ relative_export_path: 'avatar',
+ from: avatar_path
+ ).save
rescue => e
@shared.error(e)
false
@@ -19,10 +24,6 @@ module Gitlab
private
- def avatar_export_path
- File.join(@shared.export_path, 'avatar', @project.avatar_identifier)
- end
-
def avatar_path
@project.avatar.path
end
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index 8b8e48aac76..ac827cbe1ca 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -47,7 +47,7 @@ module Gitlab
def ensure_default_member!
@project.project_members.destroy_all
- ProjectMember.create!(user: @user, access_level: ProjectMember::MASTER, source_id: @project.id, importing: true)
+ ProjectMember.create!(user: @user, access_level: ProjectMember::MAINTAINER, source_id: @project.id, importing: true)
end
def add_team_member(member, existing_user = nil)
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index 2daeba90a51..3cd153a4fd2 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -15,15 +15,22 @@ module Gitlab
def save
if compress_and_save
remove_export_path
+
Rails.logger.info("Saved project export #{archive_file}")
- archive_file
+
+ save_on_object_storage if use_object_storage?
else
- @shared.error(Gitlab::ImportExport::Error.new("Unable to save #{archive_file} into #{@shared.export_path}"))
+ @shared.error(Gitlab::ImportExport::Error.new(error_message))
false
end
rescue => e
@shared.error(e)
false
+ ensure
+ if use_object_storage?
+ remove_archive
+ remove_export_path
+ end
end
private
@@ -36,9 +43,29 @@ module Gitlab
FileUtils.rm_rf(@shared.export_path)
end
+ def remove_archive
+ FileUtils.rm_rf(@shared.archive_path)
+ end
+
def archive_file
@archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
end
+
+ def save_on_object_storage
+ upload = ImportExportUpload.find_or_initialize_by(project: @project)
+
+ File.open(archive_file) { |file| upload.export_file = file }
+
+ upload.save!
+ end
+
+ def use_object_storage?
+ Gitlab::ImportExport.object_storage?
+ end
+
+ def error_message
+ "Unable to save #{archive_file} into #{@shared.export_path}. Object storage enabled: #{use_object_storage?}"
+ end
end
end
end
diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb
new file mode 100644
index 00000000000..1110149712d
--- /dev/null
+++ b/lib/gitlab/import_export/uploads_manager.rb
@@ -0,0 +1,101 @@
+module Gitlab
+ module ImportExport
+ class UploadsManager
+ include Gitlab::ImportExport::CommandLineUtil
+
+ UPLOADS_BATCH_SIZE = 100
+
+ def initialize(project:, shared:, relative_export_path: 'uploads', from: nil)
+ @project = project
+ @shared = shared
+ @relative_export_path = relative_export_path
+ @from = from || default_uploads_path
+ end
+
+ def save
+ copy_files(@from, uploads_export_path) if File.directory?(@from)
+
+ if File.file?(@from) && @relative_export_path == 'avatar'
+ copy_files(@from, File.join(uploads_export_path, @project.avatar.filename))
+ end
+
+ copy_from_object_storage
+
+ true
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ def restore
+ Dir["#{uploads_export_path}/**/*"].each do |upload|
+ next if File.directory?(upload)
+
+ add_upload(upload)
+ end
+
+ true
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ private
+
+ def add_upload(upload)
+ uploader_context = FileUploader.extract_dynamic_path(upload).named_captures.symbolize_keys
+
+ UploadService.new(@project, File.open(upload, 'r'), FileUploader, uploader_context).execute
+ end
+
+ def copy_from_object_storage
+ return unless Gitlab::ImportExport.object_storage?
+
+ each_uploader do |uploader|
+ next unless uploader.file
+ next if uploader.upload.local? # Already copied, using the old method
+
+ download_and_copy(uploader)
+ end
+ end
+
+ def default_uploads_path
+ FileUploader.absolute_base_dir(@project)
+ end
+
+ def uploads_export_path
+ @uploads_export_path ||= File.join(@shared.export_path, @relative_export_path)
+ end
+
+ def each_uploader
+ avatar_path = @project.avatar&.upload&.path
+
+ if @relative_export_path == 'avatar'
+ yield(@project.avatar)
+ else
+ project_uploads_except_avatar(avatar_path).find_each(batch_size: UPLOADS_BATCH_SIZE) do |upload|
+ yield(upload.build_uploader)
+ end
+ end
+ end
+
+ def project_uploads_except_avatar(avatar_path)
+ return @project.uploads unless avatar_path
+
+ @project.uploads.where("path != ?", avatar_path)
+ end
+
+ def download_and_copy(upload)
+ secret = upload.try(:secret) || ''
+ upload_path = File.join(uploads_export_path, secret, upload.filename)
+
+ mkdir_p(File.join(uploads_export_path, secret))
+
+ File.open(upload_path, 'w') do |file|
+ # Download (stream) file from the uploader's location
+ IO.copy_stream(URI.parse(upload.file.url).open, file)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/uploads_restorer.rb b/lib/gitlab/import_export/uploads_restorer.rb
index df19354b76e..25f85936227 100644
--- a/lib/gitlab/import_export/uploads_restorer.rb
+++ b/lib/gitlab/import_export/uploads_restorer.rb
@@ -2,13 +2,30 @@ module Gitlab
module ImportExport
class UploadsRestorer < UploadsSaver
def restore
- return true unless File.directory?(uploads_export_path)
+ if Gitlab::ImportExport.object_storage?
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).restore
+ elsif File.directory?(uploads_export_path)
+ copy_files(uploads_export_path, uploads_path)
- copy_files(uploads_export_path, uploads_path)
+ true
+ else
+ true # Proceed without uploads
+ end
rescue => e
@shared.error(e)
false
end
+
+ def uploads_path
+ FileUploader.absolute_base_dir(@project)
+ end
+
+ def uploads_export_path
+ @uploads_export_path ||= File.join(@shared.export_path, 'uploads')
+ end
end
end
end
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index 2f08dda55fd..b3f17af5661 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -9,21 +9,14 @@ module Gitlab
end
def save
- return true unless File.directory?(uploads_path)
-
- copy_files(uploads_path, uploads_export_path)
+ Gitlab::ImportExport::UploadsManager.new(
+ project: @project,
+ shared: @shared
+ ).save
rescue => e
@shared.error(e)
false
end
-
- def uploads_path
- FileUploader.absolute_base_dir(@project)
- end
-
- def uploads_export_path
- File.join(@shared.export_path, 'uploads')
- end
end
end
end
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 60d5fa4d29a..af9b880ef9e 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -16,7 +16,8 @@ module Gitlab
ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
ImportSource.new('git', 'Repo by URL', nil),
ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
- ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer)
+ ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer),
+ ImportSource.new('manifest', 'Manifest file', nil)
].freeze
class << self
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index da43bd0af4b..15c5ece2350 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -1,6 +1,10 @@
module Gitlab
# Helper methods to do with Kubernetes network services & resources
module Kubernetes
+ def self.build_header_hash
+ Hash.new { |h, k| h[k] = [] }
+ end
+
# This is the comand that is run to start a terminal session. Kubernetes
# expects `command=foo&command=bar, not `command[]=foo&command[]=bar`
EXEC_COMMAND = URI.encode_www_form(
@@ -37,13 +41,14 @@ module Gitlab
selectors: { pod: pod_name, container: container["name"] },
url: container_exec_url(api_url, namespace, pod_name, container["name"]),
subprotocols: ['channel.k8s.io'],
- headers: Hash.new { |h, k| h[k] = [] },
+ headers: ::Gitlab::Kubernetes.build_header_hash,
created_at: created_at
}
end
end
def add_terminal_auth(terminal, token:, max_session_time:, ca_pem: nil)
+ terminal[:headers] ||= ::Gitlab::Kubernetes.build_header_hash
terminal[:headers]['Authorization'] << "Bearer #{token}"
terminal[:max_session_time] = max_session_time
terminal[:ca_pem] = ca_pem if ca_pem.present?
diff --git a/lib/gitlab/kubernetes/helm/base_command.rb b/lib/gitlab/kubernetes/helm/base_command.rb
index 3d778da90c7..f9ebe53d6af 100644
--- a/lib/gitlab/kubernetes/helm/base_command.rb
+++ b/lib/gitlab/kubernetes/helm/base_command.rb
@@ -18,7 +18,7 @@ module Gitlab
ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- apk add -U ca-certificates openssl >/dev/null
+ apk add -U wget ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
HEREDOC
diff --git a/lib/gitlab/logger.rb b/lib/gitlab/logger.rb
index a42e312b5d3..e58927a40b9 100644
--- a/lib/gitlab/logger.rb
+++ b/lib/gitlab/logger.rb
@@ -4,10 +4,18 @@ module Gitlab
file_name_noext + '.log'
end
+ def self.debug(message)
+ build.debug(message)
+ end
+
def self.error(message)
build.error(message)
end
+ def self.warn(message)
+ build.warn(message)
+ end
+
def self.info(message)
build.info(message)
end
diff --git a/lib/gitlab/mail_room.rb b/lib/gitlab/mail_room.rb
index 344784c866f..db04356a5e9 100644
--- a/lib/gitlab/mail_room.rb
+++ b/lib/gitlab/mail_room.rb
@@ -53,7 +53,7 @@ module Gitlab
end
def config_file
- ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] || File.expand_path('../../../config/gitlab.yml', __FILE__)
+ ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] || File.expand_path('../../config/gitlab.yml', __dir__)
end
end
end
diff --git a/lib/gitlab/manifest_import/manifest.rb b/lib/gitlab/manifest_import/manifest.rb
new file mode 100644
index 00000000000..4d6034fb956
--- /dev/null
+++ b/lib/gitlab/manifest_import/manifest.rb
@@ -0,0 +1,81 @@
+# Class to parse manifest file and build a list of repositories for import
+#
+# <manifest>
+# <remote review="https://android-review.googlesource.com/" />
+# <project path="platform-common" name="platform" />
+# <project path="platform/art" name="platform/art" />
+# <project path="platform/device" name="platform/device" />
+# </manifest>
+#
+# 1. Project path must be uniq and can't be part of other project path.
+# For example, you can't have projects with 'foo' and 'foo/bar' paths.
+# 2. Remote must be present with review attribute so GitLab knows
+# where to fetch source code
+module Gitlab
+ module ManifestImport
+ class Manifest
+ attr_reader :parsed_xml, :errors
+
+ def initialize(file)
+ @parsed_xml = Nokogiri::XML(file) { |config| config.strict }
+ @errors = []
+ rescue Nokogiri::XML::SyntaxError
+ @errors = ['The uploaded file is not a valid XML file.']
+ end
+
+ def projects
+ raw_projects.each_with_index.map do |project, i|
+ {
+ id: i,
+ name: project['name'],
+ path: project['path'],
+ url: repository_url(project['name'])
+ }
+ end
+ end
+
+ def valid?
+ return false if @errors.any?
+
+ unless validate_remote
+ @errors << 'Make sure a <remote> tag is present and is valid.'
+ end
+
+ unless validate_projects
+ @errors << 'Make sure every <project> tag has name and path attributes.'
+ end
+
+ @errors.empty?
+ end
+
+ private
+
+ def validate_remote
+ remote.present? && URI.parse(remote).host
+ rescue URI::Error
+ false
+ end
+
+ def validate_projects
+ raw_projects.all? do |project|
+ project['name'] && project['path']
+ end
+ end
+
+ def repository_url(name)
+ URI.join(remote, name).to_s
+ end
+
+ def remote
+ return @remote if defined?(@remote)
+
+ remote_tag = parsed_xml.css('manifest > remote').first
+ @remote = remote_tag['review'] if remote_tag
+ end
+
+ def raw_projects
+ @raw_projects ||= parsed_xml.css('manifest > project')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/manifest_import/project_creator.rb b/lib/gitlab/manifest_import/project_creator.rb
new file mode 100644
index 00000000000..b5967c93735
--- /dev/null
+++ b/lib/gitlab/manifest_import/project_creator.rb
@@ -0,0 +1,41 @@
+module Gitlab
+ module ManifestImport
+ class ProjectCreator
+ attr_reader :repository, :destination, :current_user
+
+ def initialize(repository, destination, current_user)
+ @repository = repository
+ @destination = destination
+ @current_user = current_user
+ end
+
+ def execute
+ group_full_path, _, project_path = repository[:path].rpartition('/')
+ group_full_path = File.join(destination.full_path, group_full_path) if destination
+ group = create_group_with_parents(group_full_path)
+
+ params = {
+ import_url: repository[:url],
+ import_type: 'manifest',
+ namespace_id: group.id,
+ path: project_path,
+ name: project_path,
+ visibility_level: destination.visibility_level
+ }
+
+ Projects::CreateService.new(current_user, params).execute
+ end
+
+ private
+
+ def create_group_with_parents(full_path)
+ params = {
+ group_path: full_path,
+ visibility_level: destination.visibility_level
+ }
+
+ Groups::NestedCreateService.new(current_user, params).execute
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 66f30e3b397..04135dac4ff 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -162,7 +162,6 @@ module Gitlab
# When enabled this should be set before being used as the usual pattern
# "@foo ||= bar" is _not_ thread-safe.
- # rubocop:disable Gitlab/ModuleWithInstanceVariables
def pool
if influx_metrics_enabled?
if @pool.nil?
@@ -180,7 +179,6 @@ module Gitlab
@pool
end
end
- # rubocop:enable Gitlab/ModuleWithInstanceVariables
end
end
end
diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb
index b11520a79bb..f3290e3149c 100644
--- a/lib/gitlab/metrics/method_call.rb
+++ b/lib/gitlab/metrics/method_call.rb
@@ -1,5 +1,3 @@
-# rubocop:disable Style/ClassVars
-
module Gitlab
module Metrics
# Class for tracking timing information about method calls
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index 38f119cf06d..c205f348023 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -20,7 +20,7 @@ module Gitlab
define_histogram :gitlab_sql_duration_seconds do
docstring 'SQL time'
base_labels Transaction::BASE_LABELS
- buckets [0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
+ buckets [0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
end
def current_transaction
diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb
index 9753be6d5c3..18f91db98fc 100644
--- a/lib/gitlab/middleware/multipart.rb
+++ b/lib/gitlab/middleware/multipart.rb
@@ -84,7 +84,7 @@ module Gitlab
def open_file(params, key)
::UploadedFile.from_params(
params, key,
- Gitlab.config.uploads.storage_path)
+ [FileUploader.root, Gitlab.config.uploads.storage_path])
end
end
diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb
index 4a99b7cca5c..8dca431c005 100644
--- a/lib/gitlab/middleware/read_only/controller.rb
+++ b/lib/gitlab/middleware/read_only/controller.rb
@@ -69,6 +69,7 @@ module Gitlab
@route_hash ||= Rails.application.routes.recognize_path(request.url, { method: request.request_method }) rescue {}
end
+ # Overridden in EE module
def whitelisted_routes
grack_route || ReadOnly.internal_routes.any? { |path| request.path.include?(path) } || lfs_route || sidekiq_route
end
diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb
index b9832a724c4..d0cb7c1a7cf 100644
--- a/lib/gitlab/popen.rb
+++ b/lib/gitlab/popen.rb
@@ -34,11 +34,16 @@ module Gitlab
start = Time.now
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
+ # stderr and stdout pipes can block if stderr/stdout aren't drained: https://bugs.ruby-lang.org/issues/9082
+ # Mimic what Ruby does with capture3: https://github.com/ruby/ruby/blob/1ec544695fa02d714180ef9c34e755027b6a2103/lib/open3.rb#L257-L273
+ out_reader = Thread.new { stdout.read }
+ err_reader = Thread.new { stderr.read }
+
yield(stdin) if block_given?
stdin.close
- cmd_stdout = stdout.read
- cmd_stderr = stderr.read
+ cmd_stdout = out_reader.value
+ cmd_stderr = err_reader.value
cmd_status = wait_thr.value
end
diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb
index 15b8beacf60..e3da1634fa5 100644
--- a/lib/gitlab/project_authorizations/with_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/with_nested_groups.rb
@@ -24,7 +24,7 @@ module Gitlab
user.projects.select_for_project_authorization,
# The personal projects of the user.
- user.personal_projects.select_as_master_for_project_authorization,
+ user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects that belong directly to any of the groups the user has
# access to.
diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb
index ad87540e6c2..7d0c00c7f36 100644
--- a/lib/gitlab/project_authorizations/without_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/without_nested_groups.rb
@@ -15,7 +15,7 @@ module Gitlab
user.projects.select_for_project_authorization,
# Personal projects
- user.personal_projects.select_as_master_for_project_authorization,
+ user.personal_projects.select_as_maintainer_for_project_authorization,
# Projects of groups the user is a member of
user.groups_projects.select_for_project_authorization,
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 5cedd9e84c2..a17cd27e82d 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -74,17 +74,10 @@ module Gitlab
relative_path = name.dup
relative_path << '.git' unless relative_path.end_with?('.git')
- gitaly_migrate(:create_repository,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- repository = Gitlab::Git::Repository.new(storage, relative_path, '')
- repository.gitaly_repository_client.create_repository
- true
- else
- repo_path = File.join(Gitlab.config.repositories.storages[storage].legacy_disk_path, relative_path)
- Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: gitlab_shell_hooks_path)
- end
- end
+ repository = Gitlab::Git::Repository.new(storage, relative_path, '')
+ wrapped_gitaly_errors { repository.gitaly_repository_client.create_repository }
+
+ true
rescue => err # Once the Rugged codes gets removes this can be improved
Rails.logger.error("Failed to add repository #{storage}/#{name}: #{err}")
false
@@ -99,21 +92,13 @@ module Gitlab
# Ex.
# import_repository("nfs-file06", "gitlab/gitlab-ci", "https://gitlab.com/gitlab-org/gitlab-test.git")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/874
def import_repository(storage, name, url)
if url.start_with?('.', '/')
raise Error.new("don't use disk paths with import_repository: #{url.inspect}")
end
relative_path = "#{name}.git"
- cmd = gitaly_migrate(:import_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- GitalyGitlabProjects.new(storage, relative_path)
- else
- # The timeout ensures the subprocess won't hang forever
- gitlab_projects(storage, relative_path)
- end
- end
+ cmd = GitalyGitlabProjects.new(storage, relative_path)
success = cmd.import_project(url, git_timeout)
raise Error, cmd.output unless success
@@ -133,12 +118,8 @@ module Gitlab
# fetch_remote(my_repo, "upstream")
#
def fetch_remote(repository, remote, ssh_auth: nil, forced: false, no_tags: false, prune: true)
- gitaly_migrate(:fetch_remote) do |is_enabled|
- if is_enabled
- repository.gitaly_repository_client.fetch_remote(remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, timeout: git_timeout, prune: prune)
- else
- local_fetch_remote(repository.storage, repository.relative_path, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, prune: prune)
- end
+ wrapped_gitaly_errors do
+ repository.gitaly_repository_client.fetch_remote(remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, timeout: git_timeout, prune: prune)
end
end
@@ -396,28 +377,6 @@ module Gitlab
)
end
- def local_fetch_remote(storage_name, repository_relative_path, remote, ssh_auth: nil, forced: false, no_tags: false, prune: true)
- vars = { force: forced, tags: !no_tags, prune: prune }
-
- if ssh_auth&.ssh_import?
- if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
- vars[:ssh_key] = ssh_auth.ssh_private_key
- end
-
- if ssh_auth.ssh_known_hosts.present?
- vars[:known_hosts] = ssh_auth.ssh_known_hosts
- end
- end
-
- cmd = gitlab_projects(storage_name, repository_relative_path)
-
- success = cmd.fetch_remote(remote, git_timeout, vars)
-
- raise Error, cmd.output unless success
-
- success
- end
-
def gitlab_shell_fast_execute(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
@@ -447,8 +406,8 @@ module Gitlab
Gitlab.config.gitlab_shell.git_timeout
end
- def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
- Gitlab::GitalyClient.migrate(method, status: status, &block)
+ def wrapped_gitaly_errors
+ yield
rescue GRPC::NotFound, GRPC::BadStatus => e
# Old Popen code returns [Error, output] to the caller, so we
# need to do the same here...
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index 59331c827af..de8b6ec69ce 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -58,7 +58,7 @@ module Gitlab
if raw_credentials.present?
url.sub!("#{raw_credentials}@", '')
- user, password = raw_credentials.split(':')
+ user, _, password = raw_credentials.partition(':')
@credentials ||= { user: user.presence, password: password.presence }
end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index e893e46ee86..a9629a92a50 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -37,21 +37,14 @@ module Gitlab
end
def send_git_blob(repository, blob)
- params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_raw_show, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
- {
- 'GitalyServer' => gitaly_server_hash(repository),
- 'GetBlobRequest' => {
- repository: repository.gitaly_repository.to_h,
- oid: blob.id,
- limit: -1
- }
- }
- else
- {
- 'RepoPath' => repository.path_to_repo,
- 'BlobId' => blob.id
- }
- end
+ params = {
+ 'GitalyServer' => gitaly_server_hash(repository),
+ 'GetBlobRequest' => {
+ repository: repository.gitaly_repository.to_h,
+ oid: blob.id,
+ limit: -1
+ }
+ }
[
SEND_DATA_HEADER,
@@ -91,16 +84,12 @@ module Gitlab
end
def send_git_diff(repository, diff_refs)
- params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_send_git_diff, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
- {
- 'GitalyServer' => gitaly_server_hash(repository),
- 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
- gitaly_diff_or_patch_hash(repository, diff_refs)
- ).to_json
- }
- else
- workhorse_diff_or_patch_hash(repository, diff_refs)
- end
+ params = {
+ 'GitalyServer' => gitaly_server_hash(repository),
+ 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
+ gitaly_diff_or_patch_hash(repository, diff_refs)
+ ).to_json
+ }
[
SEND_DATA_HEADER,
@@ -109,16 +98,12 @@ module Gitlab
end
def send_git_patch(repository, diff_refs)
- params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_send_git_patch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
- {
- 'GitalyServer' => gitaly_server_hash(repository),
- 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
- gitaly_diff_or_patch_hash(repository, diff_refs)
- ).to_json
- }
- else
- workhorse_diff_or_patch_hash(repository, diff_refs)
- end
+ params = {
+ 'GitalyServer' => gitaly_server_hash(repository),
+ 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
+ gitaly_diff_or_patch_hash(repository, diff_refs)
+ ).to_json
+ }
[
SEND_DATA_HEADER,
@@ -231,14 +216,6 @@ module Gitlab
}
end
- def workhorse_diff_or_patch_hash(repository, diff_refs)
- {
- 'RepoPath' => repository.path_to_repo,
- 'ShaFrom' => diff_refs.base_sha,
- 'ShaTo' => diff_refs.head_sha
- }
- end
-
def gitaly_diff_or_patch_hash(repository, diff_refs)
{
repository: repository.gitaly_repository,
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index be0d97370d0..e877ab10248 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -11,7 +11,7 @@ module Rouge
@tag = tag
end
- def stream(tokens, &b)
+ def stream(tokens)
is_first = true
token_lines(tokens) do |line|
yield "\n" unless is_first
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index 21998dd2f5b..f431352b61e 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -19,6 +19,29 @@ namespace :gettext do
Rake::Task['gettext:po_to_json'].invoke
end
+ task :regenerate do
+ pot_file = 'locale/gitlab.pot'
+ # Remove all translated files, this speeds up finding
+ FileUtils.rm Dir['locale/**/gitlab.*']
+ # remove the `pot` file to ensure it's completely regenerated
+ FileUtils.rm_f pot_file
+
+ Rake::Task['gettext:find'].invoke
+
+ # leave only the required changes.
+ `git checkout -- locale/*/gitlab.po`
+
+ # Remove timestamps from the pot file
+ pot_content = File.read pot_file
+ pot_content.gsub!(/^"POT?\-(?:Creation|Revision)\-Date\:.*\n/, '')
+ File.write pot_file, pot_content
+
+ puts <<~MSG
+ All done. Please commit the changes to `locale/gitlab.pot`.
+
+ MSG
+ end
+
desc 'Lint all po files in `locale/'
task lint: :environment do
require 'simple_po_parser'
@@ -50,13 +73,12 @@ namespace :gettext do
end
task :updated_check do
+ pot_file = 'locale/gitlab.pot'
# Removing all pre-translated files speeds up `gettext:find` as the
# files don't need to be merged.
# Having `LC_MESSAGES/gitlab.mo files present also confuses the output.
FileUtils.rm Dir['locale/**/gitlab.*']
-
- # Make sure we start out with a clean pot.file
- `git checkout -- locale/gitlab.pot`
+ FileUtils.rm_f pot_file
# `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these.
@@ -64,18 +86,18 @@ namespace :gettext do
Rake::Task['gettext:find'].invoke
end
- pot_diff = `git diff -- locale/gitlab.pot`.strip
+ pot_diff = `git diff -- #{pot_file} | grep -E '^(\\+|-)msgid'`.strip
# reset the locale folder for potential next tasks
`git checkout -- locale`
if pot_diff.present?
raise <<~MSG
- Newly translated strings found, please add them to `gitlab.pot` by running:
+ Newly translated strings found, please add them to `#{pot_file}` by running:
- rm locale/**/gitlab.*; bin/rake gettext:find; git checkout -- locale/*/gitlab.po
+ bin/rake gettext:regenerate
- Then commit and push the resulting changes to `locale/gitlab.pot`.
+ Then commit and push the resulting changes to `#{pot_file}`.
The diff was:
diff --git a/lib/tasks/gitlab/bulk_add_permission.rake b/lib/tasks/gitlab/bulk_add_permission.rake
index 83dd870fa31..26cbf0740b6 100644
--- a/lib/tasks/gitlab/bulk_add_permission.rake
+++ b/lib/tasks/gitlab/bulk_add_permission.rake
@@ -1,6 +1,6 @@
namespace :gitlab do
namespace :import do
- desc "GitLab | Add all users to all projects (admin users are added as masters)"
+ desc "GitLab | Add all users to all projects (admin users are added as maintainers)"
task all_users_to_all_projects: :environment do |t, args|
user_ids = User.where(admin: false).pluck(:id)
admin_ids = User.where(admin: true).pluck(:id)
@@ -10,7 +10,7 @@ namespace :gitlab do
ProjectMember.add_users_to_projects(project_ids, user_ids, ProjectMember::DEVELOPER)
puts "Importing #{admin_ids.size} admins into #{project_ids.size} projects"
- ProjectMember.add_users_to_projects(project_ids, admin_ids, ProjectMember::MASTER)
+ ProjectMember.add_users_to_projects(project_ids, admin_ids, ProjectMember::MAINTAINER)
end
desc "GitLab | Add a specific user to all projects (as a developer)"
diff --git a/lib/tasks/gitlab/uploads/migrate.rake b/lib/tasks/gitlab/uploads/migrate.rake
index 78e18992a8e..f548a266b99 100644
--- a/lib/tasks/gitlab/uploads/migrate.rake
+++ b/lib/tasks/gitlab/uploads/migrate.rake
@@ -8,7 +8,7 @@ namespace :gitlab do
@uploader_class = args.uploader_class.constantize
@model_class = args.model_class.constantize
- uploads.each_batch(of: batch_size, &method(:enqueue_batch)) # rubocop: disable Cop/InBatches
+ uploads.each_batch(of: batch_size, &method(:enqueue_batch))
end
def enqueue_batch(batch, index)
diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb
index 5dc85b2baea..4b9cb59eab5 100644
--- a/lib/uploaded_file.rb
+++ b/lib/uploaded_file.rb
@@ -28,7 +28,7 @@ class UploadedFile
@tempfile = File.new(path, 'rb')
end
- def self.from_params(params, field, upload_path)
+ def self.from_params(params, field, upload_paths)
unless params["#{field}.path"]
raise InvalidPathError, "file is invalid" if params["#{field}.remote_id"]
@@ -37,7 +37,8 @@ class UploadedFile
file_path = File.realpath(params["#{field}.path"])
- unless self.allowed_path?(file_path, [upload_path, Dir.tmpdir].compact)
+ paths = Array(upload_paths) << Dir.tmpdir
+ unless self.allowed_path?(file_path, paths.compact)
raise InvalidPathError, "insecure path used '#{file_path}'"
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0385761de45..1b68156a503 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,6 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-07-01 21:24+1000\n"
-"PO-Revision-Date: 2018-07-01 21:24+1000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -79,6 +77,9 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
+msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -128,7 +129,7 @@ msgstr ""
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
-msgid "(checkout the %{link} for information on how to install it)."
+msgid "(check out the %{link} for information on how to install it)."
msgstr ""
msgid "+ %{moreCount} more"
@@ -204,6 +205,21 @@ msgstr ""
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr ""
+msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"John Smith\"</code> will add \"By John Smith\" to all issues and comments originally created by johnsmith@example.com."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsm...@example.com\"</code> will add \"By johnsm...@example.com\" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy."
+msgstr ""
+
+msgid "<code>\"johnsmith@example.com\": \"johnsmith@example.com\"</code> will add \"By <a href=\"#\">johnsmith@example.com</a>\" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address."
+msgstr ""
+
+msgid "<strong>%{group_name}</strong> group members"
+msgstr ""
+
msgid "<strong>Removes</strong> source branch"
msgstr ""
@@ -240,6 +256,9 @@ msgstr ""
msgid "Access Tokens"
msgstr ""
+msgid "Access denied! Please verify you can add deploy keys to this repository."
+msgstr ""
+
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
@@ -276,6 +295,9 @@ msgstr ""
msgid "Add Readme"
msgstr ""
+msgid "Add new application"
+msgstr ""
+
msgid "Add new directory"
msgstr ""
@@ -285,6 +307,12 @@ msgstr ""
msgid "Add todo"
msgstr ""
+msgid "Add user(s) to the group:"
+msgstr ""
+
+msgid "Add users to group"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -369,9 +397,27 @@ msgstr ""
msgid "Alternatively, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
+msgid "An application called %{link_to_client} is requesting access to your GitLab account."
+msgstr ""
+
+msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
+msgstr ""
+
+msgid "An error accured whilst committing your changes."
+msgstr ""
+
+msgid "An error has occurred"
+msgstr ""
+
msgid "An error occured creating the new branch."
msgstr ""
+msgid "An error occured whilst fetching the job trace."
+msgstr ""
+
+msgid "An error occured whilst fetching the latest pipline."
+msgstr ""
+
msgid "An error occured whilst loading all the files."
msgstr ""
@@ -390,6 +436,9 @@ msgstr ""
msgid "An error occured whilst loading the merge request."
msgstr ""
+msgid "An error occured whilst loading the pipelines jobs."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -453,9 +502,24 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "Anonymous"
+msgstr ""
+
+msgid "Any"
+msgstr ""
+
msgid "Appearance"
msgstr ""
+msgid "Application"
+msgstr ""
+
+msgid "Application Id"
+msgstr ""
+
+msgid "Application: %{name}"
+msgstr ""
+
msgid "Applications"
msgstr ""
@@ -471,6 +535,9 @@ msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Are you sure you want to remove %{group_name}?"
+msgstr ""
+
msgid "Are you sure you want to remove this identity?"
msgstr ""
@@ -534,6 +601,24 @@ msgstr ""
msgid "Author"
msgstr ""
+msgid "Authorization code:"
+msgstr ""
+
+msgid "Authorization was granted by entering your username and password in the application."
+msgstr ""
+
+msgid "Authorize"
+msgstr ""
+
+msgid "Authorize %{link_to_client} to use your account?"
+msgstr ""
+
+msgid "Authorized At"
+msgstr ""
+
+msgid "Authorized applications (%{size})"
+msgstr ""
+
msgid "Authors: %{authors}"
msgstr ""
@@ -675,6 +760,12 @@ msgstr ""
msgid "Below are examples of regex for existing tools:"
msgstr ""
+msgid "Below you will find all the groups that are public."
+msgstr ""
+
+msgid "Bitbucket import"
+msgstr ""
+
msgid "Boards"
msgstr ""
@@ -890,6 +981,12 @@ msgstr ""
msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
msgstr ""
+msgid "Callback URL"
+msgstr ""
+
+msgid "Callback url"
+msgstr ""
+
msgid "Can't find HEAD commit for this branch"
msgstr ""
@@ -950,6 +1047,12 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose <strong>Create archive</strong> and wait for archiving to complete."
+msgstr ""
+
+msgid "Choose <strong>Next</strong> at the bottom of the page."
+msgstr ""
+
msgid "Choose File ..."
msgstr ""
@@ -965,6 +1068,9 @@ msgstr ""
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which repositories you want to import."
msgstr ""
@@ -1058,9 +1164,15 @@ msgstr ""
msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
msgstr ""
+msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
+msgstr ""
+
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr ""
+msgid "Click the <strong>Select none</strong> button on the right, since we only need \"Google Code Project Hosting\"."
+msgstr ""
+
msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr ""
@@ -1070,6 +1182,9 @@ msgstr ""
msgid "Click to expand text"
msgstr ""
+msgid "Clients"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -1085,9 +1200,6 @@ msgstr ""
msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Add an existing Kubernetes cluster"
-msgstr ""
-
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
@@ -1109,9 +1221,6 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
@@ -1139,18 +1248,6 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
-msgstr ""
-
-msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
-msgstr ""
-
-msgid "ClusterIntegration|Create on Google Kubernetes Engine"
-msgstr ""
-
-msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1623,6 +1720,9 @@ msgstr ""
msgid "Continue"
msgstr ""
+msgid "Continue to the next step"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
@@ -1650,6 +1750,9 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control the display of third party offers."
+msgstr ""
+
msgid "Copy URL to clipboard"
msgstr ""
@@ -1662,9 +1765,6 @@ msgstr ""
msgid "Copy commit SHA to clipboard"
msgstr ""
-msgid "Copy file name to clipboard"
-msgstr ""
-
msgid "Copy file path to clipboard"
msgstr ""
@@ -1704,6 +1804,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create group"
+msgstr ""
+
msgid "Create group label"
msgstr ""
@@ -1725,6 +1828,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new file or directory"
+msgstr ""
+
msgid "Create new label"
msgstr ""
@@ -1746,9 +1852,15 @@ msgstr ""
msgid "Created"
msgstr ""
+msgid "Created At"
+msgstr ""
+
msgid "Created by me"
msgstr ""
+msgid "Created on:"
+msgstr ""
+
msgid "Cron Timezone"
msgstr ""
@@ -1770,6 +1882,12 @@ msgstr ""
msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
msgstr ""
+msgid "Customize how FogBugz email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
+msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
msgid "Cycle Analytics"
msgstr ""
@@ -1809,6 +1927,12 @@ msgstr ""
msgid "Decline and sign out"
msgstr ""
+msgid "Default: Directly import the Google Code email address or username"
+msgstr ""
+
+msgid "Default: Map a FogBugz account ID to a full name"
+msgstr ""
+
msgid "Define a custom pattern with cron syntax"
msgstr ""
@@ -1821,6 +1945,9 @@ msgstr ""
msgid "Delete list"
msgstr ""
+msgid "Deny"
+msgstr ""
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] ""
@@ -1955,6 +2082,12 @@ msgstr ""
msgid "Description"
msgstr ""
+msgid "Description:"
+msgstr ""
+
+msgid "Destroy"
+msgstr ""
+
msgid "Details"
msgstr ""
@@ -1982,9 +2115,18 @@ msgstr ""
msgid "Discard draft"
msgstr ""
+msgid "Discover projects, groups and snippets. Share your projects with others"
+msgstr ""
+
+msgid "Dismiss"
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
+msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
+msgstr ""
+
msgid "Domain"
msgstr ""
@@ -2042,9 +2184,15 @@ msgstr ""
msgid "Edit Snippet"
msgstr ""
+msgid "Edit application"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
+msgid "Edit group: %{group_name}"
+msgstr ""
+
msgid "Edit identity for %{user_name}"
msgstr ""
@@ -2105,9 +2253,18 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while stopping the environment, please try again"
+msgstr ""
+
+msgid "Environments|Are you sure you want to stop this environment?"
+msgstr ""
+
msgid "Environments|Commit"
msgstr ""
+msgid "Environments|Deploy to..."
+msgstr ""
+
msgid "Environments|Deployment"
msgstr ""
@@ -2120,43 +2277,49 @@ msgstr ""
msgid "Environments|Job"
msgstr ""
+msgid "Environments|Learn more about stopping environments"
+msgstr ""
+
msgid "Environments|New environment"
msgstr ""
msgid "Environments|No deployments yet"
msgstr ""
-msgid "Environments|Open"
+msgid "Environments|Note that this action will stop the environment, but it will %{emphasis_start}not%{emphasis_end} have an effect on any existing deployment due to no “stop environment action” being defined in the %{ci_config_link_start}.gitlab-ci.yml%{ci_config_link_end} file."
+msgstr ""
+
+msgid "Environments|Open live environment"
msgstr ""
-msgid "Environments|Re-deploy"
+msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr ""
-msgid "Environments|Rollback"
+msgid "Environments|Rollback environment"
msgstr ""
msgid "Environments|Show all"
msgstr ""
-msgid "Environments|Updated"
+msgid "Environments|Stop"
msgstr ""
-msgid "Environments|You don't have any environments right now."
+msgid "Environments|Stop environment"
msgstr ""
-msgid "Error Reporting and Logging"
+msgid "Environments|Updated"
msgstr ""
-msgid "Error committing changes. Please try again."
+msgid "Environments|You don't have any environments right now."
msgstr ""
-msgid "Error fetching contributors data."
+msgid "Error Reporting and Logging"
msgstr ""
-msgid "Error fetching job trace"
+msgid "Error fetching contributors data."
msgstr ""
msgid "Error fetching labels."
@@ -2177,6 +2340,9 @@ msgstr ""
msgid "Error loading last commit."
msgstr ""
+msgid "Error loading markdown preview"
+msgstr ""
+
msgid "Error loading merge requests."
msgstr ""
@@ -2234,6 +2400,15 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Explore GitLab"
+msgstr ""
+
+msgid "Explore Groups"
+msgstr ""
+
+msgid "Explore groups"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -2291,6 +2466,12 @@ msgstr ""
msgid "Find file"
msgstr ""
+msgid "Find the downloaded ZIP file and decompress it."
+msgstr ""
+
+msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
+msgstr ""
+
msgid "Finished"
msgstr ""
@@ -2300,6 +2481,24 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "FogBugz Email"
+msgstr ""
+
+msgid "FogBugz Import"
+msgstr ""
+
+msgid "FogBugz Password"
+msgstr ""
+
+msgid "FogBugz URL"
+msgstr ""
+
+msgid "FogBugz import"
+msgstr ""
+
+msgid "Follow the steps below to export your Google Code project data."
+msgstr ""
+
msgid "For internal projects, any logged in user can view pipelines and access job details (output logs and artifacts)"
msgstr ""
@@ -2332,6 +2531,18 @@ msgstr ""
msgid "From %{provider_title}"
msgstr ""
+msgid "From Bitbucket"
+msgstr ""
+
+msgid "From FogBugz"
+msgstr ""
+
+msgid "From GitLab.com"
+msgstr ""
+
+msgid "From Google Code"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -2353,6 +2564,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
+msgid "Git"
+msgstr ""
+
msgid "Git repository URL"
msgstr ""
@@ -2377,9 +2591,21 @@ msgstr ""
msgid "GitLab Group Runners can execute code for all the projects in this group."
msgstr ""
+msgid "GitLab Import"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "GitLab User"
+msgstr ""
+
+msgid "GitLab project export"
+msgstr ""
+
+msgid "GitLab.com import"
+msgstr ""
+
msgid "Gitaly"
msgstr ""
@@ -2389,18 +2615,33 @@ msgstr ""
msgid "Gitaly|Address"
msgstr ""
+msgid "Gitea Host URL"
+msgstr ""
+
+msgid "Gitea Import"
+msgstr ""
+
msgid "Go Back"
msgstr ""
msgid "Go back"
msgstr ""
+msgid "Go to %{link_to_google_takeout}."
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
msgid "GoToYourFork|Fork"
msgstr ""
+msgid "Google Code import"
+msgstr ""
+
+msgid "Google Takeout"
+msgstr ""
+
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
@@ -2410,18 +2651,33 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "Group"
+msgstr ""
+
msgid "Group CI/CD settings"
msgstr ""
+msgid "Group Git LFS status:"
+msgstr ""
+
msgid "Group ID"
msgstr ""
msgid "Group Runners"
msgstr ""
+msgid "Group avatar"
+msgstr ""
+
+msgid "Group info:"
+msgstr ""
+
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group: %{group_name}"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -2446,9 +2702,33 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
+msgid "Groups"
+msgstr ""
+
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr ""
+msgid "GroupsDropdown|Frequently visited"
+msgstr ""
+
+msgid "GroupsDropdown|Groups you visit often will appear here"
+msgstr ""
+
+msgid "GroupsDropdown|Loading groups"
+msgstr ""
+
+msgid "GroupsDropdown|Search your groups"
+msgstr ""
+
+msgid "GroupsDropdown|Something went wrong on our end."
+msgstr ""
+
+msgid "GroupsDropdown|Sorry, no groups matched your search"
+msgstr ""
+
+msgid "GroupsDropdown|This feature requires browser localStorage support"
+msgstr ""
+
msgid "GroupsEmptyState|A group is a collection of several projects."
msgstr ""
@@ -2583,21 +2863,57 @@ msgstr ""
msgid "Import"
msgstr ""
+msgid "Import Projects from Gitea"
+msgstr ""
+
+msgid "Import all compatible projects"
+msgstr ""
+
+msgid "Import all projects"
+msgstr ""
+
msgid "Import all repositories"
msgstr ""
+msgid "Import an exported GitLab project"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
+msgid "Import project"
+msgstr ""
+
+msgid "Import projects from Bitbucket"
+msgstr ""
+
+msgid "Import projects from FogBugz"
+msgstr ""
+
+msgid "Import projects from GitLab.com"
+msgstr ""
+
+msgid "Import projects from Google Code"
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
msgid "Import repository"
msgstr ""
+msgid "In the next step, you'll be able to select the projects you want to import."
+msgstr ""
+
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Incompatible Project"
+msgstr ""
+
msgid "Inline"
msgstr ""
@@ -2631,7 +2947,7 @@ msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
-msgid "Issue Board"
+msgid "Issue Boards"
msgstr ""
msgid "Issue events"
@@ -2795,9 +3111,18 @@ msgstr ""
msgid "Leave project"
msgstr ""
+msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
+msgstr ""
+
msgid "List"
msgstr ""
+msgid "List Your Gitea Repositories"
+msgstr ""
+
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
@@ -2825,12 +3150,21 @@ msgstr ""
msgid "Locked to current projects"
msgstr ""
-msgid "Login"
+msgid "Make sure you're logged into the account that owns the projects you'd like to import."
+msgstr ""
+
+msgid "Manage access"
msgstr ""
msgid "Manage all notifications"
msgstr ""
+msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
+msgstr ""
+
+msgid "Manage applications that you've authorized to use your account."
+msgstr ""
+
msgid "Manage group labels"
msgstr ""
@@ -2840,6 +3174,24 @@ msgstr ""
msgid "Manage project labels"
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
+msgid "Map a FogBugz account ID to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a GitLab user"
+msgstr ""
+
+msgid "Map a Google Code user to a full email address"
+msgstr ""
+
+msgid "Map a Google Code user to a full name"
+msgstr ""
+
msgid "Mar"
msgstr ""
@@ -2864,6 +3216,9 @@ msgstr ""
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Request:"
msgstr ""
@@ -2906,12 +3261,42 @@ msgstr ""
msgid "Messages"
msgstr ""
+msgid "Metrics"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
+msgstr ""
+
+msgid "Metrics|Environment"
+msgstr ""
+
+msgid "Metrics|Learn about environments"
+msgstr ""
+
+msgid "Metrics|No deployed environments"
+msgstr ""
+
+msgid "Metrics|There was an error fetching the environments data, please try again"
+msgstr ""
+
+msgid "Metrics|There was an error getting deployment information."
+msgstr ""
+
+msgid "Metrics|There was an error getting environments information."
+msgstr ""
+
+msgid "Metrics|Unexpected deployment data response from prometheus endpoint"
+msgstr ""
+
+msgid "Metrics|Unexpected metrics data response from prometheus endpoint"
+msgstr ""
+
msgid "Milestone"
msgstr ""
@@ -2957,6 +3342,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Most stars"
+msgstr ""
+
msgid "Move"
msgstr ""
@@ -2972,6 +3360,9 @@ msgstr ""
msgid "Name your individual key via a title"
msgstr ""
+msgid "Name:"
+msgstr ""
+
msgid "Nav|Help"
msgstr ""
@@ -2987,6 +3378,12 @@ msgstr ""
msgid "New"
msgstr ""
+msgid "New Application"
+msgstr ""
+
+msgid "New Group"
+msgstr ""
+
msgid "New Identity"
msgstr ""
@@ -2995,12 +3392,6 @@ msgid_plural "New Issues"
msgstr[0] ""
msgstr[1] ""
-msgid "New Kubernetes Cluster"
-msgstr ""
-
-msgid "New Kubernetes cluster"
-msgstr ""
-
msgid "New Label"
msgstr ""
@@ -3091,12 +3482,18 @@ msgstr ""
msgid "No messages were logged"
msgstr ""
+msgid "No public groups"
+msgstr ""
+
msgid "No repository"
msgstr ""
msgid "No schedules"
msgstr ""
+msgid "No, directly import the existing email addresses and usernames."
+msgstr ""
+
msgid "None"
msgstr ""
@@ -3208,6 +3605,12 @@ msgstr ""
msgid "OfSearchInADropdown|Filter"
msgstr ""
+msgid "One or more of your Bitbucket projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
+msgid "One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git."
+msgstr ""
+
msgid "Online IDE integration settings."
msgstr ""
@@ -3217,6 +3620,9 @@ msgstr ""
msgid "Only project members can comment."
msgstr ""
+msgid "Oops, are you sure?"
+msgstr ""
+
msgid "Open in Xcode"
msgstr ""
@@ -3229,6 +3635,12 @@ msgstr ""
msgid "Operations"
msgstr ""
+msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
+msgstr ""
+
+msgid "Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab."
+msgstr ""
+
msgid "Options"
msgstr ""
@@ -3274,6 +3686,9 @@ msgstr ""
msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
msgstr ""
+msgid "Path:"
+msgstr ""
+
msgid "Pause"
msgstr ""
@@ -3460,6 +3875,15 @@ msgstr ""
msgid "Please accept the Terms of Service before continuing."
msgstr ""
+msgid "Please convert them to %{link_to_git}, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
+msgstr ""
+
+msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
@@ -3505,6 +3929,9 @@ msgstr ""
msgid "Profiles|Account scheduled for removal."
msgstr ""
+msgid "Profiles|Add key"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -3532,9 +3959,15 @@ msgstr ""
msgid "Profiles|Path"
msgstr ""
+msgid "Profiles|This doesn't look like a public SSH key, are you sure you want to add it?"
+msgstr ""
+
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
+msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgstr ""
+
msgid "Profiles|Update username"
msgstr ""
@@ -3553,6 +3986,9 @@ msgstr ""
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr ""
+msgid "Profiles|e.g. My MacBook key"
+msgstr ""
+
msgid "Profiles|your account"
msgstr ""
@@ -3607,6 +4043,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project name"
+msgstr ""
+
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
@@ -3622,6 +4061,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects shared with %{group_name}"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -3640,9 +4082,6 @@ msgstr ""
msgid "ProjectsDropdown|Sorry, no projects matched your search"
msgstr ""
-msgid "ProjectsDropdown|This feature requires browser localStorage support"
-msgstr ""
-
msgid "PrometheusDashboard|Time"
msgstr ""
@@ -3748,10 +4187,10 @@ msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
-msgid "Re-deploy"
+msgid "Read more"
msgstr ""
-msgid "Read more"
+msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr ""
msgid "Readme"
@@ -3820,6 +4259,9 @@ msgstr ""
msgid "Repository Settings"
msgstr ""
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository maintenance"
msgstr ""
@@ -3885,7 +4327,7 @@ msgstr ""
msgid "Reviewing (merge request !%{mergeRequestId})"
msgstr ""
-msgid "Rollback"
+msgid "Revoke"
msgstr ""
msgid "Runner token"
@@ -3912,6 +4354,9 @@ msgstr ""
msgid "Save"
msgstr ""
+msgid "Save application"
+msgstr ""
+
msgid "Save changes"
msgstr ""
@@ -3933,6 +4378,12 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scope"
+msgstr ""
+
+msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
+msgstr ""
+
msgid "Scroll to bottom"
msgstr ""
@@ -3972,6 +4423,9 @@ msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret:"
+msgstr ""
+
msgid "Select"
msgstr ""
@@ -4002,12 +4456,18 @@ msgstr ""
msgid "Select project to choose zone"
msgstr ""
+msgid "Select projects you want to import."
+msgstr ""
+
msgid "Select source branch"
msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
+msgstr ""
+
msgid "Send email"
msgstr ""
@@ -4313,6 +4773,9 @@ msgstr ""
msgid "Storage"
msgstr ""
+msgid "Storage:"
+msgstr ""
+
msgid "Subgroups"
msgstr ""
@@ -4513,6 +4976,12 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
+msgstr ""
+
+msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
+msgstr ""
+
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
@@ -4528,12 +4997,6 @@ msgstr ""
msgid "There are problems accessing Git storage: "
msgstr ""
-msgid "There was an error loading jobs"
-msgstr ""
-
-msgid "There was an error loading latest pipeline"
-msgstr ""
-
msgid "There was an error loading users activity calendar."
msgstr ""
@@ -4555,9 +5018,18 @@ msgstr ""
msgid "They can be managed using the %{link}."
msgstr ""
+msgid "Third party offers"
+msgstr ""
+
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr ""
+msgid "This application was created by %{link_to_owner}."
+msgstr ""
+
+msgid "This application will be able to:"
+msgstr ""
+
msgid "This diff is collapsed."
msgstr ""
@@ -4814,12 +5286,21 @@ msgstr ""
msgid "Tip:"
msgstr ""
+msgid "Title"
+msgstr ""
+
msgid "To GitLab"
msgstr ""
msgid "To add an SSH key you need to %{generate_link_start}generate one%{link_end} or use an %{existing_link_start}existing key%{link_end}."
msgstr ""
+msgid "To get started you enter your FogBugz URL and login information below. In the next steps, you'll be able to map users and select the projects you want to import."
+msgstr ""
+
+msgid "To get started, please enter your Gitea Host URL and a %{link_to_personal_token}."
+msgstr ""
+
msgid "To import GitHub repositories, you can use a %{personal_access_token_link}. When you create your Personal Access Token, you will need to select the <code>repo</code> scope, so we can display a list of your public and private repositories which are available to import."
msgstr ""
@@ -4829,9 +5310,15 @@ msgstr ""
msgid "To import an SVN repository, check out %{svn_link}."
msgstr ""
+msgid "To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here."
+msgstr ""
+
msgid "To start serving your jobs you can add Runners to your group"
msgstr ""
+msgid "To this GitLab instance"
+msgstr ""
+
msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' inside your project, and click on the 'CI Lint' button."
msgstr ""
@@ -4868,6 +5355,9 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
+msgid "Trending"
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -4930,6 +5420,9 @@ msgstr[1] ""
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
+msgstr ""
+
msgid "Upload New File"
msgstr ""
@@ -4948,9 +5441,15 @@ msgstr ""
msgid "Usage statistics"
msgstr ""
+msgid "Use <code>%{native_redirect_uri}</code> for local tests"
+msgstr ""
+
msgid "Use group milestones to manage issues from multiple projects in the same milestone."
msgstr ""
+msgid "Use one line per URI"
+msgstr ""
+
msgid "Use the following registration token during setup:"
msgstr ""
@@ -4960,6 +5459,9 @@ msgstr ""
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User map"
+msgstr ""
+
msgid "Users"
msgstr ""
@@ -5008,6 +5510,12 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
+msgid "Visibility level:"
+msgstr ""
+
+msgid "Visibility:"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
@@ -5182,6 +5690,15 @@ msgstr ""
msgid "Yes"
msgstr ""
+msgid "Yes, add it"
+msgstr ""
+
+msgid "Yes, let me map Google Code users to full names or GitLab users."
+msgstr ""
+
+msgid "You are an admin, which means granting access to <strong>%{client_name}</strong> will allow them to interact with GitLab as an admin as well. Proceed with caution."
+msgstr ""
+
msgid "You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr ""
@@ -5209,6 +5726,9 @@ msgstr ""
msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr ""
+msgid "You can easily contribute to them by requesting to join these groups."
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -5230,6 +5750,12 @@ msgstr ""
msgid "You do not have any assigned merge requests"
msgstr ""
+msgid "You don't have any applications"
+msgstr ""
+
+msgid "You don't have any authorized applications"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
@@ -5296,6 +5822,12 @@ msgstr ""
msgid "Your Todos"
msgstr ""
+msgid "Your applications (%{size})"
+msgstr ""
+
+msgid "Your authorized applications"
+msgstr ""
+
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr ""
@@ -5332,6 +5864,9 @@ msgstr ""
msgid "connecting"
msgstr ""
+msgid "customize"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
@@ -5343,6 +5878,9 @@ msgstr ""
msgid "disabled"
msgstr ""
+msgid "done"
+msgstr ""
+
msgid "enabled"
msgstr ""
@@ -5352,6 +5890,12 @@ msgstr ""
msgid "for this project"
msgstr ""
+msgid "here"
+msgstr ""
+
+msgid "import flow"
+msgstr ""
+
msgid "importing"
msgstr ""
@@ -5441,6 +5985,9 @@ msgstr ""
msgid "mrWidget|Merged by"
msgstr ""
+msgid "mrWidget|Open in Web IDE"
+msgstr ""
+
msgid "mrWidget|Plain diff"
msgstr ""
@@ -5510,9 +6057,6 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
-msgid "mrWidget|Web IDE"
-msgstr ""
-
msgid "mrWidget|You can merge this merge request manually using the"
msgstr ""
@@ -5563,6 +6107,9 @@ msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "started"
+msgstr ""
+
msgid "this document"
msgstr ""
diff --git a/package.json b/package.json
index 6980416503e..9dd7d80945f 100644
--- a/package.json
+++ b/package.json
@@ -18,11 +18,11 @@
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
"dependencies": {
- "@gitlab-org/gitlab-svgs": "^1.24.0",
+ "@gitlab-org/gitlab-svgs": "^1.25.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.3",
- "babel-loader": "^7.1.4",
+ "babel-loader": "^7.1.5",
"babel-plugin-transform-define": "^1.3.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
@@ -36,7 +36,7 @@
"compression-webpack-plugin": "^1.1.11",
"core-js": "^2.4.1",
"cropper": "^2.3.0",
- "css-loader": "^0.28.11",
+ "css-loader": "^1.0.0",
"d3-array": "^1.2.1",
"d3-axis": "^1.0.8",
"d3-brush": "^1.0.4",
@@ -66,7 +66,7 @@
"katex": "^0.8.3",
"marked": "^0.3.12",
"monaco-editor": "0.13.1",
- "monaco-editor-webpack-plugin": "^1.2.1",
+ "monaco-editor-webpack-plugin": "^1.4.0",
"mousetrap": "^1.4.6",
"pikaday": "^1.6.1",
"popper.js": "^1.14.3",
@@ -90,15 +90,15 @@
"url-loader": "^1.0.1",
"visibilityjs": "^1.2.4",
"vue": "^2.5.16",
- "vue-loader": "^15.2.0",
+ "vue-loader": "^15.2.4",
"vue-resource": "^1.5.0",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.5.16",
"vue-virtual-scroll-list": "^1.2.5",
"vuex": "^3.0.1",
- "webpack": "^4.11.1",
- "webpack-bundle-analyzer": "^2.11.1",
- "webpack-cli": "^3.0.2",
+ "webpack": "^4.16.0",
+ "webpack-bundle-analyzer": "^2.13.1",
+ "webpack-cli": "^3.0.8",
"webpack-stats-plugin": "^0.2.1",
"worker-loader": "^2.0.0"
},
@@ -123,15 +123,16 @@
"ignore": "^3.3.7",
"istanbul": "^0.4.5",
"jasmine-core": "^2.9.0",
+ "jasmine-diff": "^0.1.3",
"jasmine-jquery": "^2.1.1",
- "karma": "^2.0.2",
+ "karma": "^2.0.4",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "^1.4.2",
- "karma-jasmine": "^1.1.1",
+ "karma-jasmine": "^1.1.2",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
- "karma-webpack": "3.0.0",
- "nodemon": "^1.17.3",
+ "karma-webpack": "^4.0.0-beta.0",
+ "nodemon": "^1.18.2",
"prettier": "1.12.1",
"webpack-dev-server": "^3.1.4"
}
diff --git a/qa/README.md b/qa/README.md
index a4b4398645e..be4cf89ebbc 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -55,7 +55,7 @@ Since the arguments would be passed to `rspec`, you could use all `rspec`
options there. For example, passing `--backtrace` and also line number:
```
-bin/qa Test::Instance http://localhost qa/specs/features/login/standard_spec.rb:3 --backtrace
+bin/qa Test::Instance http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace
```
### Overriding the authenticated user
diff --git a/qa/qa.rb b/qa/qa.rb
index 5013024e60f..0b48cf58766 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -1,5 +1,7 @@
$: << File.expand_path(File.dirname(__FILE__))
+Encoding.default_external = 'UTF-8'
+
module QA
##
# GitLab QA runtime classes, mostly singletons.
@@ -40,13 +42,19 @@ module QA
autoload :Issue, 'qa/factory/resource/issue'
autoload :Project, 'qa/factory/resource/project'
autoload :MergeRequest, 'qa/factory/resource/merge_request'
+ autoload :ProjectImportedFromGithub, 'qa/factory/resource/project_imported_from_github'
+ autoload :MergeRequestFromFork, 'qa/factory/resource/merge_request_from_fork'
autoload :DeployKey, 'qa/factory/resource/deploy_key'
autoload :Branch, 'qa/factory/resource/branch'
autoload :SecretVariable, 'qa/factory/resource/secret_variable'
autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
+ autoload :User, 'qa/factory/resource/user'
+ autoload :ProjectMilestone, 'qa/factory/resource/project_milestone'
autoload :Wiki, 'qa/factory/resource/wiki'
+ autoload :File, 'qa/factory/resource/file'
+ autoload :Fork, 'qa/factory/resource/fork'
end
module Repository
@@ -79,6 +87,7 @@ module QA
autoload :Instance, 'qa/scenario/test/instance'
module Integration
+ autoload :Github, 'qa/scenario/test/integration/github'
autoload :LDAP, 'qa/scenario/test/integration/ldap'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
@@ -104,6 +113,7 @@ module QA
module Main
autoload :Login, 'qa/page/main/login'
autoload :OAuth, 'qa/page/main/oauth'
+ autoload :SignUp, 'qa/page/main/sign_up'
end
module Settings
@@ -127,11 +137,24 @@ module QA
autoload :Show, 'qa/page/group/show'
end
+ module File
+ autoload :Form, 'qa/page/file/form'
+ autoload :Show, 'qa/page/file/show'
+
+ module Shared
+ autoload :CommitMessage, 'qa/page/file/shared/commit_message'
+ end
+ end
+
module Project
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
autoload :Activity, 'qa/page/project/activity'
+ module Import
+ autoload :Github, 'qa/page/project/import/github'
+ end
+
module Pipeline
autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show'
@@ -160,6 +183,15 @@ module QA
autoload :Index, 'qa/page/project/issue/index'
end
+ module Fork
+ autoload :New, 'qa/page/project/fork/new'
+ end
+
+ module Milestone
+ autoload :New, 'qa/page/project/milestone/new'
+ autoload :Index, 'qa/page/project/milestone/index'
+ end
+
module Operations
module Kubernetes
autoload :Index, 'qa/page/project/operations/kubernetes/index'
@@ -184,6 +216,14 @@ module QA
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
end
+ module Issuable
+ autoload :Sidebar, 'qa/page/issuable/sidebar'
+ end
+
+ module Layout
+ autoload :Banner, 'qa/page/layout/banner'
+ end
+
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
autoload :Show, 'qa/page/merge_request/show'
@@ -206,6 +246,7 @@ module QA
#
module Component
autoload :Dropzone, 'qa/page/component/dropzone'
+ autoload :Select2, 'qa/page/component/select2'
end
end
diff --git a/qa/qa/factory/repository/project_push.rb b/qa/qa/factory/repository/project_push.rb
index 48674c08a8d..4f78098d348 100644
--- a/qa/qa/factory/repository/project_push.rb
+++ b/qa/qa/factory/repository/project_push.rb
@@ -11,6 +11,8 @@ module QA
factory.output
end
+ product(:project) { |factory| factory.project }
+
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb
index 4f97e65b091..5b7ebf6c41f 100644
--- a/qa/qa/factory/repository/push.rb
+++ b/qa/qa/factory/repository/push.rb
@@ -5,7 +5,8 @@ module QA
module Repository
class Push < Factory::Base
attr_accessor :file_name, :file_content, :commit_message,
- :branch_name, :new_branch, :output, :repository_uri
+ :branch_name, :new_branch, :output, :repository_uri,
+ :user
attr_writer :remote_branch
@@ -31,9 +32,20 @@ module QA
def fabricate!
Git::Repository.perform do |repository|
repository.uri = repository_uri
+
repository.use_default_credentials
+ username = 'GitLab QA'
+ email = 'root@gitlab.com'
+
+ if user
+ repository.username = user.username
+ repository.password = user.password
+ username = user.name
+ email = user.email
+ end
+
repository.clone
- repository.configure_identity('GitLab QA', 'root@gitlab.com')
+ repository.configure_identity(username, email)
if new_branch
repository.checkout_new_branch(branch_name)
diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb
index 7fb0633ec90..bc252bf3148 100644
--- a/qa/qa/factory/resource/branch.rb
+++ b/qa/qa/factory/resource/branch.rb
@@ -9,18 +9,6 @@ module QA
project.name = 'protected-branch-project'
end
- product :name do
- Page::Project::Settings::Repository.act do
- expand_protected_branches(&:last_branch_name)
- end
- end
-
- product :push_allowance do
- Page::Project::Settings::Repository.act do
- expand_protected_branches(&:last_push_allowance)
- end
- end
-
def initialize
@branch_name = 'test/branch'
@allow_to_push = true
@@ -80,15 +68,6 @@ module QA
end
page.protect_branch
-
- # Avoid Selenium::WebDriver::Error::StaleElementReferenceError
- # without sleeping. I.e. this completes fast on fast machines.
- page.refresh
-
- # It is possible for the protected branch row to "disappear" at first
- page.wait do
- page.has_content?(branch_name)
- end
end
end
end
diff --git a/qa/qa/factory/resource/file.rb b/qa/qa/factory/resource/file.rb
new file mode 100644
index 00000000000..2016d10ddae
--- /dev/null
+++ b/qa/qa/factory/resource/file.rb
@@ -0,0 +1,34 @@
+module QA
+ module Factory
+ module Resource
+ class File < Factory::Base
+ attr_accessor :name,
+ :content,
+ :commit_message
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-new-file'
+ end
+
+ def initialize
+ @name = 'QA Test - File name'
+ @content = 'QA Test - File content'
+ @commit_message = 'QA Test - Commit message'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.act { go_to_new_file! }
+
+ Page::File::Form.perform do |page|
+ page.add_name(@name)
+ page.add_content(@content)
+ page.add_commit_message(@commit_message)
+ page.commit_changes
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb
new file mode 100644
index 00000000000..1d0c76a3d30
--- /dev/null
+++ b/qa/qa/factory/resource/fork.rb
@@ -0,0 +1,24 @@
+module QA
+ module Factory
+ module Resource
+ class Fork < Factory::Base
+ dependency Factory::Repository::ProjectPush, as: :push
+
+ dependency Factory::Resource::User, as: :user
+
+ product(:user) { |factory| factory.user }
+
+ def fabricate!
+ push.project.visit!
+ Page::Project::Show.act { fork_project }
+
+ Page::Project::Fork::New.perform do |fork_new|
+ fork_new.choose_namespace(user.name)
+ end
+
+ Page::Layout::Banner.act { has_notice?('The project was successfully forked.') }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/group.rb b/qa/qa/factory/resource/group.rb
index 9f13e26f35c..531fccd2ad8 100644
--- a/qa/qa/factory/resource/group.rb
+++ b/qa/qa/factory/resource/group.rb
@@ -23,7 +23,7 @@ module QA
Page::Group::New.perform do |group|
group.set_path(@path)
group.set_description(@description)
- group.set_visibility('Private')
+ group.set_visibility('Public')
group.create
end
end
diff --git a/qa/qa/factory/resource/kubernetes_cluster.rb b/qa/qa/factory/resource/kubernetes_cluster.rb
index f32cf985e9d..1c9e5f94b22 100644
--- a/qa/qa/factory/resource/kubernetes_cluster.rb
+++ b/qa/qa/factory/resource/kubernetes_cluster.rb
@@ -36,6 +36,9 @@ module QA
if @install_helm_tiller
Page::Project::Operations::Kubernetes::Show.perform do |page|
+ # We must wait a few seconds for permissions to be setup correctly for new cluster
+ sleep 10
+
# Helm must be installed before everything else
page.install!(:helm)
page.await_installed(:helm)
diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb
index 24d3597d993..ddb62bd0a68 100644
--- a/qa/qa/factory/resource/merge_request.rb
+++ b/qa/qa/factory/resource/merge_request.rb
@@ -7,7 +7,10 @@ module QA
attr_accessor :title,
:description,
:source_branch,
- :target_branch
+ :target_branch,
+ :assignee,
+ :milestone,
+ :labels
product :project do |factory|
factory.project
@@ -41,16 +44,18 @@ module QA
@description = 'This is a test merge request'
@source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
@target_branch = "master"
+ @assignee = nil
+ @milestone = nil
+ @labels = []
end
def fabricate!
project.visit!
-
Page::Project::Show.act { new_merge_request }
-
Page::MergeRequest::New.perform do |page|
page.fill_title(@title)
page.fill_description(@description)
+ page.choose_milestone(@milestone) if @milestone
page.create_merge_request
end
end
diff --git a/qa/qa/factory/resource/merge_request_from_fork.rb b/qa/qa/factory/resource/merge_request_from_fork.rb
new file mode 100644
index 00000000000..6caaf65f673
--- /dev/null
+++ b/qa/qa/factory/resource/merge_request_from_fork.rb
@@ -0,0 +1,24 @@
+module QA
+ module Factory
+ module Resource
+ class MergeRequestFromFork < MergeRequest
+ attr_accessor :fork_branch
+
+ dependency Factory::Resource::Fork, as: :fork
+
+ dependency Factory::Repository::ProjectPush, as: :push do |push, factory|
+ push.project = factory.fork
+ push.branch_name = factory.fork_branch
+ push.file_name = 'file2.txt'
+ push.user = factory.fork.user
+ end
+
+ def fabricate!
+ fork.visit!
+ Page::Project::Show.act { new_merge_request }
+ Page::MergeRequest::New.act { create_merge_request }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb
index cda1b35ba6a..7fff22b5468 100644
--- a/qa/qa/factory/resource/project.rb
+++ b/qa/qa/factory/resource/project.rb
@@ -5,16 +5,12 @@ module QA
module Resource
class Project < Factory::Base
attr_writer :description
+ attr_reader :name
dependency Factory::Resource::Group, as: :group
- def name=(name)
- @name = "#{name}-#{SecureRandom.hex(8)}"
- @description = 'My awesome project'
- end
-
- product :name do
- Page::Project::Show.act { project_name }
+ product :name do |factory|
+ factory.name
end
product :repository_ssh_location do
@@ -24,6 +20,14 @@ module QA
end
end
+ def initialize
+ @description = 'My awesome project'
+ end
+
+ def name=(raw_name)
+ @name = "#{raw_name}-#{SecureRandom.hex(8)}"
+ end
+
def fabricate!
group.visit!
@@ -33,6 +37,7 @@ module QA
page.choose_test_namespace
page.choose_name(@name)
page.add_description(@description)
+ page.set_visibility('Public')
page.create_new_project
end
end
diff --git a/qa/qa/factory/resource/project_imported_from_github.rb b/qa/qa/factory/resource/project_imported_from_github.rb
new file mode 100644
index 00000000000..df2a3340d60
--- /dev/null
+++ b/qa/qa/factory/resource/project_imported_from_github.rb
@@ -0,0 +1,37 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class ProjectImportedFromGithub < Resource::Project
+ attr_writer :personal_access_token, :github_repository_path
+
+ dependency Factory::Resource::Group, as: :group
+
+ product :name do |factory|
+ factory.name
+ end
+
+ def fabricate!
+ group.visit!
+
+ Page::Group::Show.act { go_to_new_project }
+
+ Page::Project::New.perform do |page|
+ page.go_to_import_project
+ end
+
+ Page::Project::New.perform do |page|
+ page.go_to_github_import
+ end
+
+ Page::Project::Import::Github.perform do |page|
+ page.add_personal_access_token(@personal_access_token)
+ page.list_repos
+ page.import!(@github_repository_path, @name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/project_milestone.rb b/qa/qa/factory/resource/project_milestone.rb
new file mode 100644
index 00000000000..47a5e74204f
--- /dev/null
+++ b/qa/qa/factory/resource/project_milestone.rb
@@ -0,0 +1,36 @@
+module QA
+ module Factory
+ module Resource
+ class ProjectMilestone < Factory::Base
+ attr_accessor :description
+ attr_reader :title
+
+ dependency Factory::Resource::Project, as: :project
+
+ product(:title) { |factory| factory.title }
+
+ def title=(title)
+ @title = "#{title}-#{SecureRandom.hex(4)}"
+ @description = 'A milestone'
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Menu::Side.act do
+ click_issues
+ click_milestones
+ end
+
+ Page::Project::Milestone::Index.act { click_new_milestone }
+
+ Page::Project::Milestone::New.perform do |milestone_new|
+ milestone_new.set_title(@title)
+ milestone_new.set_description(@description)
+ milestone_new.create_new_milestone
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/factory/resource/sandbox.rb b/qa/qa/factory/resource/sandbox.rb
index ad376988e82..4f6039f300f 100644
--- a/qa/qa/factory/resource/sandbox.rb
+++ b/qa/qa/factory/resource/sandbox.rb
@@ -21,8 +21,8 @@ module QA
Page::Group::New.perform do |group|
group.set_path(@name)
- group.set_description('GitLab QA Sandbox')
- group.set_visibility('Private')
+ group.set_description('GitLab QA Sandbox Group')
+ group.set_visibility('Public')
group.create
end
end
diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb
new file mode 100644
index 00000000000..e08df9e0cd0
--- /dev/null
+++ b/qa/qa/factory/resource/user.rb
@@ -0,0 +1,34 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class User < Factory::Base
+ attr_accessor :name, :username, :email, :password
+
+ def initialize
+ @name = "name-#{SecureRandom.hex(8)}"
+ @username = "username-#{SecureRandom.hex(8)}"
+ @email = "mail#{SecureRandom.hex(8)}@mail.com"
+ @password = 'password'
+ end
+
+ product(:name) { |factory| factory.name }
+
+ product(:username) { |factory| factory.username }
+
+ product(:email) { |factory| factory.email }
+
+ product(:password) { |factory| factory.password }
+
+ def fabricate!
+ Page::Menu::Main.act { sign_out }
+ Page::Main::Login.act { switch_to_register_tab }
+ Page::Main::SignUp.perform do |page|
+ page.sign_up!(name: name, username: username, email: email, password: password)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
new file mode 100644
index 00000000000..30829eb0221
--- /dev/null
+++ b/qa/qa/page/component/select2.rb
@@ -0,0 +1,11 @@
+module QA
+ module Page
+ module Component
+ module Select2
+ def select_item(item_text)
+ find('ul.select2-result-sub > li', text: item_text).click
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/form.rb b/qa/qa/page/file/form.rb
new file mode 100644
index 00000000000..f6e502f500b
--- /dev/null
+++ b/qa/qa/page/file/form.rb
@@ -0,0 +1,40 @@
+module QA
+ module Page
+ module File
+ class Form < Page::Base
+ include Shared::CommitMessage
+
+ view 'app/views/projects/blob/_editor.html.haml' do
+ element :file_name, "text_field_tag 'file_name'"
+ element :editor, '#editor'
+ end
+
+ view 'app/views/projects/_commit_button.html.haml' do
+ element :commit_changes, "button_tag 'Commit changes'"
+ end
+
+ def add_name(name)
+ fill_in 'file_name', with: name
+ end
+
+ def add_content(content)
+ text_area.set content
+ end
+
+ def remove_content
+ text_area.send_keys([:command, 'a'], :backspace)
+ end
+
+ def commit_changes
+ click_on 'Commit changes'
+ end
+
+ private
+
+ def text_area
+ find('#editor>textarea', visible: false)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
new file mode 100644
index 00000000000..5af1a55e2ef
--- /dev/null
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -0,0 +1,19 @@
+module QA
+ module Page
+ module File
+ module Shared
+ module CommitMessage
+ def self.included(base)
+ base.view 'app/views/shared/_commit_message_container.html.haml' do
+ element :commit_message, "text_area_tag 'commit_message'"
+ end
+ end
+
+ def add_commit_message(message)
+ fill_in 'commit_message', with: message
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
new file mode 100644
index 00000000000..99f5924b67f
--- /dev/null
+++ b/qa/qa/page/file/show.rb
@@ -0,0 +1,30 @@
+module QA
+ module Page
+ module File
+ class Show < Page::Base
+ include Shared::CommitMessage
+
+ view 'app/helpers/blob_helper.rb' do
+ element :edit_button, "_('Edit')"
+ element :delete_button, /label:\s+"Delete"/
+ end
+
+ view 'app/views/projects/blob/_remove.html.haml' do
+ element :delete_file_button, "button_tag 'Delete file'"
+ end
+
+ def click_edit
+ click_on 'Edit'
+ end
+
+ def click_delete
+ click_on 'Delete'
+ end
+
+ def click_delete_file
+ click_on 'Delete file'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/issuable/sidebar.rb b/qa/qa/page/issuable/sidebar.rb
new file mode 100644
index 00000000000..f207264e24f
--- /dev/null
+++ b/qa/qa/page/issuable/sidebar.rb
@@ -0,0 +1,24 @@
+module QA
+ module Page
+ module Issuable
+ class Sidebar < Page::Base
+ view 'app/views/shared/issuable/_sidebar.html.haml' do
+ element :labels_block, ".issuable-show-labels"
+ element :milestones_block, '.block.milestone'
+ end
+
+ def has_label?(label)
+ page.within('.issuable-show-labels') do
+ !!find('span', text: label)
+ end
+ end
+
+ def has_milestone?(milestone)
+ page.within('.block.milestone') do
+ !!find("[href*='/milestones/']", text: milestone)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/layout/banner.rb b/qa/qa/page/layout/banner.rb
new file mode 100644
index 00000000000..e7654bdafc9
--- /dev/null
+++ b/qa/qa/page/layout/banner.rb
@@ -0,0 +1,17 @@
+module QA
+ module Page
+ module Layout
+ class Banner < Page::Base
+ view 'app/views/layouts/header/_read_only_banner.html.haml' do
+ element :flash_notice, ".flash-notice"
+ end
+
+ def has_notice?(message)
+ page.within('.flash-notice') do
+ !!find('span', text: message)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 26c99efc53d..6cdfbd1c125 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -25,19 +25,24 @@ module QA
element :standard_tab, "link_to 'Standard'"
end
+ view 'app/views/devise/shared/_tabs_normal.html.haml' do
+ element :sign_in_tab, /nav-link.*login-pane.*Sign in/
+ element :register_tab, /nav-link.*register-pane.*Register/
+ end
+
def initialize
# The login page is usually the entry point for all the scenarios so
# we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here.
wait(max: 500) do
page.has_css?('.login-page') ||
- Page::Menu::Main.act { has_personal_area? }
+ Page::Menu::Main.act { has_personal_area?(wait: 0) }
end
end
def sign_in_using_credentials
# Don't try to log-in if we're already logged-in
- return if Page::Menu::Main.act { has_personal_area? }
+ return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
using_wait_time 0 do
set_initial_password_if_present
@@ -48,12 +53,22 @@ module QA
sign_in_using_gitlab_credentials
end
end
+
+ Page::Menu::Main.act { has_personal_area? }
end
def self.path
'/users/sign_in'
end
+ def switch_to_sign_in_tab
+ click_on 'Sign in'
+ end
+
+ def switch_to_register_tab
+ click_on 'Register'
+ end
+
private
def sign_in_using_ldap_credentials
diff --git a/qa/qa/page/main/oauth.rb b/qa/qa/page/main/oauth.rb
index 6f548148363..618f114e058 100644
--- a/qa/qa/page/main/oauth.rb
+++ b/qa/qa/page/main/oauth.rb
@@ -3,7 +3,7 @@ module QA
module Main
class OAuth < Page::Base
view 'app/views/doorkeeper/authorizations/new.html.haml' do
- element :authorization_button, 'submit_tag "Authorize"'
+ element :authorization_button, 'submit_tag _("Authorize")'
end
def needs_authorization?
diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
new file mode 100644
index 00000000000..9a834e94b81
--- /dev/null
+++ b/qa/qa/page/main/sign_up.rb
@@ -0,0 +1,27 @@
+module QA
+ module Page
+ module Main
+ class SignUp < Page::Base
+ view 'app/views/devise/shared/_signup_box.html.haml' do
+ element :name, 'text_field :name'
+ element :username, 'text_field :username'
+ element :email_field, 'email_field :email'
+ element :email_confirmation, 'email_field :email_confirmation'
+ element :password, 'password_field :password'
+ element :register_button, 'submit "Register"'
+ end
+
+ def sign_up!(name:, username:, email:, password:)
+ fill_in :new_user_name, with: name
+ fill_in :new_user_username, with: username
+ fill_in :new_user_email, with: email
+ fill_in :new_user_email_confirmation, with: email
+ fill_in :new_user_password, with: password
+ click_button 'Register'
+
+ Page::Menu::Main.act { has_personal_area? }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/menu/main.rb
index fda9c45c091..36e7285f7b7 100644
--- a/qa/qa/page/menu/main.rb
+++ b/qa/qa/page/menu/main.rb
@@ -16,7 +16,7 @@ module QA
view 'app/views/layouts/nav/_dashboard.html.haml' do
element :admin_area_link
element :projects_dropdown
- element :groups_link
+ element :groups_dropdown
end
view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
@@ -25,7 +25,13 @@ module QA
end
def go_to_groups
- within_top_menu { click_element :groups_link }
+ within_top_menu do
+ click_element :groups_dropdown
+ end
+
+ page.within('.qa-groups-dropdown-sidebar') do
+ click_element :your_groups_link
+ end
end
def go_to_projects
@@ -54,9 +60,9 @@ module QA
end
end
- def has_personal_area?
+ def has_personal_area?(wait: Capybara.default_max_wait_time)
# No need to wait, either we're logged-in, or not.
- using_wait_time(0) { page.has_selector?('.qa-user-avatar') }
+ using_wait_time(wait) { page.has_selector?('.qa-user-avatar') }
end
private
diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb
index 6bf4825cf00..c14a835c2c9 100644
--- a/qa/qa/page/menu/side.rb
+++ b/qa/qa/page/menu/side.rb
@@ -10,10 +10,13 @@ module QA
element :operations_kubernetes_link, "title: _('Kubernetes')"
element :issues_link, /link_to.*shortcuts-issues/
element :issues_link_text, "Issues"
+ element :merge_requests_link, /link_to.*shortcuts-merge_requests/
+ element :merge_requests_link_text, "Merge Requests"
element :top_level_items, '.sidebar-top-level-items'
element :operations_section, "class: 'shortcuts-operations'"
element :activity_link, "title: 'Activity'"
element :wiki_link_text, "Wiki"
+ element :milestones_link
end
view 'app/assets/javascripts/fly_out_nav.js' do
@@ -62,6 +65,18 @@ module QA
end
end
+ def click_merge_requests
+ within_sidebar do
+ click_link('Merge Requests')
+ end
+ end
+
+ def click_milestones
+ within_sidebar do
+ click_element :milestones_link
+ end
+ end
+
def click_wiki
within_sidebar do
click_link('Wiki')
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index ec94ff4ac98..83cc4bbbace 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -10,10 +10,18 @@ module QA
element :issuable_form_title
end
+ view 'app/views/shared/issuable/form/_metadata.html.haml' do
+ element :issuable_milestone_dropdown
+ end
+
view 'app/views/shared/form_elements/_description.html.haml' do
element :issuable_form_description
end
+ view 'app/views/shared/issuable/_milestone_dropdown.html.haml' do
+ element :issuable_dropdown_menu_milestone
+ end
+
def create_merge_request
click_element :issuable_create_button
end
@@ -25,6 +33,13 @@ module QA
def fill_description(description)
fill_element :issuable_form_description, description
end
+
+ def choose_milestone(milestone)
+ click_element :issuable_milestone_dropdown
+ within_element(:issuable_dropdown_menu_milestone) do
+ click_on milestone.title
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
new file mode 100644
index 00000000000..ed92df956bf
--- /dev/null
+++ b/qa/qa/page/project/fork/new.rb
@@ -0,0 +1,17 @@
+module QA
+ module Page
+ module Project
+ module Fork
+ class New < Page::Base
+ view 'app/views/projects/forks/_fork_button.html.haml' do
+ element :namespace, 'link_to project_forks_path'
+ end
+
+ def choose_namespace(namespace = Runtime::Namespace.path)
+ click_on namespace
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
new file mode 100644
index 00000000000..36567927194
--- /dev/null
+++ b/qa/qa/page/project/import/github.rb
@@ -0,0 +1,66 @@
+module QA
+ module Page
+ module Project
+ module Import
+ class Github < Page::Base
+ include Page::Component::Select2
+
+ view 'app/views/import/github/new.html.haml' do
+ element :personal_access_token_field, 'text_field_tag :personal_access_token'
+ element :list_repos_button, "submit_tag _('List your GitHub repositories')"
+ end
+
+ view 'app/views/import/_githubish_status.html.haml' do
+ element :project_import_row, 'data: { qa: { repo_path: repo.full_name } }'
+ element :project_namespace_select
+ element :project_namespace_field, 'select_tag :namespace_id'
+ element :project_path_field, 'text_field_tag :path, repo.name'
+ element :import_button, "_('Import')"
+ end
+
+ def add_personal_access_token(personal_access_token)
+ fill_in 'personal_access_token', with: personal_access_token
+ end
+
+ def list_repos
+ click_button 'List your GitHub repositories'
+ end
+
+ def import!(full_path, name)
+ choose_test_namespace(full_path)
+ set_path(full_path, name)
+ import_project(full_path)
+ end
+
+ private
+
+ def within_repo_path(full_path)
+ page.within(%Q(tr[data-qa-repo-path="#{full_path}"])) do
+ yield
+ end
+ end
+
+ def choose_test_namespace(full_path)
+ within_repo_path(full_path) do
+ click_element :project_namespace_select
+ end
+
+ select_item(Runtime::Namespace.path)
+ end
+
+ def set_path(full_path, name)
+ within_repo_path(full_path) do
+ fill_in 'path', with: name
+ end
+ end
+
+ def import_project(full_path)
+ within_repo_path(full_path) do
+ click_button 'Import'
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/milestone/index.rb b/qa/qa/page/project/milestone/index.rb
new file mode 100644
index 00000000000..a1519c9ef1c
--- /dev/null
+++ b/qa/qa/page/project/milestone/index.rb
@@ -0,0 +1,17 @@
+module QA
+ module Page
+ module Project
+ module Milestone
+ class Index < Page::Base
+ view 'app/views/projects/milestones/index.html.haml' do
+ element :new_project_milestone
+ end
+
+ def click_new_milestone
+ click_element :new_project_milestone
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/milestone/new.rb b/qa/qa/page/project/milestone/new.rb
new file mode 100644
index 00000000000..992ef89004b
--- /dev/null
+++ b/qa/qa/page/project/milestone/new.rb
@@ -0,0 +1,27 @@
+module QA
+ module Page
+ module Project
+ module Milestone
+ class New < Page::Base
+ view 'app/views/projects/milestones/_form.html.haml' do
+ element :milestone_create_button
+ element :milestone_title
+ element :milestone_description
+ end
+
+ def set_title(title)
+ fill_element :milestone_title, title
+ end
+
+ def set_description(description)
+ fill_element :milestone_description, description
+ end
+
+ def create_new_milestone
+ click_element :milestone_create_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 186a4724326..9e812fa7c74 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -2,18 +2,33 @@ module QA
module Page
module Project
class New < Page::Base
+ include Page::Component::Select2
+
+ view 'app/views/projects/new.html.haml' do
+ element :import_project_tab, "Import project"
+ end
+
view 'app/views/projects/_new_project_fields.html.haml' do
element :project_namespace_select
element :project_namespace_field, /select :namespace_id.*class: 'select2/
element :project_path, 'text_field :path'
element :project_description, 'text_area :description'
element :project_create_button, "submit 'Create project'"
+ element :visibility_radios, 'visibility_level:'
+ end
+
+ view 'app/views/projects/_import_project_pane.html.haml' do
+ element :import_github, "icon('github', text: 'GitHub')"
end
def choose_test_namespace
click_element :project_namespace_select
- find('ul.select2-result-sub > li', text: Runtime::Namespace.path).click
+ select_item(Runtime::Namespace.path)
+ end
+
+ def go_to_import_project
+ click_on 'Import project'
end
def choose_name(name)
@@ -27,6 +42,14 @@ module QA
def create_new_project
click_on 'Create project'
end
+
+ def set_visibility(visibility)
+ choose visibility
+ end
+
+ def go_to_github_import
+ click_link 'GitHub'
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 0bd031e96b5..76591a4e3fe 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -16,7 +16,6 @@ module QA
end
view 'app/views/projects/protected_branches/_update_protected_branch.html.haml' do
- element :allowed_to_push
element :allowed_to_merge
end
@@ -24,10 +23,6 @@ module QA
element :protected_branches_list
end
- view 'app/views/projects/protected_branches/shared/_protected_branch.html.haml' do
- element :protected_branch_name
- end
-
def select_branch(branch_name)
click_element :protected_branch_select
@@ -44,6 +39,9 @@ module QA
click_allow(:push, 'Developers + Maintainers')
end
+ # @deprecated
+ alias_method :allow_devs_and_masters_to_push, :allow_devs_and_maintainers_to_push
+
def allow_no_one_to_merge
click_allow(:merge, 'No one')
end
@@ -52,22 +50,13 @@ module QA
click_allow(:merge, 'Developers + Maintainers')
end
+ # @deprecated
+ alias_method :allow_devs_and_masters_to_merge, :allow_devs_and_maintainers_to_merge
+
def protect_branch
click_on 'Protect'
end
- def last_branch_name
- within_element(:protected_branches_list) do
- all('.qa-protected-branch-name').last
- end
- end
-
- def last_push_allowance
- within_element(:protected_branches_list) do
- all('.qa-allowed-to-push').last
- end
- end
-
private
def click_allow(action, text)
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 1406edece17..1d3dad4cda0 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -22,10 +22,27 @@ module QA
element :branches_dropdown
end
+ view 'app/views/projects/buttons/_fork.html.haml' do
+ element :fork_label, "%span= s_('GoToYourFork|Fork')"
+ element :fork_link, "link_to new_project_fork_path(@project)"
+ end
+
+ view 'app/views/projects/_files.html.haml' do
+ element :tree_holder, '.tree-holder'
+ end
+
+ view 'app/presenters/project_presenter.rb' do
+ element :new_file_button, "label: _('New file'),"
+ end
+
def project_name
find('.qa-project-name').text
end
+ def go_to_new_file!
+ click_on 'New file'
+ end
+
def switch_to_branch(branch_name)
find_element(:branches_select).click
@@ -46,11 +63,21 @@ module QA
click_element :create_merge_request
end
+ def wait_for_import
+ wait(reload: true) do
+ has_css?('.tree-holder')
+ end
+ end
+
def go_to_new_issue
click_element :new_menu_toggle
click_link 'New issue'
end
+
+ def fork_project
+ click_on 'Fork'
+ end
end
end
end
diff --git a/qa/qa/page/view.rb b/qa/qa/page/view.rb
index 6635e1ce039..b2a2da4dbf3 100644
--- a/qa/qa/page/view.rb
+++ b/qa/qa/page/view.rb
@@ -9,7 +9,7 @@ module QA
end
def pathname
- @pathname ||= Pathname.new(File.join(__dir__, '../../../', @path))
+ @pathname ||= Pathname.new(::File.join(__dir__, '../../../', @path))
.cleanpath.expand_path
end
@@ -23,7 +23,7 @@ module QA
# elements' existence.
#
@missing ||= @elements.dup.tap do |elements|
- File.foreach(pathname.to_s) do |line|
+ ::File.foreach(pathname.to_s) do |line|
elements.reject! { |element| element.matches?(line) }
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index cee381f3379..877864fb40c 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -85,6 +85,10 @@ module QA
driver.browser.save_screenshot(path)
end
+ Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example|
+ File.join(QA::Runtime::Namespace.name, example.file_path.sub('./qa/specs/features/', ''))
+ end
+
Capybara.configure do |config|
config.default_driver = :chrome
config.javascript_driver = :chrome
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 7610c7f3f43..5dc194e0aef 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -66,6 +66,17 @@ module QA
def has_gcloud_credentials?
%w[GCLOUD_ACCOUNT_KEY GCLOUD_ACCOUNT_EMAIL].none? { |var| ENV[var].to_s.empty? }
end
+
+ # Specifies the token that can be used for the GitHub API
+ def github_access_token
+ ENV['GITHUB_ACCESS_TOKEN'].to_s.strip
+ end
+
+ def require_github_access_token!
+ return unless github_access_token.empty?
+
+ raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
+ end
end
end
end
diff --git a/qa/qa/runtime/namespace.rb b/qa/qa/runtime/namespace.rb
index 8d05b387416..f1c8ef11f94 100644
--- a/qa/qa/runtime/namespace.rb
+++ b/qa/qa/runtime/namespace.rb
@@ -8,7 +8,7 @@ module QA
end
def name
- 'qa-test-' + time.strftime('%d-%m-%Y-%H-%M-%S')
+ "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}"
end
def path
@@ -16,7 +16,7 @@ module QA
end
def sandbox_name
- Runtime::Env.sandbox_name || 'gitlab-qa-sandbox'
+ Runtime::Env.sandbox_name || 'gitlab-qa-sandbox-group'
end
end
end
diff --git a/qa/qa/runtime/release.rb b/qa/qa/runtime/release.rb
index 12e56404cf6..4f83a773645 100644
--- a/qa/qa/runtime/release.rb
+++ b/qa/qa/runtime/release.rb
@@ -21,7 +21,7 @@ module QA
end
def self.method_missing(name, *args)
- self.new.strategy.public_send(name, *args) # rubocop:disable GitlabSecurity/PublicSend
+ self.new.strategy.public_send(name, *args)
end
end
end
diff --git a/qa/qa/scenario/test/integration/github.rb b/qa/qa/scenario/test/integration/github.rb
new file mode 100644
index 00000000000..1d22b532aa5
--- /dev/null
+++ b/qa/qa/scenario/test/integration/github.rb
@@ -0,0 +1,18 @@
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class Github < Test::Instance
+ tags :github
+
+ def perform(address, *rspec_options)
+ # This test suite requires a GitHub personal access token
+ Runtime::Env.require_github_access_token!
+
+ super
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/login/standard_spec.rb b/qa/qa/specs/features/login/standard_spec.rb
deleted file mode 100644
index 254f47cf217..00000000000
--- a/qa/qa/specs/features/login/standard_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module QA
- describe 'standard user login', :core do
- it 'user logs in using credentials' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- # TODO, since `Signed in successfully` message was removed
- # this is the only way to tell if user is signed in correctly.
- #
- Page::Menu::Main.perform do |menu|
- expect(menu).to have_personal_area
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb
index 18e8c1f35af..36d7efb02e1 100644
--- a/qa/qa/specs/features/merge_request/create_spec.rb
+++ b/qa/qa/specs/features/merge_request/create_spec.rb
@@ -4,14 +4,29 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
+ current_project = Factory::Resource::Project.fabricate! do |project|
+ project.name = 'project-with-merge-request-and-milestone'
+ end
+
+ current_milestone = Factory::Resource::ProjectMilestone.fabricate! do |milestone|
+ milestone.title = 'unique-milestone'
+ milestone.project = current_project
+ end
+
Factory::Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.title = 'This is a merge request'
- merge_request.description = 'Great feature'
+ merge_request.title = 'This is a merge request with a milestone'
+ merge_request.description = 'Great feature with milestone'
+ merge_request.project = current_project
+ merge_request.milestone = current_milestone
end
- expect(page).to have_content('This is a merge request')
- expect(page).to have_content('Great feature')
+ expect(page).to have_content('This is a merge request with a milestone')
+ expect(page).to have_content('Great feature with milestone')
expect(page).to have_content(/Opened [\w\s]+ ago/)
+
+ Page::Issuable::Sidebar.perform do |sidebar|
+ expect(sidebar).to have_milestone(current_milestone.title)
+ end
end
end
end
diff --git a/qa/qa/specs/features/project/file_spec.rb b/qa/qa/specs/features/project/file_spec.rb
new file mode 100644
index 00000000000..5659784cd5c
--- /dev/null
+++ b/qa/qa/specs/features/project/file_spec.rb
@@ -0,0 +1,54 @@
+module QA
+ describe 'File Functionality', :core do
+ it 'lets a user create, edit and delete a file via WebUI' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ # Create
+ file_name = 'QA Test - File name'
+ file_content = 'QA Test - File content'
+ commit_message_for_create = 'QA Test - Create new file'
+
+ Factory::Resource::File.fabricate! do |file|
+ file.name = file_name
+ file.content = file_content
+ file.commit_message = commit_message_for_create
+ end
+
+ expect(page).to have_content('The file has been successfully created.')
+ expect(page).to have_content(file_name)
+ expect(page).to have_content(file_content)
+ expect(page).to have_content(commit_message_for_create)
+
+ # Edit
+ updated_file_content = 'QA Test - Updated file content'
+ commit_message_for_update = 'QA Test - Update file'
+
+ Page::File::Show.act { click_edit }
+
+ Page::File::Form.act do
+ remove_content
+ add_content(updated_file_content)
+ add_commit_message(commit_message_for_update)
+ commit_changes
+ end
+
+ expect(page).to have_content('Your changes have been successfully committed.')
+ expect(page).to have_content(updated_file_content)
+ expect(page).to have_content(commit_message_for_update)
+
+ # Delete
+ commit_message_for_delete = 'QA Test - Delete file'
+
+ Page::File::Show.act do
+ click_delete
+ add_commit_message(commit_message_for_delete)
+ click_delete_file
+ end
+
+ expect(page).to have_content('The file has been successfully deleted.')
+ expect(page).to have_content(commit_message_for_delete)
+ expect(page).to have_no_content(file_name)
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/fork_project_spec.rb b/qa/qa/specs/features/project/fork_project_spec.rb
new file mode 100644
index 00000000000..8ad0120305a
--- /dev/null
+++ b/qa/qa/specs/features/project/fork_project_spec.rb
@@ -0,0 +1,23 @@
+module QA
+ describe 'Project fork', :core do
+ it 'can submit merge requests to upstream master' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request|
+ merge_request.fork_branch = 'feature-branch'
+ end
+
+ Page::Menu::Main.act { sign_out }
+ Page::Main::Login.act do
+ switch_to_sign_in_tab
+ sign_in_using_credentials
+ end
+
+ merge_request.visit!
+ Page::MergeRequest::Show.act { merge! }
+
+ expect(page).to have_content('The changes were merged')
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/import_from_github_spec.rb b/qa/qa/specs/features/project/import_from_github_spec.rb
new file mode 100644
index 00000000000..221b5c27fba
--- /dev/null
+++ b/qa/qa/specs/features/project/import_from_github_spec.rb
@@ -0,0 +1,106 @@
+module QA
+ describe 'user imports a GitHub repo', :core, :github do
+ let(:imported_project) do
+ Factory::Resource::ProjectImportedFromGithub.fabricate! do |project|
+ project.name = 'imported-project'
+ project.personal_access_token = Runtime::Env.github_access_token
+ project.github_repository_path = 'gitlab-qa/test-project'
+ end
+ end
+
+ after do
+ # We need to delete the imported project because it's impossible to import
+ # the same GitHub project twice for a given user.
+ api_client = Runtime::API::Client.new(:gitlab)
+ delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
+ delete delete_project_request.url
+
+ expect_status(202)
+ end
+
+ it 'user imports a GitHub repo' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ imported_project # import the project
+
+ Page::Menu::Main.act { go_to_projects }
+ Page::Dashboard::Projects.perform do |dashboard|
+ dashboard.go_to_project(imported_project.name)
+ end
+
+ Page::Project::Show.act { wait_for_import }
+
+ verify_repository_import
+ verify_issues_import
+ verify_merge_requests_import
+ verify_labels_import
+ verify_milestones_import
+ verify_wiki_import
+ end
+
+ def verify_repository_import
+ expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
+ expect(page).to have_content(imported_project.name)
+ end
+
+ def verify_issues_import
+ Page::Menu::Side.act { click_issues }
+ expect(page).to have_content('This is a sample issue')
+
+ click_link 'This is a sample issue'
+
+ expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
+
+ # Comments
+ expect(page).to have_content('This is a comment from @rymai.')
+
+ Page::Issuable::Sidebar.perform do |issuable|
+ expect(issuable).to have_label('enhancement')
+ expect(issuable).to have_label('help wanted')
+ expect(issuable).to have_label('good first issue')
+ end
+ end
+
+ def verify_merge_requests_import
+ Page::Menu::Side.act { click_merge_requests }
+ expect(page).to have_content('Improve README.md')
+
+ click_link 'Improve README.md'
+
+ expect(page).to have_content('This improves the README file a bit.')
+
+ # Review comment are not supported yet
+ expect(page).not_to have_content('Really nice change.')
+
+ # Comments
+ expect(page).to have_content('Nice work! This is a comment from @rymai.')
+
+ # Diff comments
+ expect(page).to have_content('[Review comment] I like that!')
+ expect(page).to have_content('[Review comment] Nice blank line.')
+ expect(page).to have_content('[Single diff comment] Much better without this line!')
+
+ Page::Issuable::Sidebar.perform do |issuable|
+ expect(issuable).to have_label('bug')
+ expect(issuable).to have_label('enhancement')
+ end
+ end
+
+ def verify_labels_import
+ # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19228
+ # to build upon it.
+ end
+
+ def verify_milestones_import
+ # TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18727
+ # to build upon it.
+ end
+
+ def verify_wiki_import
+ Page::Menu::Side.act { click_wiki }
+
+ expect(page).to have_content('Welcome to the test-project wiki!')
+ end
+ end
+end
diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb
index 29ea2e69ec7..c2de94516d9 100644
--- a/qa/qa/specs/features/repository/protected_branches_spec.rb
+++ b/qa/qa/specs/features/repository/protected_branches_spec.rb
@@ -13,27 +13,20 @@ module QA
Page::Main::Login.act { sign_in_using_credentials }
end
- after do |example|
+ after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
-
- # In order to help diagnose a false failure
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/48241
- log_push_output if example.exception
end
context 'when developers and maintainers are allowed to push to a protected branch' do
- let!(:protected_branch) { create_protected_branch(allow_to_push: true) }
-
it 'user with push rights successfully pushes to the protected branch' do
- expect(protected_branch.name).to have_content(branch_name)
- expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
+ create_protected_branch(allow_to_push: true)
- @push = push_new_file(branch_name)
+ push = push_new_file(branch_name)
- expect(@push.output).to match(/remote: To create a merge request for protected-branch, visit/)
+ expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
end
@@ -41,11 +34,11 @@ module QA
it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
- @push = push_new_file(branch_name)
+ push = push_new_file(branch_name)
- expect(@push.output)
+ expect(push.output)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
- expect(@push.output)
+ expect(push.output)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
@@ -69,13 +62,5 @@ module QA
resource.new_branch = false
end
end
-
- def log_push_output
- if defined?(@push)
- filename = File.join('tmp', "push-output-#{project.name}")
- puts "Exception detected. Push output will be saved to #{filename}"
- IO.binwrite(filename, @push.output)
- end
- end
end
end
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 2b6365dbc41..851026c71f0 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -76,4 +76,27 @@ describe QA::Runtime::Env do
expect { described_class.user_type }.to raise_error(ArgumentError)
end
end
+
+ describe '.github_access_token' do
+ it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do
+ expect(described_class.github_access_token).to eq('')
+ end
+
+ it 'returns stripped string if GITHUB_ACCESS_TOKEN is defined' do
+ stub_env('GITHUB_ACCESS_TOKEN', ' abc123 ')
+ expect(described_class.github_access_token).to eq('abc123')
+ end
+ end
+
+ describe '.require_github_access_token!' do
+ it 'raises ArgumentError if GITHUB_ACCESS_TOKEN is not defined' do
+ expect { described_class.require_github_access_token! }.to raise_error(ArgumentError)
+ end
+
+ it 'does not raise if GITHUB_ACCESS_TOKEN is defined' do
+ stub_env('GITHUB_ACCESS_TOKEN', ' abc123 ')
+
+ expect { described_class.require_github_access_token! }.not_to raise_error
+ end
+ end
end
diff --git a/rubocop/cop/line_break_around_conditional_block.rb b/rubocop/cop/line_break_around_conditional_block.rb
index 8b6052fee1b..011f2bcf8bf 100644
--- a/rubocop/cop/line_break_around_conditional_block.rb
+++ b/rubocop/cop/line_break_around_conditional_block.rb
@@ -43,6 +43,8 @@ module RuboCop
# end
# end
class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
+ include RangeHelp
+
MSG = 'Add a line break around conditional blocks'
def on_if(node)
diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js
index 6e4e36b9b2d..b66ba885701 100644
--- a/scripts/frontend/prettier.js
+++ b/scripts/frontend/prettier.js
@@ -49,7 +49,7 @@ const stagedFiles =
if (stagedFiles) {
if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) {
console.log('No matching staged files.');
- return;
+ process.exit(1);
}
console.log(`Matching staged Files : ${stagedFiles.length}`);
}
@@ -78,7 +78,7 @@ files = prettierIgnore.filter(files);
if (!files.length) {
console.log('No Files found to process with Prettier');
- return;
+ process.exit(1);
}
console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`);
diff --git a/spec/bin/changelog_spec.rb b/spec/bin/changelog_spec.rb
index f278043028f..9dc4edf97d1 100644
--- a/spec/bin/changelog_spec.rb
+++ b/spec/bin/changelog_spec.rb
@@ -3,6 +3,20 @@ require 'spec_helper'
load File.expand_path('../../bin/changelog', __dir__)
describe 'bin/changelog' do
+ let(:options) { OpenStruct.new(title: 'Test title', type: 'fixed', dry_run: true) }
+
+ describe ChangelogEntry do
+ it 'truncates the file path' do
+ entry = described_class.new(options)
+
+ allow(entry).to receive(:ee?).and_return(false)
+ allow(entry).to receive(:branch_name).and_return('long-branch-' * 100)
+
+ file_path = entry.send(:file_path)
+ expect(file_path.length).to eq(140)
+ end
+ end
+
describe ChangelogOptionParser do
describe '.parse' do
it 'parses --amend' do
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index fb6d82d7de3..2c59d1929a1 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -228,12 +228,12 @@ describe AutocompleteController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'authorized projects' do
before do
- authorized_project.add_master(user)
+ authorized_project.add_maintainer(user)
end
describe 'GET #projects with project ID' do
@@ -253,8 +253,8 @@ describe AutocompleteController do
context 'authorized projects and search' do
before do
- authorized_project.add_master(user)
- authorized_search_project.add_master(user)
+ authorized_project.add_maintainer(user)
+ authorized_search_project.add_maintainer(user)
end
describe 'GET #projects with project ID and search' do
@@ -277,9 +277,9 @@ describe AutocompleteController do
authorized_project2 = create(:project)
authorized_project3 = create(:project)
- authorized_project.add_master(user)
- authorized_project2.add_master(user)
- authorized_project3.add_master(user)
+ authorized_project.add_maintainer(user)
+ authorized_project2.add_maintainer(user)
+ authorized_project3.add_maintainer(user)
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
end
@@ -301,9 +301,9 @@ describe AutocompleteController do
authorized_project2 = create(:project)
authorized_project3 = create(:project)
- authorized_project.add_master(user)
- authorized_project2.add_master(user)
- authorized_project3.add_master(user)
+ authorized_project.add_maintainer(user)
+ authorized_project2.add_maintainer(user)
+ authorized_project3.add_maintainer(user)
end
describe 'GET #projects with project ID and offset_id' do
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index e47ff8661a2..ce7762691c9 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -13,7 +13,7 @@ describe Boards::IssuesController do
let!(:list2) { create(:list, board: board, label: development, position: 1) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_guest(guest)
end
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 57ccbf1d6b5..80631d2efb0 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -7,7 +7,7 @@ describe Boards::ListsController do
let(:guest) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_guest(guest)
end
diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb
index ba84fbf8564..503eb416962 100644
--- a/spec/controllers/concerns/group_tree_spec.rb
+++ b/spec/controllers/concerns/group_tree_spec.rb
@@ -63,6 +63,17 @@ describe GroupTree do
expect(assigns(:groups)).to contain_exactly(parent, subgroup)
end
+
+ it 'preloads parents regardless of pagination' do
+ allow(Kaminari.config).to receive(:default_per_page).and_return(1)
+ group = create(:group, :public)
+ subgroup = create(:group, :public, parent: group)
+ search_result = create(:group, :public, name: 'result', parent: subgroup)
+
+ get :index, filter: 'resu', format: :json
+
+ expect(assigns(:groups)).to contain_exactly(group, subgroup, search_result)
+ end
end
context 'json content' do
diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb
index 7f2eaf95165..9068c1a792e 100644
--- a/spec/controllers/dashboard/groups_controller_spec.rb
+++ b/spec/controllers/dashboard/groups_controller_spec.rb
@@ -28,8 +28,8 @@ describe Dashboard::GroupsController do
let!(:other_group) { create(:group, name: 'other') }
before do
- top_level_result.add_master(user)
- top_level_a.add_master(user)
+ top_level_result.add_maintainer(user)
+ top_level_a.add_maintainer(user)
end
it 'renders only groups the user is a member of when searching hierarchy correctly' do
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 60547db82b6..ba2669a5ea7 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -17,7 +17,7 @@ describe Dashboard::MilestonesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'milestone tabs'
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb
index 3458d679107..187542ba30c 100644
--- a/spec/controllers/dashboard_controller_spec.rb
+++ b/spec/controllers/dashboard_controller_spec.rb
@@ -5,7 +5,7 @@ describe DashboardController do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb
index 0f5bde62006..bf41aa0706f 100644
--- a/spec/controllers/groups/boards_controller_spec.rb
+++ b/spec/controllers/groups/boards_controller_spec.rb
@@ -5,7 +5,7 @@ describe Groups::BoardsController do
let(:user) { create(:user) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index 733386500ca..f7068546093 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -28,7 +28,7 @@ describe Groups::MilestonesController do
before do
sign_in(user)
group.add_owner(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#index' do
diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb
index 5770d15557c..598fb84552f 100644
--- a/spec/controllers/groups/runners_controller_spec.rb
+++ b/spec/controllers/groups/runners_controller_spec.rb
@@ -14,7 +14,7 @@ describe Groups::RunnersController do
before do
sign_in(user)
- group.add_master(user)
+ group.add_maintainer(user)
end
describe '#update' do
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
index e9f0924caba..ea18122e0c3 100644
--- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
@@ -5,7 +5,7 @@ describe Groups::Settings::CiCdController do
let(:user) { create(:user) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/variables_controller_spec.rb b/spec/controllers/groups/variables_controller_spec.rb
index 39a36b92bb4..e5ac5634f95 100644
--- a/spec/controllers/groups/variables_controller_spec.rb
+++ b/spec/controllers/groups/variables_controller_spec.rb
@@ -6,7 +6,7 @@ describe Groups::VariablesController do
before do
sign_in(user)
- group.add_master(user)
+ group.add_maintainer(user)
end
describe 'GET #show' do
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 8688fb33f0d..7a037828035 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -7,7 +7,7 @@ describe GroupsController do
let(:project) { create(:project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
let!(:owner) { group.add_owner(create(:user)).user }
- let!(:master) { group.add_master(create(:user)).user }
+ let!(:maintainer) { group.add_maintainer(create(:user)).user }
let!(:developer) { group.add_developer(create(:user)).user }
let!(:guest) { group.add_guest(create(:user)).user }
@@ -62,7 +62,7 @@ describe GroupsController do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
before do
- User.where(id: [admin, owner, master, developer, guest]).update_all(can_create_group: can_create_group_status)
+ User.where(id: [admin, owner, maintainer, developer, guest]).update_all(can_create_group: can_create_group_status)
end
[:admin, :owner].each do |member_type|
@@ -73,7 +73,7 @@ describe GroupsController do
end
end
- [:guest, :developer, :master].each do |member_type|
+ [:guest, :developer, :maintainer].each do |member_type|
context "and logged in as #{member_type.capitalize}" do
it_behaves_like 'member without ability to create subgroups' do
let(:member) { send(member_type) }
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 7376841fac8..c7c83369d7c 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -15,55 +15,16 @@ describe MetricsController do
allow(Prometheus::Client.configuration).to receive(:multiprocess_files_dir).and_return(metrics_multiproc_dir)
allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(true)
allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip, whitelisted_ip_range])
+ allow_any_instance_of(MetricsService).to receive(:metrics_text).and_return("prometheus_counter 1")
end
describe '#index' do
shared_examples_for 'endpoint providing metrics' do
- it 'returns DB ping metrics' do
+ it 'returns prometheus metrics' do
get :index
- expect(response.body).to match(/^db_ping_timeout 0$/)
- expect(response.body).to match(/^db_ping_success 1$/)
- expect(response.body).to match(/^db_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Redis ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_ping_timeout 0$/)
- expect(response.body).to match(/^redis_ping_success 1$/)
- expect(response.body).to match(/^redis_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Caching ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_cache_ping_timeout 0$/)
- expect(response.body).to match(/^redis_cache_ping_success 1$/)
- expect(response.body).to match(/^redis_cache_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Queues ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_queues_ping_timeout 0$/)
- expect(response.body).to match(/^redis_queues_ping_success 1$/)
- expect(response.body).to match(/^redis_queues_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns SharedState ping metrics' do
- get :index
-
- expect(response.body).to match(/^redis_shared_state_ping_timeout 0$/)
- expect(response.body).to match(/^redis_shared_state_ping_success 1$/)
- expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/)
- end
-
- it 'returns Gitaly metrics' do
- get :index
-
- expect(response.body).to match(/^gitaly_health_check_success{shard="default"} 1$/)
- expect(response.body).to match(/^gitaly_health_check_latency_seconds{shard="default"} [0-9\.]+$/)
+ expect(response.status).to eq(200)
+ expect(response.body).to match(/^prometheus_counter 1$/)
end
context 'prometheus metrics are disabled' do
@@ -101,7 +62,7 @@ describe MetricsController do
allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip)
end
- it 'returns proper response' do
+ it 'returns the expected error response' do
get :index
expect(response.status).to eq(404)
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index acfa2730d94..17c9a61f339 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::AvatarsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/badges_controller_spec.rb b/spec/controllers/projects/badges_controller_spec.rb
index e7cddf8cfbf..dfe34171b55 100644
--- a/spec/controllers/projects/badges_controller_spec.rb
+++ b/spec/controllers/projects/badges_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::BadgesController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb
index 88d4f4e9cd0..fe4c4863717 100644
--- a/spec/controllers/projects/blame_controller_spec.rb
+++ b/spec/controllers/projects/blame_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::BlameController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 4dcb7dc6c87..32cd7c6e70a 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -108,7 +108,7 @@ describe Projects::BlobController do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -230,12 +230,12 @@ describe Projects::BlobController do
end
end
- context 'as master' do
- let(:master) { create(:user) }
+ context 'as maintainer' do
+ let(:maintainer) { create(:user) }
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
get :edit, default_params
end
@@ -263,7 +263,7 @@ describe Projects::BlobController do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index 509f19ed030..096efc1c7b2 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::BoardsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 4860ea5dcce..31471cde420 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::BranchesController do
let(:developer) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user)
allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz'])
diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb
index 99fdff5f846..9e17e392d3d 100644
--- a/spec/controllers/projects/clusters/applications_controller_spec.rb
+++ b/spec/controllers/projects/clusters/applications_controller_spec.rb
@@ -17,7 +17,7 @@ describe Projects::Clusters::ApplicationsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -70,7 +70,7 @@ describe Projects::Clusters::ApplicationsController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb
index e47ccdc9aea..42917d0d505 100644
--- a/spec/controllers/projects/clusters_controller_spec.rb
+++ b/spec/controllers/projects/clusters_controller_spec.rb
@@ -11,7 +11,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -61,7 +61,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -79,7 +79,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -142,7 +142,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -156,7 +156,7 @@ describe Projects::ClustersController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -185,7 +185,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -236,7 +236,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -267,7 +267,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -286,7 +286,7 @@ describe Projects::ClustersController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -306,7 +306,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -327,7 +327,7 @@ describe Projects::ClustersController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -350,7 +350,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -365,7 +365,7 @@ describe Projects::ClustersController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -386,7 +386,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -437,7 +437,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -525,7 +525,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -552,7 +552,7 @@ describe Projects::ClustersController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -605,7 +605,7 @@ describe Projects::ClustersController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 003fec8ac68..916a4be2567 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::CommitController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET show' do
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index 55ed276f96b..d44048fdf55 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CommitsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET show" do
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index b15cde4314e..8695aa826bb 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CompareController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET index' do
diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb
index 5516c95d044..5c79269e8f1 100644
--- a/spec/controllers/projects/cycle_analytics_controller_spec.rb
+++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CycleAnalyticsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'cycle analytics not set up flag' do
diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb
index 97db69427e9..d2f133f972a 100644
--- a/spec/controllers/projects/deploy_keys_controller_spec.rb
+++ b/spec/controllers/projects/deploy_keys_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::DeployKeysController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb
index 6c67dfde63a..d1c960e895d 100644
--- a/spec/controllers/projects/deployments_controller_spec.rb
+++ b/spec/controllers/projects/deployments_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::DeploymentsController do
let(:environment) { create(:environment, name: 'production', project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 63cef579864..b86029a4baf 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::EnvironmentsController do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb
index 505fe82851a..66fe41108e2 100644
--- a/spec/controllers/projects/find_file_controller_spec.rb
+++ b/spec/controllers/projects/find_file_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::FindFileController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index e20623c0ac1..945b6142abf 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -31,7 +31,7 @@ describe Projects::ForksController do
context 'when fork is private' do
before do
- forked_project.update_attributes(visibility_level: Project::PRIVATE, group: group)
+ forked_project.update(visibility_level: Project::PRIVATE, group: group)
end
it 'is not be visible for non logged in users' do
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index c3605555fe7..da78592a6f6 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::GraphsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET languages' do
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 72f6af112b3..879aff26deb 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::GroupLinksController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -23,7 +23,7 @@ describe Projects::GroupLinksController do
context 'when project is not allowed to be shared with a group' do
before do
- group.update_attributes(share_with_group_lock: false)
+ group.update(share_with_group_lock: false)
end
include_context 'link project to group'
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index 2d473d5bf52..0f3033b0933 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::HooksController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 812833cc86b..adf3c78ae51 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::ImportsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET #show' do
@@ -29,7 +29,7 @@ describe Projects::ImportsController do
context 'when import is in progress' do
before do
- project.update_attributes(import_status: :started)
+ project.update(import_status: :started)
end
it 'renders template' do
@@ -47,7 +47,7 @@ describe Projects::ImportsController do
context 'when import failed' do
before do
- project.update_attributes(import_status: :failed)
+ project.update(import_status: :failed)
end
it 'redirects to new_namespace_project_import_path' do
@@ -59,7 +59,7 @@ describe Projects::ImportsController do
context 'when import finished' do
before do
- project.update_attributes(import_status: :finished)
+ project.update(import_status: :finished)
end
context 'when project is a fork' do
@@ -108,7 +108,7 @@ describe Projects::ImportsController do
context 'when import never happened' do
before do
- project.update_attributes(import_status: :none)
+ project.update(import_status: :none)
end
it 'redirects to namespace_project_path' do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 3a41f0fc07a..ff1835a34c2 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -695,7 +695,7 @@ describe Projects::IssuesController do
let(:project) { merge_request.source_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
@@ -869,7 +869,7 @@ describe Projects::IssuesController do
def post_spam
admin = create(:admin)
create(:user_agent_detail, subject: issue)
- project.add_master(admin)
+ project.add_maintainer(admin)
sign_in(admin)
post :mark_as_spam, {
namespace_id: project.namespace,
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index e6332a10265..1aca44c6e74 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -216,17 +216,19 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'when trace artifact is in ObjectStorage' do
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
let!(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }
before do
allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
- allow_any_instance_of(JobArtifactUploader).to receive(:url) { remote_trace_url }
- allow_any_instance_of(JobArtifactUploader).to receive(:size) { remote_trace_size }
+ allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
+ allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
end
context 'when there are no network issues' do
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
get_trace
end
@@ -241,11 +243,11 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when there is a network issue' do
before do
- stub_remote_trace_500
+ stub_remote_url_500(url)
end
it 'returns a trace' do
- expect { get_trace }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
+ expect { get_trace }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
end
end
end
@@ -429,7 +431,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'POST erase' do
- let(:role) { :master }
+ let(:role) { :maintainer }
before do
project.add_role(user, role)
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 452d7e23983..273702e6d21 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::LabelsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index c5ac0be27bb..c2a334a849c 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::MattermostsController do
let!(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 00d76f3c39a..d8995f98575 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -16,7 +16,7 @@ describe Projects::MergeRequests::CreationsController do
end
before do
- fork_project.add_master(user)
+ fork_project.add_maintainer(user)
Projects::ForkService.new(project, user).execute(fork_project)
sign_in(user)
end
@@ -94,7 +94,7 @@ describe Projects::MergeRequests::CreationsController do
let(:other_project) { create(:project, :repository) }
before do
- other_project.add_master(user)
+ other_project.add_maintainer(user)
end
context 'when the path exists in the diff' do
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index ec82b35f227..9dc06436c72 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -140,7 +140,7 @@ describe Projects::MergeRequests::DiffsController do
let(:other_project) { create(:project) }
before do
- other_project.add_master(user)
+ other_project.add_maintainer(user)
diff_for_path(old_path: existing_path, new_path: existing_path, project_id: other_project)
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 7f5f0b76c51..444415011a9 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -315,7 +315,7 @@ describe Projects::MergeRequestsController do
context 'when the merge request is not mergeable' do
before do
- merge_request.update_attributes(title: "WIP: #{merge_request.title}")
+ merge_request.update(title: "WIP: #{merge_request.title}")
post :merge, base_params
end
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index b1d83246238..ea906cf7f32 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -11,7 +11,7 @@ describe Projects::MilestonesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index 8d2fa6a1740..927b6e0c473 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -14,7 +14,7 @@ describe Projects::PagesController do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET show' do
diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb
index d4058a5c515..75871eab1ab 100644
--- a/spec/controllers/projects/pages_domains_controller_spec.rb
+++ b/spec/controllers/projects/pages_domains_controller_spec.rb
@@ -19,7 +19,7 @@ describe Projects::PagesDomainsController do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET show' do
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 4cdaa54e0bc..7179423dde2 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -121,7 +121,7 @@ describe Projects::PipelineSchedulesController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -274,7 +274,7 @@ describe Projects::PipelineSchedulesController do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project).own(pipeline_schedule) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -292,19 +292,19 @@ describe Projects::PipelineSchedulesController do
it { expect { go }.to be_allowed_for(developer_1) }
it { expect { go }.to be_denied_for(:developer).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
end
- context 'when a master created a pipeline schedule' do
- let(:master_1) { create(:user) }
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master_1) }
+ context 'when a maintainer created a pipeline schedule' do
+ let(:maintainer_1) { create(:user) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: maintainer_1) }
before do
- project.add_master(master_1)
+ project.add_maintainer(maintainer_1)
end
- it { expect { go }.to be_allowed_for(master_1) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(maintainer_1) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
end
end
@@ -331,7 +331,7 @@ describe Projects::PipelineSchedulesController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -346,7 +346,7 @@ describe Projects::PipelineSchedulesController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project).own(pipeline_schedule) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -364,7 +364,7 @@ describe Projects::PipelineSchedulesController do
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
- it { expect { go }.to be_allowed_for(:master).of(project) }
+ it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_allowed_for(:developer).of(project).own(pipeline_schedule) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
@@ -453,9 +453,9 @@ describe Projects::PipelineSchedulesController do
end
end
- context 'when a master makes the request' do
+ context 'when a maintainer makes the request' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/pipelines_settings_controller_spec.rb b/spec/controllers/projects/pipelines_settings_controller_spec.rb
index 694896b6bcf..b1ba9f74e38 100644
--- a/spec/controllers/projects/pipelines_settings_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_settings_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::PipelinesSettingsController do
let(:project) { project_auto_devops.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index d84b31ad978..519af10d78c 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -37,7 +37,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds user to members' do
@@ -70,7 +70,7 @@ describe Projects::ProjectMembersController do
let(:requester) { create(:project_member, :access_request, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -121,7 +121,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it '[HTML] removes user from members' do
@@ -181,7 +181,7 @@ describe Projects::ProjectMembersController do
let(:project) { create(:project, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'cannot remove themselves from the project' do
@@ -263,7 +263,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds user to members' do
@@ -285,7 +285,7 @@ describe Projects::ProjectMembersController do
let(:member) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
another_project.add_guest(member)
sign_in(user)
end
@@ -332,7 +332,7 @@ describe Projects::ProjectMembersController do
context 'when creating owner' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -346,9 +346,9 @@ describe Projects::ProjectMembersController do
end
end
- context 'when create master' do
+ context 'when create maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -356,7 +356,7 @@ describe Projects::ProjectMembersController do
expect do
post :create, user_ids: stranger.id,
namespace_id: project.namespace,
- access_level: Member::MASTER,
+ access_level: Member::MAINTAINER,
project_id: project
end.to change { project.members.count }.by(1)
end
diff --git a/spec/controllers/projects/prometheus/metrics_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
index 871dcf5c796..5c56a712245 100644
--- a/spec/controllers/projects/prometheus/metrics_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::Prometheus::MetricsController do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/protected_branches_controller_spec.rb b/spec/controllers/projects/protected_branches_controller_spec.rb
index 096e29bc39f..ac812707e74 100644
--- a/spec/controllers/projects/protected_branches_controller_spec.rb
+++ b/spec/controllers/projects/protected_branches_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::ProtectedBranchesController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET #index" do
@@ -20,10 +20,10 @@ describe Projects::ProtectedBranchesController do
end
describe "POST #create" do
- let(:master_access_level) { [{ access_level: Gitlab::Access::MASTER }] }
+ let(:maintainer_access_level) { [{ access_level: Gitlab::Access::MAINTAINER }] }
let(:access_level_params) do
- { merge_access_levels_attributes: master_access_level,
- push_access_levels_attributes: master_access_level }
+ { merge_access_levels_attributes: maintainer_access_level,
+ push_access_levels_attributes: maintainer_access_level }
end
let(:create_params) { attributes_for(:protected_branch).merge(access_level_params) }
diff --git a/spec/controllers/projects/protected_tags_controller_spec.rb b/spec/controllers/projects/protected_tags_controller_spec.rb
index b6de90039f3..20440c5a5d5 100644
--- a/spec/controllers/projects/protected_tags_controller_spec.rb
+++ b/spec/controllers/projects/protected_tags_controller_spec.rb
@@ -15,7 +15,7 @@ describe Projects::ProtectedTagsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb
index 2082dd2cff0..b1e0b496ede 100644
--- a/spec/controllers/projects/runners_controller_spec.rb
+++ b/spec/controllers/projects/runners_controller_spec.rb
@@ -15,7 +15,7 @@ describe Projects::RunnersController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#update' do
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index 61f35cf325b..45cea8c1351 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::ServicesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#test' do
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index d53fe9bf734..1f14a0cc381 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::Settings::CiCdController do
let(:project) { project_auto_devops.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -27,7 +27,7 @@ describe Projects::Settings::CiCdController do
let!(:shared_runner) { create(:ci_runner, :instance) }
it 'sets assignable project runners only' do
- group.add_master(user)
+ group.add_maintainer(user)
get :show, namespace_id: project.namespace, project_id: project
@@ -40,7 +40,7 @@ describe Projects::Settings::CiCdController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(true)
end
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index 77df9a6f567..a2484c04c7a 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::Settings::IntegrationsController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb
index 3a4014b7768..9cee40b7553 100644
--- a/spec/controllers/projects/settings/repository_controller_spec.rb
+++ b/spec/controllers/projects/settings/repository_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::Settings::RepositoryController do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index e7c0b484ede..9c383bd7628 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -6,8 +6,8 @@ describe Projects::SnippetsController do
let(:user2) { create(:user) }
before do
- project.add_master(user)
- project.add_master(user2)
+ project.add_maintainer(user)
+ project.add_maintainer(user2)
end
describe 'GET #index' do
@@ -291,7 +291,7 @@ describe Projects::SnippetsController do
def mark_as_spam
admin = create(:admin)
create(:user_agent_detail, subject: snippet)
- project.add_master(admin)
+ project.add_maintainer(admin)
sign_in(admin)
post :mark_as_spam,
diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb
index 8fcfa3c9ecd..d7f07aa2b01 100644
--- a/spec/controllers/projects/templates_controller_spec.rb
+++ b/spec/controllers/projects/templates_controller_spec.rb
@@ -13,7 +13,7 @@ describe Projects::TemplatesController do
end
before do
- project.add_user(user, Gitlab::Access::MASTER)
+ project.add_user(user, Gitlab::Access::MAINTAINER)
project.repository.create_file(user, file_path_1, 'something valid',
message: 'test 3', branch_name: 'master')
end
diff --git a/spec/controllers/projects/todos_controller_spec.rb b/spec/controllers/projects/todos_controller_spec.rb
index 58f2817c7cc..1ce7e84bef9 100644
--- a/spec/controllers/projects/todos_controller_spec.rb
+++ b/spec/controllers/projects/todos_controller_spec.rb
@@ -5,29 +5,10 @@ describe Projects::TodosController do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, source_project: project) }
- let(:parent) { project }
-
- shared_examples 'project todos actions' do
- it_behaves_like 'todos actions'
-
- context 'when not authorized for resource' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
- sign_in(user)
- end
-
- it "doesn't create todo" do
- expect { post_create }.not_to change { user.todos.count }
- expect(response).to have_gitlab_http_status(404)
- end
- end
- end
context 'Issues' do
describe 'POST create' do
- def post_create
+ def go
post :create,
namespace_id: project.namespace,
project_id: project,
@@ -36,13 +17,66 @@ describe Projects::TodosController do
format: 'html'
end
- it_behaves_like 'project todos actions'
+ context 'when authorized' do
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ it 'creates todo for issue' do
+ expect do
+ go
+ end.to change { user.todos.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns todo path and pending count' do
+ go
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['count']).to eq 1
+ expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
+ end
+ end
+
+ context 'when not authorized for project' do
+ it 'does not create todo for issue that user has no access to' do
+ sign_in(user)
+ expect do
+ go
+ end.to change { user.todos.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not create todo for issue when user not logged in' do
+ expect do
+ go
+ end.to change { user.todos.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when not authorized for issue' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ sign_in(user)
+ end
+
+ it "doesn't create todo" do
+ expect { go }.not_to change { user.todos.count }
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
end
end
context 'Merge Requests' do
describe 'POST create' do
- def post_create
+ def go
post :create,
namespace_id: project.namespace,
project_id: project,
@@ -51,7 +85,60 @@ describe Projects::TodosController do
format: 'html'
end
- it_behaves_like 'project todos actions'
+ context 'when authorized' do
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ it 'creates todo for merge request' do
+ expect do
+ go
+ end.to change { user.todos.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns todo path and pending count' do
+ go
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['count']).to eq 1
+ expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
+ end
+ end
+
+ context 'when not authorized for project' do
+ it 'does not create todo for merge request user has no access to' do
+ sign_in(user)
+ expect do
+ go
+ end.to change { user.todos.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not create todo for merge request user has no access to' do
+ expect do
+ go
+ end.to change { user.todos.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when not authorized for merge_request' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ sign_in(user)
+ end
+
+ it "doesn't create todo" do
+ expect { go }.not_to change { user.todos.count }
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
end
end
end
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index d3188f054cf..9982b49eebb 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::TreeController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/variables_controller_spec.rb b/spec/controllers/projects/variables_controller_spec.rb
index 68019743be0..9afd1f751c6 100644
--- a/spec/controllers/projects/variables_controller_spec.rb
+++ b/spec/controllers/projects/variables_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::VariablesController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET #show' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 34ed835a388..94644b1f9fd 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -166,7 +166,7 @@ describe ProjectsController do
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
before do
- user.update_attributes(project_view: project_view)
+ user.update(project_view: project_view)
get :show, namespace_id: empty_project.namespace, id: empty_project
end
@@ -188,7 +188,7 @@ describe ProjectsController do
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
before do
- user.update_attributes(project_view: project_view)
+ user.update(project_view: project_view)
get :show, namespace_id: empty_project.namespace, id: empty_project
end
@@ -759,7 +759,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when project export is enabled' do
@@ -787,26 +787,58 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
- context 'when project export is enabled' do
- it 'returns 302' do
- get :download_export, namespace_id: project.namespace, id: project
+ context 'object storage disabled' do
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
- expect(response).to have_gitlab_http_status(302)
+ context 'when project export is enabled' do
+ it 'returns 302' do
+ get :download_export, namespace_id: project.namespace, id: project
+
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when project export is disabled' do
+ before do
+ stub_application_setting(project_export_enabled?: false)
+ end
+
+ it 'returns 404' do
+ get :download_export, namespace_id: project.namespace, id: project
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
- context 'when project export is disabled' do
+ context 'object storage enabled' do
before do
- stub_application_setting(project_export_enabled?: false)
+ stub_feature_flags(import_export_object_storage: true)
end
- it 'returns 404' do
- get :download_export, namespace_id: project.namespace, id: project
+ context 'when project export is enabled' do
+ it 'returns 302' do
+ get :download_export, namespace_id: project.namespace, id: project
- expect(response).to have_gitlab_http_status(404)
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when project export is disabled' do
+ before do
+ stub_application_setting(project_export_enabled?: false)
+ end
+
+ it 'returns 404' do
+ get :download_export, namespace_id: project.namespace, id: project
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
end
end
@@ -815,7 +847,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when project export is enabled' do
@@ -843,7 +875,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when project export is enabled' do
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index eb94d395a9e..bcf289f36a9 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -269,13 +269,13 @@ describe UploadsController do
context "when the user has access to the project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "when the user is blocked" do
before do
user.block
- project.add_master(user)
+ project.add_maintainer(user)
end
it "redirects to the sign in page" do
@@ -475,13 +475,13 @@ describe UploadsController do
context "when the user has access to the project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "when the user is blocked" do
before do
user.block
- project.add_master(user)
+ project.add_maintainer(user)
end
it "redirects to the sign in page" do
diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb
index c0b9a25bfe8..3e8e2736423 100644
--- a/spec/factories/ci/build_trace_chunks.rb
+++ b/spec/factories/ci/build_trace_chunks.rb
@@ -3,5 +3,53 @@ FactoryBot.define do
build factory: :ci_build
chunk_index 0
data_store :redis
+
+ trait :redis_with_data do
+ data_store :redis
+
+ transient do
+ initial_data 'test data'
+ end
+
+ after(:create) do |build_trace_chunk, evaluator|
+ Ci::BuildTraceChunks::Redis.new.set_data(build_trace_chunk, evaluator.initial_data)
+ end
+ end
+
+ trait :redis_without_data do
+ data_store :redis
+ end
+
+ trait :database_with_data do
+ data_store :database
+
+ transient do
+ initial_data 'test data'
+ end
+
+ after(:build) do |build_trace_chunk, evaluator|
+ Ci::BuildTraceChunks::Database.new.set_data(build_trace_chunk, evaluator.initial_data)
+ end
+ end
+
+ trait :database_without_data do
+ data_store :database
+ end
+
+ trait :fog_with_data do
+ data_store :fog
+
+ transient do
+ initial_data 'test data'
+ end
+
+ after(:create) do |build_trace_chunk, evaluator|
+ Ci::BuildTraceChunks::Fog.new.set_data(build_trace_chunk, evaluator.initial_data)
+ end
+ end
+
+ trait :fog_without_data do
+ data_store :fog
+ end
end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 1c2214e9481..47036560b9d 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -7,7 +7,7 @@ FactoryBot.define do
trait(:guest) { access_level GroupMember::GUEST }
trait(:reporter) { access_level GroupMember::REPORTER }
trait(:developer) { access_level GroupMember::DEVELOPER }
- trait(:master) { access_level GroupMember::MASTER }
+ trait(:maintainer) { access_level GroupMember::MAINTAINER }
trait(:owner) { access_level GroupMember::OWNER }
trait(:access_request) { requested_at Time.now }
diff --git a/spec/factories/import_export_uploads.rb b/spec/factories/import_export_uploads.rb
new file mode 100644
index 00000000000..7750d49b1d0
--- /dev/null
+++ b/spec/factories/import_export_uploads.rb
@@ -0,0 +1,5 @@
+FactoryBot.define do
+ factory :import_export_upload do
+ project { create(:project) }
+ end
+end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 3a35bdd25de..4d4f74e101e 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -27,7 +27,7 @@ FactoryBot.define do
end
after(:create) do |issue, evaluator|
- issue.update_attributes(labels: evaluator.labels)
+ issue.update(labels: evaluator.labels)
end
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 3441ce1b8cb..f722bb9cb0d 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -117,7 +117,7 @@ FactoryBot.define do
end
after(:create) do |merge_request, evaluator|
- merge_request.update_attributes(labels: evaluator.labels)
+ merge_request.update(labels: evaluator.labels)
end
end
end
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index 4260f52498d..22a8085ea45 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -2,12 +2,12 @@ FactoryBot.define do
factory :project_member do
user
project
- master
+ maintainer
trait(:guest) { access_level ProjectMember::GUEST }
trait(:reporter) { access_level ProjectMember::REPORTER }
trait(:developer) { access_level ProjectMember::DEVELOPER }
- trait(:master) { access_level ProjectMember::MASTER }
+ trait(:maintainer) { access_level ProjectMember::MAINTAINER }
trait(:access_request) { requested_at Time.now }
trait(:invited) do
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index f6b05bac0e8..fec1bea2751 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -47,7 +47,7 @@ FactoryBot.define do
# user have access to the project. Our specs don't use said service class,
# thus we must manually refresh things here.
unless project.group || project.pending_delete
- project.add_master(project.owner)
+ project.add_maintainer(project.owner)
end
project.group&.refresh_members_authorized_projects
@@ -103,6 +103,22 @@ FactoryBot.define do
end
trait :with_export do
+ before(:create) do |_project, _evaluator|
+ allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { false }
+ allow(Feature).to receive(:enabled?).with('import_export_object_storage') { false }
+ end
+
+ after(:create) do |project, _evaluator|
+ ProjectExportWorker.new.perform(project.creator.id, project.id)
+ end
+ end
+
+ trait :with_object_export do
+ before(:create) do |_project, _evaluator|
+ allow(Feature).to receive(:enabled?).with(:import_export_object_storage) { true }
+ allow(Feature).to receive(:enabled?).with('import_export_object_storage') { true }
+ end
+
after(:create) do |project, evaluator|
ProjectExportWorker.new.perform(project.creator.id, project.id)
end
diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb
index 60956511834..5457c0d2a8f 100644
--- a/spec/factories/protected_branches.rb
+++ b/spec/factories/protected_branches.rb
@@ -39,23 +39,23 @@ FactoryBot.define do
end
end
- trait :masters_can_push do
+ trait :maintainers_can_push do
transient do
default_push_level false
end
after(:build) do |protected_branch|
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
after(:build) do |protected_branch, evaluator|
if evaluator.default_access_level && evaluator.default_push_level
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
if evaluator.default_access_level && evaluator.default_merge_level
- protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
diff --git a/spec/factories/protected_tags.rb b/spec/factories/protected_tags.rb
index df9c8b3cb63..2b81d089549 100644
--- a/spec/factories/protected_tags.rb
+++ b/spec/factories/protected_tags.rb
@@ -27,19 +27,19 @@ FactoryBot.define do
end
end
- trait :masters_can_create do
+ trait :maintainers_can_create do
transient do
default_access_level false
end
after(:build) do |protected_tag|
- protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_tag.create_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
after(:build) do |protected_tag, evaluator|
if evaluator.default_access_level
- protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
+ protected_tag.create_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 14486c80341..94f8caedfa6 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -1,8 +1,8 @@
FactoryBot.define do
factory :todo do
project
- author { project&.creator || user }
- user { project&.creator || user }
+ author { project.creator }
+ user { project.creator }
target factory: :issue
action { Todo::ASSIGNED }
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index b45f6f30e40..a81b2169b89 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -28,6 +28,13 @@ FactoryBot.define do
secret SecureRandom.hex
end
+ trait :with_file do
+ after(:create) do |upload|
+ FileUtils.mkdir_p(File.dirname(upload.absolute_path))
+ FileUtils.touch(upload.absolute_path)
+ end
+ end
+
trait :object_storage do
store ObjectStorage::Store::REMOTE
end
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 5b0a53688c2..96dfde2e08c 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -168,7 +168,7 @@ describe 'Admin Groups' do
it 'renders shared project' do
empty_project = create(:project)
empty_project.project_group_links.create!(
- group_access: Gitlab::Access::MASTER,
+ group_access: Gitlab::Access::MAINTAINER,
group: group
)
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 328e8f25f89..d6ee256f5b5 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -88,7 +88,7 @@ describe "Admin::Projects" do
describe 'add admin himself to a project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds admin a to a project as developer', :js do
@@ -110,7 +110,7 @@ describe "Admin::Projects" do
describe 'admin remove himself from a project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(current_user)
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 9c6758abe86..a852ca689e7 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -272,6 +272,16 @@ describe 'Admin updates settings' do
expect(Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services).to be true
end
+ it 'Enable hiding third party offers' do
+ page.within('.as-third-party-offers') do
+ check 'Do not display offers from third parties within GitLab'
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+ expect(Gitlab::CurrentSettings.hide_third_party_offers).to be true
+ end
+
it 'Change Slack Notifications Service template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index da7749b42d2..bd4c00d97b1 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -8,8 +8,8 @@ describe "Dashboard Issues Feed" do
let!(:project2) { create(:project) }
before do
- project1.add_master(user)
- project2.add_master(user)
+ project1.add_maintainer(user)
+ project2.add_maintainer(user)
end
describe "atom feed" do
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 462eab07a75..86b3f88298f 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -26,7 +26,7 @@ describe "Dashboard Feed" do
let(:note) { create(:note, noteable: issue, author: user, note: 'Bug confirmed', project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
issue_event(issue, user)
note_event(note, user)
visit dashboard_projects_path(:atom, feed_token: user.feed_token)
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index eeaaa40fe21..8d7df346abb 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -47,7 +47,7 @@ describe "User Feed" do
let!(:push_event_payload) { create(:push_event_payload, event: push_event) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
issue_event(issue, user)
note_event(note, user)
merge_request_event(merge_request, user)
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 7a14a441088..eebc987499d 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards add issue modal', :js do
let!(:issue2) { create(:issue, project: project, title: 'hij', description: 'klm') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index f6e0dee28c6..a0af2dea3ad 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -11,8 +11,8 @@ describe 'Issue Boards', :js do
let!(:user2) { create(:user) }
before do
- project.add_master(user)
- project.add_master(user2)
+ project.add_maintainer(user)
+ project.add_maintainer(user2)
set_cookie('sidebar_collapsed', 'true')
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 32bd7b88840..ec0ca21450a 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -13,7 +13,7 @@ describe 'Issue Boards', :js do
let!(:issue3) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label], relative_position: 1) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index be9c6a51c29..615223a2a88 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -10,7 +10,7 @@ describe 'Issue Boards add issue modal filtering', :js do
let!(:issue1) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 7a95f5cf871..0bf1ecbc433 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -8,7 +8,7 @@ describe 'Issue Boards new issue', :js do
context 'authorized user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index a03aa681827..ee38e756f9e 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -22,7 +22,7 @@ describe 'Issue Boards', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
index 271c610dcc8..de2cb4c335e 100644
--- a/spec/features/boards/sub_group_project_spec.rb
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -11,7 +11,7 @@ describe 'Sub-group project issue boards', :js do
let!(:issue) { create(:labeled_issue, project: project, labels: [label]) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 87fa3f60826..8989b2051bb 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -89,7 +89,7 @@ describe 'Commits' do
context 'Download artifacts' do
before do
- build.update_attributes(legacy_artifacts_file: artifacts_file)
+ build.update(legacy_artifacts_file: artifacts_file)
end
it do
@@ -146,7 +146,7 @@ describe 'Commits' do
context "when logged as reporter" do
before do
project.add_reporter(user)
- build.update_attributes(legacy_artifacts_file: artifacts_file)
+ build.update(legacy_artifacts_file: artifacts_file)
visit pipeline_path(pipeline)
end
@@ -168,7 +168,7 @@ describe 'Commits' do
project.update(
visibility_level: Gitlab::VisibilityLevel::INTERNAL,
public_builds: false)
- build.update_attributes(legacy_artifacts_file: artifacts_file)
+ build.update(legacy_artifacts_file: artifacts_file)
visit pipeline_path(pipeline)
end
@@ -188,7 +188,7 @@ describe 'Commits' do
let(:branch_name) { 'master' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_commits_path(project, branch_name)
end
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index dfcd7ada23b..32c75cae0a1 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -12,7 +12,7 @@ describe 'Cycle Analytics', :js do
context 'as an allowed user' do
context 'when project is new' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
@@ -39,7 +39,7 @@ describe 'Cycle Analytics', :js do
context "when there's cycle analytics data" do
before do
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
- project.add_master(user)
+ project.add_maintainer(user)
@build = create_cycle(user, project, issue, mr, milestone, pipeline)
deploy_master(user, project)
@@ -95,7 +95,7 @@ describe 'Cycle Analytics', :js do
before do
user.update_attribute(:preferred_language, 'es')
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_cycle_analytics_path(project)
wait_for_requests
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index 9ed912820f7..bf91dc121d8 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -60,7 +60,7 @@ describe 'Dashboard > Activity' do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit activity_dashboard_path
wait_for_requests
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index b36231fd78b..6a0cd848345 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe 'Dashboard Archived Project' do
let(:archived_project) { create(:project, :archived) }
before do
- project.add_master(user)
- archived_project.add_master(user)
+ project.add_maintainer(user)
+ archived_project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 28bff4d2821..d7234158fa1 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -8,7 +8,7 @@ describe 'Tooltips on .timeago dates', :js do
context 'on the activity tab' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
Event.create( project: project, author_id: user.id, action: Event::JOINED,
updated_at: created_date, created_at: created_date)
@@ -27,7 +27,7 @@ describe 'Tooltips on .timeago dates', :js do
context 'on the snippets tab' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:snippet, author: user, updated_at: created_date, created_at: created_date)
sign_in user
diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb
index 340262be502..95e2610dd4a 100644
--- a/spec/features/dashboard/issues_filter_spec.rb
+++ b/spec/features/dashboard/issues_filter_spec.rb
@@ -11,7 +11,7 @@ describe 'Dashboard Issues filtering', :js do
let!(:issue2) { create(:issue, project: project, author: user, assignees: [user], milestone: milestone) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit_issues
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 3cc7b38550d..4ae062f242a 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Dashboard Issues' do
let!(:other_issue) { create :issue, project: project }
before do
- [project, project_with_issues_disabled].each { |project| project.add_master(current_user) }
+ [project, project_with_issues_disabled].each { |project| project.add_maintainer(current_user) }
sign_in(current_user)
visit issues_dashboard_path(assignee_id: current_user.id)
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 46d7a82d468..f51142f5790 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -12,7 +12,7 @@ describe 'Dashboard Merge Requests' do
let(:forked_project) { fork_project(public_project, current_user, repository: true) }
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
sign_in(current_user)
end
@@ -20,7 +20,7 @@ describe 'Dashboard Merge Requests' do
let(:project_with_disabled_merge_requests) { create(:project, :merge_requests_disabled) }
before do
- project_with_disabled_merge_requests.add_master(current_user)
+ project_with_disabled_merge_requests.add_maintainer(current_user)
visit merge_requests_dashboard_path
end
diff --git a/spec/features/dashboard/milestone_tabs_spec.rb b/spec/features/dashboard/milestone_tabs_spec.rb
index 6fcde35f541..21de7c2f06f 100644
--- a/spec/features/dashboard/milestone_tabs_spec.rb
+++ b/spec/features/dashboard/milestone_tabs_spec.rb
@@ -14,7 +14,7 @@ describe 'Dashboard milestone tabs', :js do
let!(:merge_request) { create(:labeled_merge_request, source_project: project, target_project: project, milestone: project_milestone, labels: [label]) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index c0699a72521..0db69432702 100644
--- a/spec/features/dashboard/milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
@@ -16,7 +16,7 @@ describe 'Dashboard > Milestones' do
let(:project) { create(:project, namespace: user.namespace) }
let!(:milestone) { create(:milestone, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit dashboard_milestones_path
end
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index cfd6329fad0..498775acff3 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -5,7 +5,7 @@ describe 'Project member activity', :js do
let(:project) { create(:project, :public, name: 'x', namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
def visit_activities_and_wait_with_event(event_type)
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index 8647616101b..4daacc61d85 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -59,7 +59,7 @@ describe 'Dashboard Projects' do
context 'when last_repository_updated_at, last_activity_at and update_at are present' do
it 'shows the last_repository_updated_at attribute as the update date' do
- project.update_attributes!(last_repository_updated_at: Time.now, last_activity_at: 1.hour.ago)
+ project.update!(last_repository_updated_at: Time.now, last_activity_at: 1.hour.ago)
visit dashboard_projects_path
@@ -67,7 +67,7 @@ describe 'Dashboard Projects' do
end
it 'shows the last_activity_at attribute as the update date' do
- project.update_attributes!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.now)
+ project.update!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.now)
visit dashboard_projects_path
@@ -77,7 +77,7 @@ describe 'Dashboard Projects' do
context 'when last_repository_updated_at and last_activity_at are missing' do
it 'shows the updated_at attribute as the update date' do
- project.update_attributes!(last_repository_updated_at: nil, last_activity_at: nil)
+ project.update!(last_repository_updated_at: nil, last_activity_at: nil)
project.touch
visit dashboard_projects_path
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index 92f4d4b854c..3746d37b9cd 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -7,7 +7,7 @@ describe 'Dashboard > User filters projects' do
let(:project2) { create(:project, name: 'Treasure', namespace: user2.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
index 69d35cdbc72..7a3b1d7ed47 100644
--- a/spec/features/discussion_comments/commit_spec.rb
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -8,7 +8,7 @@ describe 'Discussion Comments Commit', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_commit_path(project, sample_commit.id)
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index 9812eaf3420..5ec19460bbd 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -6,7 +6,7 @@ describe 'Discussion Comments Issue', :js do
let(:issue) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index b0019c32189..f940e973923 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'Discussion Comments Merge Request', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index 4a236c4639b..d330e89505e 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -6,7 +6,7 @@ describe 'Discussion Comments Snippet', :js do
let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_snippet_path(project, snippet)
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index df64219de99..d7692181453 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -5,7 +5,7 @@ describe 'Global search' do
let(:project) { create(:project, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb
index 5643240377b..89e0cdd8ed7 100644
--- a/spec/features/group_variables_spec.rb
+++ b/spec/features/group_variables_spec.rb
@@ -7,7 +7,7 @@ describe 'Group variables', :js do
let(:page_path) { group_settings_ci_cd_path(group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
gitlab_sign_in(user)
visit page_path
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
index 27520cf0e22..88fc12ae1e4 100644
--- a/spec/features/groups/activity_spec.rb
+++ b/spec/features/groups/activity_spec.rb
@@ -23,7 +23,7 @@ describe 'Group activity page' do
let(:project) { create(:project, :public, namespace: group) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit path
end
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index dd901b034f7..8f5ca781b2c 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -19,7 +19,7 @@ describe 'Group empty states' do
let(:project) { create(:project, namespace: group) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "the project has #{issuable_name}s" do
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 2bab6aa3d48..97d8776b15a 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -80,10 +80,10 @@ describe 'Group issues page' do
context 'projects with issues disabled' do
describe 'issue dropdown' do
- let(:user_in_group) { create(:group_member, :master, user: create(:user), group: group ).user }
+ let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
before do
- [project, project_with_issues_disabled].each { |project| project.add_master(user_in_group) }
+ [project, project_with_issues_disabled].each { |project| project.add_maintainer(user_in_group) }
sign_in(user_in_group)
visit issues_group_path(group)
end
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index 8b4f6dbcc50..386d81546d7 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -7,7 +7,7 @@ describe 'Groups > Members > Filter members' do
before do
group.add_owner(user)
- group.add_master(user_with_2fa)
+ group.add_maintainer(user_with_2fa)
sign_in(user)
end
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
index 4fdf1497781..bd615c99412 100644
--- a/spec/features/groups/members/master_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-describe 'Groups > Members > Master manages access requests' do
- it_behaves_like 'Master manages access requests' do
+describe 'Groups > Members > Maintainer manages access requests' do
+ it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:group, :public, :access_requestable) }
let(:members_page_path) { group_group_members_path(entity) }
end
diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb
index 1c833c36a61..94510f917a3 100644
--- a/spec/features/groups/members/request_access_spec.rb
+++ b/spec/features/groups/members/request_access_spec.rb
@@ -13,7 +13,7 @@ describe 'Groups > Members > Request access' do
end
it 'request access feature is disabled' do
- group.update_attributes(request_access_enabled: false)
+ group.update(request_access_enabled: false)
visit group_path(group)
expect(page).not_to have_content 'Request Access'
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 5ab03cb6ee6..80df0618a6a 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
describe 'Group milestones' do
let(:group) { create(:group) }
let!(:project) { create(:project_empty_repo, group: group) }
- let(:user) { create(:group_member, :master, user: create(:user), group: group ).user }
+ let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
around do |example|
Timecop.freeze { example.run }
diff --git a/spec/features/groups/milestones_sorting_spec.rb b/spec/features/groups/milestones_sorting_spec.rb
index 5deb55bc8bb..bc226ff41c1 100644
--- a/spec/features/groups/milestones_sorting_spec.rb
+++ b/spec/features/groups/milestones_sorting_spec.rb
@@ -9,7 +9,7 @@ describe 'Milestones sorting', :js do
let!(:project_milestone2) { create(:milestone, project: project, title: 'v2.0', due_date: 5.days.from_now) }
let!(:other_project_milestone2) { create(:milestone, project: other_project, title: 'v2.0', due_date: 5.days.from_now) }
let!(:group_milestone) { create(:milestone, group: group, title: 'v3.0', due_date: 7.days.from_now) }
- let(:user) { create(:group_member, :master, user: create(:user), group: group ).user }
+ let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
before do
sign_in(user)
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 053e3b189c3..e62bd6f8187 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -154,6 +154,12 @@ describe 'Group' do
end
end
+ it 'focuses confirmation field on remove group' do
+ click_button('Remove group')
+
+ expect(page).to have_selector '#confirm_name_input:focus'
+ end
+
it 'removes group' do
expect { remove_with_confirm('Remove group', group.path) }.to change {Group.count}.by(-1)
expect(group.members.all.count).to be_zero
diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb
index a4d05c25a90..ea714934ae7 100644
--- a/spec/features/ics/dashboard_issues_spec.rb
+++ b/spec/features/ics/dashboard_issues_spec.rb
@@ -8,7 +8,7 @@ describe 'Dashboard Issues Calendar Feed' do
let(:milestone) { create(:milestone, project_id: project.id, title: 'v1.0') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when authenticated' do
diff --git a/spec/features/ide_spec.rb b/spec/features/ide_spec.rb
index b3f24c2966d..65989c36c1e 100644
--- a/spec/features/ide_spec.rb
+++ b/spec/features/ide_spec.rb
@@ -8,7 +8,7 @@ describe 'IDE', :js do
let(:subgroup_project) { create(:project, :repository, namespace: subgroup) }
before do
- subgroup_project.add_master(user)
+ subgroup_project.add_maintainer(user)
sign_in(user)
visit project_path(subgroup_project)
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
new file mode 100644
index 00000000000..e381d073804
--- /dev/null
+++ b/spec/features/import/manifest_import_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe 'Import multiple repositories by uploading a manifest file', :js, :postgresql do
+ include Select2Helper
+
+ let(:user) { create(:admin) }
+ let(:group) { create(:group) }
+
+ before do
+ sign_in(user)
+
+ group.add_owner(user)
+ end
+
+ it 'parses manifest file and list repositories' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
+ click_on 'List available repositories'
+
+ expect(page).to have_button('Import all repositories')
+ expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
+ end
+
+ it 'imports succesfully imports a project' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
+ click_on 'List available repositories'
+
+ page.within(first_row) do
+ click_on 'Import'
+
+ expect(page).to have_content 'Done'
+ expect(page).to have_content("#{group.full_path}/build/make")
+ end
+ end
+
+ it 'renders an error if invalid file was provided' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/banana_sample.gif'))
+ click_on 'List available repositories'
+
+ expect(page).to have_content 'The uploaded file is not a valid XML file.'
+ end
+
+ def first_row
+ page.all('table.import-jobs tbody tr')[0]
+ end
+end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index a986ddc4abc..9e1a12a9c2a 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -8,7 +8,7 @@ describe 'Invites' do
let(:group_invite) { group.group_members.invite.last }
before do
- project.add_master(owner)
+ project.add_maintainer(owner)
group.add_user(owner, Gitlab::Access::OWNER)
group.add_developer('user@example.com', owner)
group_invite.generate_invite_token!
diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb
index 3df77a104e8..de6f5fe1560 100644
--- a/spec/features/issuables/close_reopen_report_toggle_spec.rb
+++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb
@@ -46,7 +46,7 @@ describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
login_as user
end
@@ -83,7 +83,7 @@ describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
login_as user
end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 1131e1711bf..bf60b18873c 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -11,7 +11,7 @@ describe 'Awards Emoji' do
context 'authorized user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 44ddc032656..06cb2e36334 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -11,7 +11,7 @@ describe 'Issues > Labels bulk assignment' do
context 'as an allowed user', :js do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index 32c6ac52f92..ada57285abf 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -14,7 +14,7 @@ describe 'Resolving all open discussions in a merge request from an issue', :js
describe 'as a user with access to the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index b8222283a98..b20730bdb22 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -14,7 +14,7 @@ describe 'Resolve an open discussion in a merge request by creating an issue', :
describe 'As a user with access to the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index c8115db9212..d011d2545bb 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -20,9 +20,9 @@ describe 'Dropdown assignee', :js do
end
before do
- project.add_master(user)
- project.add_master(user_john)
- project.add_master(user_jacob)
+ project.add_maintainer(user)
+ project.add_maintainer(user_john)
+ project.add_maintainer(user_jacob)
sign_in(user)
create(:issue, project: project)
@@ -224,7 +224,7 @@ describe 'Dropdown assignee', :js do
expect(initial_size).to be > 0
new_user = create(:user)
- project.add_master(new_user)
+ project.add_maintainer(new_user)
find('.filtered-search-box .clear-search').click
input_filtered_search('assignee:', submit: false, extra_space: false)
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 70b4f11410d..50d819a6161 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -28,9 +28,9 @@ describe 'Dropdown author', :js do
end
before do
- project.add_master(user)
- project.add_master(user_john)
- project.add_master(user_jacob)
+ project.add_maintainer(user)
+ project.add_maintainer(user_john)
+ project.add_maintainer(user_jacob)
sign_in(user)
create(:issue, project: project)
@@ -195,7 +195,7 @@ describe 'Dropdown author', :js do
expect(initial_size).to be > 0
new_user = create(:user)
- project.add_master(new_user)
+ project.add_maintainer(new_user)
find('.filtered-search-box .clear-search').click
filtered_search.set('author')
send_keys_to_filtered_search(':')
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index 436625a6f7b..be229e8aa7d 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -28,7 +28,7 @@ describe 'Dropdown emoji', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup')
create_list(:award_emoji, 1, user: user, name: 'thumbsdown')
create_list(:award_emoji, 3, user: user, name: 'star')
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index ef40dddfd3a..b99c5a7f4e3 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -13,7 +13,7 @@ describe 'Dropdown hint', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:issue, project: project)
end
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index 18cdb199c70..ca5d506ab04 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -33,7 +33,7 @@ describe 'Dropdown label', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 94710c2f71f..f76d30056da 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -29,7 +29,7 @@ describe 'Dropdown milestone', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 8dca81a8627..d4949de3f27 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -24,7 +24,7 @@ describe 'Filter issues', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:issue, project: project, author: user2, title: "Bug report 1")
create(:issue, project: project, author: user2, title: "Bug report 2")
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 268590da599..8abab3f35d6 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -8,7 +8,7 @@ describe 'Search bar', :js do
let(:filtered_search) { find('.filtered-search') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index 0ae70c855db..6ac7ccd00f7 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -22,8 +22,8 @@ describe 'Visual tokens', :js do
end
before do
- project.add_user(user, :master)
- project.add_user(user_rock, :master)
+ project.add_user(user, :maintainer)
+ project.add_user(user_rock, :maintainer)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 2cb3ae08b0e..1456a2f0375 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -13,8 +13,8 @@ describe 'New/edit issue', :js do
let!(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
before do
- project.add_master(user)
- project.add_master(user2)
+ project.add_maintainer(user)
+ project.add_maintainer(user2)
sign_in(user)
end
@@ -321,7 +321,7 @@ describe 'New/edit issue', :js do
let(:sub_group_project) { create(:project, group: nested_group_1) }
before do
- sub_group_project.add_master(user)
+ sub_group_project.add_maintainer(user)
visit new_project_issue_path(sub_group_project)
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index a330ba4c8b3..98e37d8011a 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -7,7 +7,7 @@ describe 'GFM autocomplete', :js do
let(:issue) { create(:issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index b4cb3835a13..3050f23c130 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -112,7 +112,7 @@ describe 'Issue Sidebar' do
context 'editing issue labels', :js do
before do
- issue.update_attributes(labels: [label])
+ issue.update(labels: [label])
page.within('.block.labels') do
find('.edit-link').click
end
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 73022afbda2..7cce45ff206 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -17,7 +17,7 @@ describe 'New issue', :js do
recaptcha_private_key: 'test private key'
)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index d23f9059d0f..0114178b9be 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -6,7 +6,7 @@ describe 'Manually create a todo item from issue', :js do
let!(:user) { create(:user)}
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index cd6a5977eb8..845a7c5fc42 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -6,7 +6,7 @@ describe 'Multiple issue updating from issues#index', :js do
let!(:user) { create(:user)}
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index a28378b22ca..5926e442f24 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -12,7 +12,7 @@ describe 'Issues > User uses quick actions', :js do
let(:project) { create(:project, :public) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
end
@@ -196,7 +196,7 @@ describe 'Issues > User uses quick actions', :js do
let(:target_project) { create(:project, :public) }
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
gitlab_sign_out
sign_in(user)
visit project_issue_path(project, issue)
@@ -258,7 +258,7 @@ describe 'Issues > User uses quick actions', :js do
let(:wontfix_target) { create(:label, project: target_project, title: 'wontfix') }
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
gitlab_sign_out
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index c6dcd97631d..4d9b8a10e04 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -398,7 +398,7 @@ describe 'Issues' do
before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
- project1.add_master(user)
+ project1.add_maintainer(user)
visit namespace_project_issues_path(user.namespace, project1)
end
diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
index 3c2186b3598..6997ca48427 100644
--- a/spec/features/markdown/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
@@ -6,7 +6,7 @@ describe "GitLab Flavored Markdown" do
let(:issue) { create(:issue, project: project) }
let(:fred) do
create(:user, name: 'fred') do |user|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 1808d0c0a0c..7839b97122c 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -18,7 +18,7 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js
end
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(target_project, merge_request)
diff --git a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
index 0af37d76539..0ccab5b2fac 100644
--- a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
+++ b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb
@@ -71,7 +71,7 @@ describe 'create a merge request, allowing commits from members who can merge to
end
before do
- target_project.add_master(member)
+ target_project.add_maintainer(member)
sign_in(member)
end
diff --git a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
index e1e70b6d260..8d2d4279d3c 100644
--- a/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
+++ b/spec/features/merge_request/user_customizes_merge_commit_message_spec.rb
@@ -32,7 +32,7 @@ describe 'Merge request < User customizes merge commit message', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_merges_immediately_spec.rb b/spec/features/merge_request/user_merges_immediately_spec.rb
index b16fc9bfc89..ea61f9675bc 100644
--- a/spec/features/merge_request/user_merges_immediately_spec.rb
+++ b/spec/features/merge_request/user_merges_immediately_spec.rb
@@ -19,7 +19,7 @@ describe 'Merge requests > User merges immediately', :js do
context 'when there is active pipeline for merge request' do
before do
create(:ci_build, pipeline: pipeline)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
index a045791f6b4..8372b61f872 100644
--- a/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb
@@ -5,7 +5,7 @@ describe 'Merge request > User merges only if pipeline succeeds', :js do
let(:project) { merge_request.target_project }
before do
- project.add_master(merge_request.author)
+ project.add_maintainer(merge_request.author)
sign_in(merge_request.author)
end
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index db92a3504f3..53ed5d78598 100644
--- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -17,7 +17,7 @@ describe 'Merge request > User merges when pipeline succeeds', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when there is active pipeline for merge request' do
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index 13cc5f256eb..02d19db3828 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -198,7 +198,7 @@ describe 'Merge request > User posts diff notes', :js do
context 'when the MR only supports legacy diff notes' do
before do
- merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
+ merge_request.merge_request_diff.update(start_commit_sha: nil)
visit diffs_project_merge_request_path(project, merge_request, view: 'inline')
end
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index fa819cbc385..260c5f9c28b 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -14,7 +14,7 @@ describe 'Merge request > User posts notes', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index a0b9d6cb059..bf4d5396df9 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -19,7 +19,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
context 'no discussions' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
note.destroy
visit_merge_request
@@ -33,7 +33,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
context 'as authorized user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit_merge_request
end
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index 0a8296bd722..428eb414274 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -19,7 +19,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
set_cookie('sidebar_collapsed', 'true')
diff --git a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
index c40c720d168..86086a58f18 100644
--- a/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
+++ b/spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe 'Merge request > User sees Check out branch modal', :js do
+describe 'Merge request > User sees check out branch modal', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -16,7 +16,7 @@ describe 'Merge request > User sees Check out branch modal', :js do
expect(page).to have_content('Check out, review, and merge locally')
end
- it 'closes the check out branch model with Escape keypress' do
+ it 'closes the check out branch modal with escape keypress' do
find('#modal_merge_info').send_keys(:escape)
expect(page).not_to have_content('Check out, review, and merge locally')
diff --git a/spec/features/merge_request/user_cherry_picks_spec.rb b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
index 61d1bdaa95a..aa499493dbe 100644
--- a/spec/features/merge_request/user_cherry_picks_spec.rb
+++ b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
@@ -7,7 +7,7 @@ describe 'Merge request > User cherry-picks', :js do
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -21,7 +21,7 @@ describe 'Merge request > User cherry-picks', :js do
end
# Fast-forward merge, or merged before GitLab 8.5.
- context 'Without a merge commit' do
+ context 'without a merge commit' do
before do
merge_request.merge_commit_sha = nil
merge_request.save
@@ -34,7 +34,7 @@ describe 'Merge request > User cherry-picks', :js do
end
end
- context 'With a merge commit' do
+ context 'with a merge commit' do
it 'shows a Cherry-pick button' do
visit project_merge_request_path(project, merge_request)
@@ -49,5 +49,23 @@ describe 'Merge request > User cherry-picks', :js do
expect(page).not_to have_link 'Cherry-pick'
end
end
+
+ context 'and seeing the cherry-pick modal' do
+ before do
+ visit project_merge_request_path(project, merge_request)
+
+ click_link('Cherry-pick')
+ end
+
+ it 'shows the cherry-pick modal' do
+ expect(page).to have_content('Cherry-pick this merge request')
+ end
+
+ it 'closes the cherry-pick modal with escape keypress' do
+ find('#modal-cherry-pick-commit').send_keys(:escape)
+
+ expect(page).not_to have_content('Start a new merge request with these changes')
+ end
+ end
end
end
diff --git a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
index 726f35557a7..d7c784b14c5 100644
--- a/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
+++ b/spec/features/merge_request/user_sees_closing_issues_message_spec.rb
@@ -18,7 +18,7 @@ describe 'Merge request > User sees closing issues message', :js do
let(:merge_request_title) { 'Merge Request Title' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
wait_for_requests
diff --git a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
index 01115318370..46c21a2b155 100644
--- a/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_deleted_target_branch_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User sees deleted target branch', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
DeleteBranchService.new(project, user).execute('feature')
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/merge_request/user_sees_discussions_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb
index 10390bd5864..7b8c3bacfe2 100644
--- a/spec/features/merge_request/user_sees_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_discussions_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User sees discussions', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_empty_state_spec.rb b/spec/features/merge_request/user_sees_empty_state_spec.rb
index a939c7e9001..482f31b02d4 100644
--- a/spec/features/merge_request/user_sees_empty_state_spec.rb
+++ b/spec/features/merge_request/user_sees_empty_state_spec.rb
@@ -5,7 +5,7 @@ describe 'Merge request > User sees empty state' do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
index 85df43df38e..f6b771facf8 100644
--- a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User sees merge button depending on unresolved discuss
let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 51a65407aec..b6b3844f2ae 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -10,8 +10,8 @@ describe 'Merge request > User sees merge widget', :js do
let(:merge_request_in_only_mwps_project) { create(:merge_request, source_project: project_only_mwps) }
before do
- project.add_master(user)
- project_only_mwps.add_master(user)
+ project.add_maintainer(user)
+ project_only_mwps.add_maintainer(user)
sign_in(user)
end
@@ -275,7 +275,7 @@ describe 'Merge request > User sees merge widget', :js do
let(:user2) { create(:user) }
before do
- project.add_master(user2)
+ project.add_maintainer(user2)
sign_out(:user)
sign_in(user2)
merge_request.update(target_project: fork_project)
diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb
index a42c016392b..45cccbee63e 100644
--- a/spec/features/merge_request/user_sees_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_pipelines_spec.rb
@@ -7,7 +7,7 @@ describe 'Merge request > User sees pipelines', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -70,7 +70,7 @@ describe 'Merge request > User sees pipelines', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 11e0806ba62..f42b4dcbb47 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -10,7 +10,7 @@ describe 'Merge request > User sees versions', :js do
let!(:params) { {} }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request, params)
end
diff --git a/spec/features/merge_request/user_sees_wip_help_message_spec.rb b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
index bc25243244e..92cc73ddf1f 100644
--- a/spec/features/merge_request/user_sees_wip_help_message_spec.rb
+++ b/spec/features/merge_request/user_sees_wip_help_message_spec.rb
@@ -5,7 +5,7 @@ describe 'Merge request > User sees WIP help message' do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index ed6e29335d1..ae41cf90576 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -11,7 +11,7 @@ describe 'Merge request > User selects branches for new MR', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
index 2e95a628013..dd860382daa 100644
--- a/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
+++ b/spec/features/merge_request/user_toggles_whitespace_changes_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge request > User toggles whitespace changes', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_uses_slash_commands_spec.rb b/spec/features/merge_request/user_uses_slash_commands_spec.rb
index 83ad4b45b5a..b81478a481f 100644
--- a/spec/features/merge_request/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_request/user_uses_slash_commands_spec.rb
@@ -21,7 +21,7 @@ describe 'Merge request > User uses quick actions', :js do
let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'time tracking' do
@@ -147,7 +147,7 @@ describe 'Merge request > User uses quick actions', :js do
let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } }
before do
- another_project.add_master(user)
+ another_project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index 199ba7e87ad..bb327159cb0 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -6,7 +6,7 @@ describe 'Merge requests > User mass updates', :js do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/user_squashes_merge_request_spec.rb b/spec/features/merge_requests/user_squashes_merge_request_spec.rb
index da3d6772eeb..ec1153b7f7f 100644
--- a/spec/features/merge_requests/user_squashes_merge_request_spec.rb
+++ b/spec/features/merge_requests/user_squashes_merge_request_spec.rb
@@ -46,7 +46,7 @@ describe 'User squashes a merge request', :js do
# Prevent source branch from being removed so we can use be_merged_to_root_ref
# method to check if squash was performed or not
allow_any_instance_of(MergeRequest).to receive(:force_remove_source_branch?).and_return(false)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index b12aba2c263..a0673b12738 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -7,7 +7,7 @@ describe 'Milestone' do
before do
create(:group_member, group: group, user: user)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/milestones/user_edits_milestone_spec.rb b/spec/features/milestones/user_edits_milestone_spec.rb
new file mode 100644
index 00000000000..077295f1cc0
--- /dev/null
+++ b/spec/features/milestones/user_edits_milestone_spec.rb
@@ -0,0 +1,22 @@
+require "rails_helper"
+
+describe "User edits milestone", :js do
+ set(:user) { create(:user) }
+ set(:project) { create(:project) }
+ set(:milestone) { create(:milestone, project: project, start_date: Date.today, due_date: 5.days.from_now) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ visit(edit_project_milestone_path(project, milestone))
+ end
+
+ it "shows the right start date and due date" do
+ start_date = milestone.start_date.strftime("%F")
+ due_date = milestone.due_date.strftime("%F")
+
+ expect(page).to have_field(with: start_date)
+ expect(page).to have_field(with: due_date)
+ end
+end
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index bfb17a56613..e6586fc8a0a 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -30,6 +30,20 @@ describe 'Profile > SSH Keys' do
expect(find('.breadcrumbs-sub-title')).to have_link(attrs[:title])
end
+ it 'shows a confirmable warning if the key does not start with ssh-' do
+ attrs = attributes_for(:key)
+
+ fill_in('Key', with: 'invalid-key')
+ fill_in('Title', with: attrs[:title])
+ click_button('Add key')
+
+ expect(page).to have_selector('.js-add-ssh-key-validation-warning')
+
+ find('.js-add-ssh-key-validation-confirm-submit').click
+
+ expect(page).to have_content('Key is invalid')
+ end
+
context 'when only DSA and ECDSA keys are allowed' do
before do
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index f9c6ff90ca1..5e3569e4beb 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -117,7 +117,7 @@ describe 'Profile > Password' do
before do
sign_in(user)
- user.update_attributes(password_expires_at: 1.hour.ago)
+ user.update(password_expires_at: 1.hour.ago)
user.identities.delete
expect(user.ldap_user?).to eq false
end
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 689196c2258..db797bb586f 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -5,7 +5,7 @@ describe 'User visits the notifications tab', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(profile_notifications_path)
end
diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb
index 713112477c8..2dc4547b2d8 100644
--- a/spec/features/profiles/user_visits_profile_spec.rb
+++ b/spec/features/profiles/user_visits_profile_spec.rb
@@ -29,7 +29,7 @@ describe 'User visits their profile' do
let!(:project) do
create(:project, :repository, namespace: group) do |project|
create(:closed_issue_event, project: project)
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 0ba2224359a..a93df3696d2 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -8,7 +8,7 @@ describe 'Project variables', :js do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
project.variables << variable
visit page_path
diff --git a/spec/features/projects/actve_tabs_spec.rb b/spec/features/projects/actve_tabs_spec.rb
index ce5606b63ae..7c6110c533b 100644
--- a/spec/features/projects/actve_tabs_spec.rb
+++ b/spec/features/projects/actve_tabs_spec.rb
@@ -5,7 +5,7 @@ describe 'Project active tab' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb b/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
index 12e07647ecd..4d860893abe 100644
--- a/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
+++ b/spec/features/projects/awards/user_interacts_with_awards_in_issue_spec.rb
@@ -6,7 +6,7 @@ describe 'User interacts with awards in an issue', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_issue_path(project, issue))
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index da87039ad44..e30b908c60d 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -4,7 +4,7 @@ describe 'list of badges' do
before do
user = create(:user)
project = create(:project, :repository)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_settings_ci_cd_path(project)
end
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 7280d421cea..27589428896 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -144,7 +144,7 @@ describe 'File blob', :js do
context 'Markdown file (stored in LFS)' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -237,7 +237,7 @@ describe 'File blob', :js do
context 'PDF file' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -350,7 +350,7 @@ describe 'File blob', :js do
context 'empty file' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -418,7 +418,7 @@ describe 'File blob', :js do
context '.gitlab-ci.yml' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -446,7 +446,7 @@ describe 'File blob', :js do
context '.gitlab/route-map.yml' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
@@ -494,7 +494,7 @@ describe 'File blob', :js do
context '*.gemspec' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
Files::CreateService.new(
project,
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 2657f5d999f..0e036b4ea68 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -134,11 +134,11 @@ describe 'Editing file blob', :js do
end
end
- context 'as master' do
+ context 'as maintainer' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_edit_blob_path(project, tree_join(branch, file_path))
end
diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
index 0b7988f6335..8a0b92190dd 100644
--- a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
+++ b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
@@ -24,9 +24,9 @@ describe 'User creates blob in new project', :js do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'creating a file'
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
index 0be434a567b..0faf73db7da 100644
--- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -6,7 +6,7 @@ describe 'New Branch Ref Dropdown', :js do
let(:toggle) { find('.create-from .dropdown-menu-toggle') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit new_project_branch_path(project)
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index b7ce1b9993a..97757e8da92 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -182,10 +182,10 @@ describe 'Branches' do
end
end
- context 'logged in as master' do
+ context 'logged in as maintainer' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'Initial branches page' do
diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb
index f57647a348f..a65ca662350 100644
--- a/spec/features/projects/clusters/applications_spec.rb
+++ b/spec/features/projects/clusters/applications_spec.rb
@@ -7,7 +7,7 @@ describe 'Clusters Applications', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index bd8cb9b4b94..31e3ebf675d 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -7,7 +7,7 @@ describe 'Gcp Cluster', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
end
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index a49dd72a91f..babf47cc341 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -7,7 +7,7 @@ describe 'User Cluster', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
allow(Projects::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 }
end
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index a7274c99704..91eac9c8278 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -7,7 +7,7 @@ describe 'Clusters', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
end
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index da0552441fe..bd254caddfb 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -5,7 +5,7 @@ describe 'project commit pipelines', :js do
before do
user = create(:user)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 1df45865d6f..bc3c00dafe2 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -9,7 +9,7 @@ describe 'Cherry-pick Commits' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
visit project_commit_path(project, master_pickable_commit.id)
end
diff --git a/spec/features/projects/commit/comments/user_adds_comment_spec.rb b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
index 53866c32c69..6397df086a7 100644
--- a/spec/features/projects/commit/comments/user_adds_comment_spec.rb
+++ b/spec/features/projects/commit/comments/user_adds_comment_spec.rb
@@ -62,7 +62,7 @@ describe "User adds a comment on a commit", :js do
click_diff_line(sample_commit.line_code)
expect(page).to have_css(".js-temp-notes-holder form.new-note")
- .and have_css(".js-close-discussion-note-form", text: "Discard draft")
+ .and have_css(".js-close-discussion-note-form", text: "Cancel")
# The `Cancel` button closes the current form. The page should not have any open forms after that.
find(".js-close-discussion-note-form").click
diff --git a/spec/features/projects/commit/diff_notes_spec.rb b/spec/features/projects/commit/diff_notes_spec.rb
index 6d66889761f..e2aefa35fad 100644
--- a/spec/features/projects/commit/diff_notes_spec.rb
+++ b/spec/features/projects/commit/diff_notes_spec.rb
@@ -7,7 +7,7 @@ describe 'Commit diff', :js do
let(:project) { create(:project, :public, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 35ed6620548..23d8d606790 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -7,7 +7,7 @@ describe 'User browses commits' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 7e863d9df32..69600884909 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -5,7 +5,7 @@ describe "Compare", :js do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 1552a3512dd..e12532e97fa 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -5,7 +5,7 @@ describe 'Project deploy keys', :js do
let(:project) { create(:project_empty_repo) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index 237157cd89d..df05625d105 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -24,7 +24,7 @@ describe 'Diff file viewer', :js do
context 'Ruby file (stored in LFS)' do
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
@commit_id = Files::CreateService.new(
project,
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index 0c34309c1f4..4c5dda29fee 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -102,8 +102,8 @@ describe 'Environment' do
context 'with terminal' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
- context 'for project master' do
- let(:role) { :master }
+ context 'for project maintainer' do
+ let(:role) { :maintainer }
it 'it shows the terminal button' do
expect(page).to have_terminal_button
@@ -166,7 +166,8 @@ describe 'Environment' do
end
it 'allows to stop environment' do
- click_link('Stop')
+ click_button('Stop')
+ click_button('Stop environment') # confirm modal
expect(page).to have_content('close_app')
end
@@ -174,7 +175,7 @@ describe 'Environment' do
context 'when user has no ability to stop environment' do
it 'does not allow to stop environment' do
- expect(page).to have_no_link('Stop')
+ expect(page).not_to have_button('Stop')
end
end
@@ -182,7 +183,7 @@ describe 'Environment' do
let(:role) { :reporter }
it 'does not show stop button' do
- expect(page).not_to have_link('Stop')
+ expect(page).not_to have_button('Stop')
end
end
end
@@ -192,7 +193,7 @@ describe 'Environment' do
let(:environment) { create(:environment, project: project, state: :stopped) }
it 'does not show stop button' do
- expect(page).not_to have_link('Stop')
+ expect(page).not_to have_button('Stop')
end
end
end
@@ -230,7 +231,7 @@ describe 'Environment' do
it 'user visits environment page' do
visit_environment(environment)
- expect(page).to have_link('Stop')
+ expect(page).to have_button('Stop')
end
it 'user deletes the branch with running environment' do
@@ -242,7 +243,7 @@ describe 'Environment' do
visit_environment(environment)
- expect(page).to have_no_link('Stop')
+ expect(page).not_to have_button('Stop')
end
##
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 9900c13095e..f0890018286 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -10,6 +10,10 @@ describe 'Environments page', :js do
sign_in(user)
end
+ def stop_button_selector
+ %q{button[data-original-title="Stop environment"]}
+ end
+
describe 'page tabs' do
it 'shows "Available" and "Stopped" tab with links' do
visit_environments(project)
@@ -120,7 +124,7 @@ describe 'Environments page', :js do
end
it 'does not show stip button when environment is not stoppable' do
- expect(page).not_to have_selector('.stop-env-link')
+ expect(page).not_to have_selector(stop_button_selector)
end
end
@@ -178,7 +182,7 @@ describe 'Environments page', :js do
end
it 'shows a stop button' do
- expect(page).not_to have_selector('.stop-env-link')
+ expect(page).not_to have_selector(stop_button_selector)
end
it 'does not show external link button' do
@@ -211,22 +215,22 @@ describe 'Environments page', :js do
end
it 'shows a stop button' do
- expect(page).to have_selector('.stop-env-link')
+ expect(page).to have_selector(stop_button_selector)
end
context 'when user is a reporter' do
let(:role) { :reporter }
it 'does not show stop button' do
- expect(page).not_to have_selector('.stop-env-link')
+ expect(page).not_to have_selector(stop_button_selector)
end
end
end
context 'when kubernetes terminal is available' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
- context 'for project master' do
- let(:role) { :master }
+ context 'for project maintainer' do
+ let(:role) { :maintainer }
it 'shows the terminal button' do
expect(page).to have_terminal_button
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index b0eb7c5b42a..ab16fdee883 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -8,7 +8,7 @@ describe 'Edit Project Settings' do
describe 'project features visibility selectors', :js do
before do
- project.add_master(member)
+ project.add_maintainer(member)
sign_in(member)
end
@@ -165,7 +165,7 @@ describe 'Edit Project Settings' do
describe 'repository visibility', :js do
before do
- project.add_master(member)
+ project.add_maintainer(member)
sign_in(member)
visit edit_project_path(project)
end
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index b410199fd1f..ac6c8c337fa 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -2,16 +2,16 @@ require 'spec_helper'
describe 'Projects > Files > Project owner creates a license file', :js do
let(:project) { create(:project, :repository) }
- let(:project_master) { project.owner }
+ let(:project_maintainer) { project.owner }
before do
- project.repository.delete_file(project_master, 'LICENSE',
+ project.repository.delete_file(project_maintainer, 'LICENSE',
message: 'Remove LICENSE', branch_name: 'master')
- sign_in(project_master)
+ sign_in(project_maintainer)
visit project_path(project)
end
- it 'project master creates a license file manually from a template' do
+ it 'project maintainer creates a license file manually from a template' do
visit project_tree_path(project, project.repository.root_ref)
find('.add-to-tree').click
click_link 'New file'
@@ -35,7 +35,7 @@ describe 'Projects > Files > Project owner creates a license file', :js do
expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
- it 'project master creates a license file from the "Add license" link' do
+ it 'project maintainer creates a license file from the "Add license" link' do
click_link 'Add License'
expect(page).to have_content('New file')
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 53d8ace7c94..801291c1f77 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -2,13 +2,13 @@ require 'spec_helper'
describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js do
let(:project) { create(:project_empty_repo) }
- let(:project_master) { project.owner }
+ let(:project_maintainer) { project.owner }
before do
- sign_in(project_master)
+ sign_in(project_maintainer)
end
- it 'project master creates a license file from a template' do
+ it 'project maintainer creates a license file from a template' do
visit project_path(project)
click_on 'Add License'
expect(page).to have_content('New file')
diff --git a/spec/features/projects/files/template_selector_menu_spec.rb b/spec/features/projects/files/template_selector_menu_spec.rb
index b7e1e172af9..6b313824acd 100644
--- a/spec/features/projects/files/template_selector_menu_spec.rb
+++ b/spec/features/projects/files/template_selector_menu_spec.rb
@@ -5,7 +5,7 @@ describe 'Template selector menu', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 41f6c52fb8a..f56174fc85c 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -147,11 +147,8 @@ describe "User browses files" do
page.within(".tree-table") do
click_link("README.md")
end
-
- # rubocop:disable Lint/Void
# Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
find("a", text: /^empty$/)["href"] == project_blob_url(project, "markdown/d/README.md")
- # rubocop:enable Lint/Void
end
it "shows correct content of directory" do
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index 208cc8d81f7..d4dda43c823 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -12,7 +12,7 @@ describe 'Projects > Files > User creates files' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/files/user_deletes_files_spec.rb b/spec/features/projects/files/user_deletes_files_spec.rb
index 36d3e001a64..0e9f83a16ce 100644
--- a/spec/features/projects/files/user_deletes_files_spec.rb
+++ b/spec/features/projects/files/user_deletes_files_spec.rb
@@ -17,7 +17,7 @@ describe 'Projects > Files > User deletes files' do
context 'when an user has write access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit(project_tree_path_root_ref)
end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index dc6e4fd27cb..ccc1bc1bc10 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -31,7 +31,7 @@ describe 'Projects > Files > User edits files' do
context 'when an user has write access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit(project_tree_path_root_ref)
end
diff --git a/spec/features/projects/files/user_find_file_spec.rb b/spec/features/projects/files/user_find_file_spec.rb
index df405e70dd4..e2d881b34d2 100644
--- a/spec/features/projects/files/user_find_file_spec.rb
+++ b/spec/features/projects/files/user_find_file_spec.rb
@@ -6,7 +6,7 @@ describe 'User find project file' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
visit project_tree_path(project, project.repository.root_ref)
end
diff --git a/spec/features/projects/files/user_reads_pipeline_status_spec.rb b/spec/features/projects/files/user_reads_pipeline_status_spec.rb
index 2d0b447913e..ff0aa933a3e 100644
--- a/spec/features/projects/files/user_reads_pipeline_status_spec.rb
+++ b/spec/features/projects/files/user_reads_pipeline_status_spec.rb
@@ -7,7 +7,7 @@ describe 'user reads pipeline status', :js do
let(:x110_pipeline) { create_pipeline('x1.1.0', 'failed') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.repository.add_tag(user, 'x1.1.0', 'v1.1.0')
v110_pipeline
diff --git a/spec/features/projects/files/user_replaces_files_spec.rb b/spec/features/projects/files/user_replaces_files_spec.rb
index 9ac3417b671..3a81e77c4ba 100644
--- a/spec/features/projects/files/user_replaces_files_spec.rb
+++ b/spec/features/projects/files/user_replaces_files_spec.rb
@@ -19,7 +19,7 @@ describe 'Projects > Files > User replaces files' do
context 'when an user has write access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
visit(project_tree_path_root_ref)
end
diff --git a/spec/features/projects/files/user_uploads_files_spec.rb b/spec/features/projects/files/user_uploads_files_spec.rb
index 8b212faa29d..af3fc528a20 100644
--- a/spec/features/projects/files/user_uploads_files_spec.rb
+++ b/spec/features/projects/files/user_uploads_files_spec.rb
@@ -14,7 +14,7 @@ describe 'Projects > Files > User uploads files' do
let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index 1743b1e083f..cd5fef8238e 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -129,11 +129,11 @@ describe 'Project fork' do
end
end
- context 'master in group' do
+ context 'maintainer in group' do
let(:group) { create(:group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'allows user to fork project to group or to user namespace' do
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
index 335174b7729..9665f1755d6 100644
--- a/spec/features/projects/graph_spec.rb
+++ b/spec/features/projects/graph_spec.rb
@@ -6,7 +6,7 @@ describe 'Project Graph', :js do
let(:branch_name) { 'master' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/hook_logs/user_reads_log_spec.rb b/spec/features/projects/hook_logs/user_reads_log_spec.rb
index c3bc35565f6..086cd4b9f03 100644
--- a/spec/features/projects/hook_logs/user_reads_log_spec.rb
+++ b/spec/features/projects/hook_logs/user_reads_log_spec.rb
@@ -6,7 +6,7 @@ describe 'Hook logs' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index 8a418356541..eb281cd2122 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -25,6 +25,7 @@ describe 'Import/Export - project export integration test', :js do
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb
index 7d056b0c140..9bb8a2063b5 100644
--- a/spec/features/projects/import_export/namespace_export_file_spec.rb
+++ b/spec/features/projects/import_export/namespace_export_file_spec.rb
@@ -5,6 +5,7 @@ describe 'Import/Export - Namespace export file cleanup', :js do
before do
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index ceba4dfec57..3b5df47e0b6 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 9cd4af2de80..a57edc394f9 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -8,7 +8,7 @@ describe 'issuable templates', :js do
let(:issue_form_location) { '#content-body .issuable-details .detail-page-description' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index e9588daf37d..e639f0cf82e 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -90,7 +90,7 @@ describe 'Project Jobs Permissions' do
before do
archive = fixture_file_upload('spec/fixtures/ci_build_artifacts.zip')
- job.update_attributes(legacy_artifacts_file: archive)
+ job.update(legacy_artifacts_file: archive)
end
context 'when public access for jobs is disabled' do
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index ce0b38b7239..50e957bf12b 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -8,7 +8,7 @@ describe 'User browses a job', :js do
let!(:build) { create(:ci_build, :success, :trace_artifact, :coverage, pipeline: pipeline) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.enable_ci
sign_in(user)
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 786ec327b92..08786fe1630 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -7,7 +7,7 @@ describe 'User browses jobs' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.enable_ci
project.update_attribute(:build_coverage_regex, /Coverage (\d+)%/)
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index c742eb79392..35b1c46ecf6 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -187,7 +187,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
context "Download artifacts" do
before do
- job.update_attributes(legacy_artifacts_file: artifacts_file)
+ job.update(legacy_artifacts_file: artifacts_file)
visit project_job_path(project, job)
end
@@ -198,8 +198,8 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
context 'Artifacts expire date' do
before do
- job.update_attributes(legacy_artifacts_file: artifacts_file,
- artifacts_expire_at: expire_at)
+ job.update(legacy_artifacts_file: artifacts_file,
+ artifacts_expire_at: expire_at)
visit project_job_path(project, job)
end
@@ -530,14 +530,14 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
describe "GET /:project/jobs/:id/download" do
before do
- job.update_attributes(legacy_artifacts_file: artifacts_file)
+ job.update(legacy_artifacts_file: artifacts_file)
visit project_job_path(project, job)
click_link 'Download'
end
context "Build from other project" do
before do
- job2.update_attributes(legacy_artifacts_file: artifacts_file)
+ job2.update(legacy_artifacts_file: artifacts_file)
visit download_project_job_artifacts_path(project, job2)
end
diff --git a/spec/features/projects/labels/user_creates_labels_spec.rb b/spec/features/projects/labels/user_creates_labels_spec.rb
index 9fd7f3ee775..c71b04fea09 100644
--- a/spec/features/projects/labels/user_creates_labels_spec.rb
+++ b/spec/features/projects/labels/user_creates_labels_spec.rb
@@ -18,7 +18,7 @@ describe "User creates labels" do
context "in project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(new_project_label_path(project))
@@ -69,7 +69,7 @@ describe "User creates labels" do
before do
create(:label, project: project, title: "bug") # Create label for `project` (not `another_project`) project.
- another_project.add_master(user)
+ another_project.add_maintainer(user)
sign_in(user)
visit(new_project_label_path(another_project))
diff --git a/spec/features/projects/labels/user_edits_labels_spec.rb b/spec/features/projects/labels/user_edits_labels_spec.rb
index d1041ff5c1e..0708bbd40ce 100644
--- a/spec/features/projects/labels/user_edits_labels_spec.rb
+++ b/spec/features/projects/labels/user_edits_labels_spec.rb
@@ -6,7 +6,7 @@ describe "User edits labels" do
set(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(edit_project_label_path(project, label))
diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb
index efa74015c6e..b0ce03a1c31 100644
--- a/spec/features/projects/labels/user_removes_labels_spec.rb
+++ b/spec/features/projects/labels/user_removes_labels_spec.rb
@@ -5,7 +5,7 @@ describe "User removes labels" do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
index 19e52294a38..b3ed725f602 100644
--- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb
+++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Members > Anonymous user sees members' do
let(:project) { create(:project, :public) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:project_group_link, project: project, group: group)
end
diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
index 7bc53345ddf..bb475ea95e5 100644
--- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
@@ -12,8 +12,8 @@ describe 'Projects > Members > Group member cannot request access to his group p
expect(page).not_to have_content 'Request Access'
end
- it 'master does not see the request access button' do
- group.add_master(user)
+ it 'maintainer does not see the request access button' do
+ group.add_maintainer(user)
login_and_visit_project_page(user)
expect(page).not_to have_content 'Request Access'
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index b65c46b345f..c0b5d943e96 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Members > Groups with access list', :js do
let(:project) { create(:project, :public) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
@group_link = create(:project_group_link, project: project, group: group)
sign_in(user)
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 90f09bf6264..26de6fb33fd 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -1,16 +1,16 @@
require 'spec_helper'
-describe 'Projects > Members > Master adds member with expiration date', :js do
+describe 'Projects > Members > Maintainer adds member with expiration date', :js do
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:project) { create(:project) }
let!(:new_member) { create(:user) }
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
end
it 'expiration date is displayed in the members list' do
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 112b06c047d..adc8202cde7 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-describe 'Projects > Members > Master manages access requests' do
- it_behaves_like 'Master manages access requests' do
+describe 'Projects > Members > Maintainer manages access requests' do
+ it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:project, :public, :access_requestable) }
let(:members_page_path) { project_project_members_path(entity) }
end
diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb
index b126f0c6cb1..c6d85e5d22f 100644
--- a/spec/features/projects/members/share_with_group_spec.rb
+++ b/spec/features/projects/members/share_with_group_spec.rb
@@ -4,7 +4,7 @@ describe 'Project > Members > Share with Group', :js do
include Select2Helper
include ActionView::Helpers::DateHelper
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
describe 'Share with group lock' do
shared_examples 'the project can be shared with groups' do
@@ -27,8 +27,8 @@ describe 'Project > Members > Share with Group', :js do
let(:project) { create(:project, namespace: create(:group)) }
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
end
context 'when the group has "Share with group lock" disabled' do
@@ -65,8 +65,8 @@ describe 'Project > Members > Share with Group', :js do
let(:project) { create(:project, namespace: subgroup) }
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
end
context 'when the root_group has "Share with group lock" disabled' do
@@ -112,8 +112,8 @@ describe 'Project > Members > Share with Group', :js do
end
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
visit project_settings_members_path(project)
@@ -142,11 +142,11 @@ describe 'Project > Members > Share with Group', :js do
let(:project) { create(:project) }
before do
- project.add_master(master)
- sign_in(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
- create(:group).add_owner(master)
- create(:group).add_owner(master)
+ create(:group).add_owner(maintainer)
+ create(:group).add_owner(maintainer)
visit project_settings_members_path(project)
@@ -174,10 +174,10 @@ describe 'Project > Members > Share with Group', :js do
let!(:project) { create(:project, namespace: nested_group) }
before do
- project.add_master(master)
- sign_in(master)
- group.add_master(master)
- group_to_share_with.add_master(master)
+ project.add_maintainer(maintainer)
+ sign_in(maintainer)
+ group.add_maintainer(maintainer)
+ group_to_share_with.add_maintainer(maintainer)
end
it 'the groups dropdown does not show ancestors', :nested_groups do
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index 1e1071348c3..220775b514d 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -1,20 +1,20 @@
require 'spec_helper'
describe 'Projects > Members > Sorting' do
- let(:master) { create(:user, name: 'John Doe') }
+ let(:maintainer) { create(:user, name: 'John Doe') }
let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) }
- let(:project) { create(:project, namespace: master.namespace, creator: master) }
+ let(:project) { create(:project, namespace: maintainer.namespace, creator: maintainer) }
before do
create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago)
- sign_in(master)
+ sign_in(maintainer)
end
it 'sorts alphabetically by default' do
visit_members_list(sort: nil)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
@@ -23,14 +23,14 @@ describe 'Projects > Members > Sorting' do
visit_members_list(sort: :access_level_asc)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
it 'sorts by access level descending' do
visit_members_list(sort: :access_level_desc)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
@@ -38,7 +38,7 @@ describe 'Projects > Members > Sorting' do
it 'sorts by last joined' do
visit_members_list(sort: :last_joined)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
@@ -47,14 +47,14 @@ describe 'Projects > Members > Sorting' do
visit_members_list(sort: :oldest_joined)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
it 'sorts by name ascending' do
visit_members_list(sort: :name_asc)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
@@ -63,14 +63,14 @@ describe 'Projects > Members > Sorting' do
visit_members_list(sort: :name_desc)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
visit_members_list(sort: :recent_sign_in)
- expect(first_member).to include(master.name)
+ expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
@@ -79,7 +79,7 @@ describe 'Projects > Members > Sorting' do
visit_members_list(sort: :oldest_sign_in)
expect(first_member).to include(developer.name)
- expect(second_member).to include(master.name)
+ expect(second_member).to include(maintainer.name)
expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index 35d6ac1c650..50ba67f0ffc 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Projects > Members > User requests access', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable, :repository) }
- let(:master) { project.owner }
+ let(:maintainer) { project.owner }
before do
sign_in(user)
@@ -11,7 +11,7 @@ describe 'Projects > Members > User requests access', :js do
end
it 'request access feature is disabled' do
- project.update_attributes(request_access_enabled: false)
+ project.update(request_access_enabled: false)
visit project_path(project)
expect(page).not_to have_content 'Request Access'
@@ -20,7 +20,7 @@ describe 'Projects > Members > User requests access', :js do
it 'user can request access to a project' do
perform_enqueued_jobs { click_link 'Request Access' }
- expect(ActionMailer::Base.deliveries.last.to).to eq [master.notification_email]
+ expect(ActionMailer::Base.deliveries.last.to).to eq [maintainer.notification_email]
expect(ActionMailer::Base.deliveries.last.subject).to eq "Request to join the #{project.full_name} project"
expect(project.requesters.exists?(user_id: user)).to be_truthy
diff --git a/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb b/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
index b257f447439..2d12d690151 100644
--- a/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_closes_merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'User closes a merge requests', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb b/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
index 0a952cfc2a9..8ea358bcc70 100644
--- a/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/merge_requests/user_comments_on_commit_spec.rb
@@ -9,7 +9,7 @@ describe 'User comments on a commit', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_commit_path(project, sample_commit.id))
diff --git a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
index 1828b60fec7..441b080bee5 100644
--- a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
+++ b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb
@@ -11,7 +11,7 @@ describe 'User comments on a diff', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(diffs_project_merge_request_path(project, merge_request))
@@ -31,7 +31,7 @@ describe 'User comments on a diff', :js do
page.within('.files > div:nth-child(3)') do
expect(page).to have_content('Line is wrong')
- find('.js-toggle-diff-comments').click
+ find('.js-btn-vue-toggle-comments').click
expect(page).not_to have_content('Line is wrong')
end
@@ -64,7 +64,7 @@ describe 'User comments on a diff', :js do
# Hide the comment.
page.within('.files > div:nth-child(3)') do
- find('.js-toggle-diff-comments').click
+ find('.js-btn-vue-toggle-comments').click
expect(page).not_to have_content('Line is wrong')
end
@@ -77,7 +77,7 @@ describe 'User comments on a diff', :js do
# Show the comment.
page.within('.files > div:nth-child(3)') do
- find('.js-toggle-diff-comments').click
+ find('.js-btn-vue-toggle-comments').click
end
# Now both the comments should be shown.
diff --git a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb b/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
index f90aaba3caf..69bdab85d81 100644
--- a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb
@@ -8,7 +8,7 @@ describe 'User comments on a merge request', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb b/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
index 1f21ef7b382..38b4e4a6d1b 100644
--- a/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_creates_merge_request_spec.rb
@@ -8,7 +8,7 @@ describe "User creates a merge request", :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb b/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
index 3d19a2923b9..7de0f9daac6 100644
--- a/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb
@@ -8,7 +8,7 @@ describe 'User edits a merge request', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(edit_project_merge_request_path(project, merge_request))
diff --git a/spec/features/projects/merge_requests/user_manages_subscription_spec.rb b/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
index f55eb5c6664..68a835e7f77 100644
--- a/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
+++ b/spec/features/projects/merge_requests/user_manages_subscription_spec.rb
@@ -6,7 +6,7 @@ describe 'User manages subscription', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb b/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
index ba3c9789da1..745b4537e72 100644
--- a/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_reopens_merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'User reopens a merge requests', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
index 305658f1b5d..e401933aed2 100644
--- a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
+++ b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
@@ -9,7 +9,7 @@ describe 'User sorts merge requests' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_merge_requests_path(project))
diff --git a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
index 3aac93eaf7c..6ac495aa03d 100644
--- a/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
+++ b/spec/features/projects/merge_requests/user_views_open_merge_request_spec.rb
@@ -31,7 +31,7 @@ describe 'User views an open merge request' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(edit_project_merge_request_path(project, merge_request))
diff --git a/spec/features/projects/milestones/user_interacts_with_labels_spec.rb b/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
index f6a82f80d65..a6d58be7b13 100644
--- a/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
+++ b/spec/features/projects/milestones/user_interacts_with_labels_spec.rb
@@ -11,7 +11,7 @@ describe 'User interacts with labels' do
let(:label_enhancement) { create(:label, project: project, title: 'enhancement') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
issue1.labels << [label_bug, label_feature]
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index f23ec11a458..bbe08ff83ff 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -25,6 +25,22 @@ describe 'New project' do
expect(page).to have_link('GitLab export')
end
+ describe 'manifest import option' do
+ before do
+ visit new_project_path
+
+ find('#import-project-tab').click
+ end
+
+ context 'when using postgres', :postgresql do
+ it { expect(page).to have_link('Manifest file') }
+ end
+
+ context 'when using mysql', :mysql do
+ it { expect(page).not_to have_link('Manifest file') }
+ end
+ end
+
context 'Visibility level selector', :js do
Gitlab::VisibilityLevel.options.each do |key, level|
it "sets selector to #{key}" do
@@ -94,7 +110,7 @@ describe 'New project' do
let(:subgroup) { create(:group, parent: group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
visit new_project_path(namespace_id: subgroup.id)
end
@@ -201,5 +217,16 @@ describe 'New project' do
expect(current_path).to eq new_import_google_code_path
end
end
+
+ context 'from manifest file', :postgresql do
+ before do
+ first('.import_manifest').click
+ end
+
+ it 'shows import instructions' do
+ expect(page).to have_content('Manifest file import')
+ expect(current_path).to eq new_import_manifest_path
+ end
+ end
end
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 6bf65e16291..831f22a0e69 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Pages' do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:role) { :master }
+ let(:role) { :maintainer }
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 220b3529c59..ee6b67b2188 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -9,9 +9,9 @@ describe 'Pipeline Schedules', :js do
let(:scope) { nil }
let!(:user) { create(:user) }
- context 'logged in as master' do
+ context 'logged in as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
gitlab_sign_in(user)
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 9c165b17704..4a83bcc3efb 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -595,7 +595,7 @@ describe 'Pipelines', :js do
before do
create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
- project.add_master(user)
+ project.add_maintainer(user)
visit project_pipelines_path(project)
end
@@ -606,7 +606,7 @@ describe 'Pipelines', :js do
describe 'user clicks the button' do
context 'when project already has jobs_cache_index' do
before do
- project.update_attributes(jobs_cache_index: 1)
+ project.update(jobs_cache_index: 1)
end
it 'increments jobs_cache_index' do
diff --git a/spec/features/projects/remote_mirror_spec.rb b/spec/features/projects/remote_mirror_spec.rb
index 53a3f6f474b..5259a8942dc 100644
--- a/spec/features/projects/remote_mirror_spec.rb
+++ b/spec/features/projects/remote_mirror_spec.rb
@@ -7,13 +7,13 @@ describe 'Project remote mirror', :feature do
describe 'On a project', :js do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
context 'when last_error is present but last_update_at is not' do
it 'renders error message without timstamp' do
- remote_mirror.update_attributes(last_error: 'Some new error', last_update_at: nil)
+ remote_mirror.update(last_error: 'Some new error', last_update_at: nil)
visit project_mirror_path(project)
@@ -23,7 +23,7 @@ describe 'Project remote mirror', :feature do
context 'when last_error and last_update_at are present' do
it 'renders error message with timestamp' do
- remote_mirror.update_attributes(last_error: 'Some new error', last_update_at: Time.now - 5.minutes)
+ remote_mirror.update(last_error: 'Some new error', last_update_at: Time.now - 5.minutes)
visit project_mirror_path(project)
diff --git a/spec/features/projects/services/user_activates_asana_spec.rb b/spec/features/projects/services/user_activates_asana_spec.rb
index db836d2985c..c44e07dd3b4 100644
--- a/spec/features/projects/services/user_activates_asana_spec.rb
+++ b/spec/features/projects/services/user_activates_asana_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Asana' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_assembla_spec.rb b/spec/features/projects/services/user_activates_assembla_spec.rb
index f099b332785..9c3884a7c74 100644
--- a/spec/features/projects/services/user_activates_assembla_spec.rb
+++ b/spec/features/projects/services/user_activates_assembla_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Assembla' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
index a00c2e0ad99..19573565265 100644
--- a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
+++ b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Atlassian Bamboo CI' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_emails_on_push_spec.rb b/spec/features/projects/services/user_activates_emails_on_push_spec.rb
index 3769875b29c..cc55f7b2060 100644
--- a/spec/features/projects/services/user_activates_emails_on_push_spec.rb
+++ b/spec/features/projects/services/user_activates_emails_on_push_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Emails on push' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_flowdock_spec.rb b/spec/features/projects/services/user_activates_flowdock_spec.rb
index 5298d8acaf5..f981b7e9da9 100644
--- a/spec/features/projects/services/user_activates_flowdock_spec.rb
+++ b/spec/features/projects/services/user_activates_flowdock_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Flowdock' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_hipchat_spec.rb b/spec/features/projects/services/user_activates_hipchat_spec.rb
index a9bf16642c7..2f5313c91f9 100644
--- a/spec/features/projects/services/user_activates_hipchat_spec.rb
+++ b/spec/features/projects/services/user_activates_hipchat_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates HipChat' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_irker_spec.rb b/spec/features/projects/services/user_activates_irker_spec.rb
index 435663c818f..4c8e321b411 100644
--- a/spec/features/projects/services/user_activates_irker_spec.rb
+++ b/spec/features/projects/services/user_activates_irker_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Irker (IRC gateway)' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_issue_tracker_spec.rb b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
index e9502178bd7..7cd5b12802b 100644
--- a/spec/features/projects/services/user_activates_issue_tracker_spec.rb
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -15,7 +15,7 @@ describe 'User activates issue tracker', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_settings_integrations_path(project)
diff --git a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
index 1048803fde8..28d83a8b961 100644
--- a/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
+++ b/spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates JetBrains TeamCity CI' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb
index 429128ec096..08e1855d034 100644
--- a/spec/features/projects/services/user_activates_jira_spec.rb
+++ b/spec/features/projects/services/user_activates_jira_spec.rb
@@ -17,7 +17,7 @@ describe 'User activates Jira', :js do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_settings_integrations_path(project)
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index d4a6417290d..25b74cc481d 100644
--- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
@@ -8,7 +8,7 @@ describe 'Setup Mattermost slash commands', :js do
before do
stub_mattermost_setting(enabled: mattermost_enabled)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit edit_project_service_path(project, service)
end
diff --git a/spec/features/projects/services/user_activates_packagist_spec.rb b/spec/features/projects/services/user_activates_packagist_spec.rb
index b0cc818f093..756e9b33c07 100644
--- a/spec/features/projects/services/user_activates_packagist_spec.rb
+++ b/spec/features/projects/services/user_activates_packagist_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Packagist' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb b/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
index d5d109ba48b..1d6b19e0b0c 100644
--- a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
+++ b/spec/features/projects/services/user_activates_pivotaltracker_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates PivotalTracker' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_prometheus_spec.rb b/spec/features/projects/services/user_activates_prometheus_spec.rb
index 33f884eb148..61361c8a2e3 100644
--- a/spec/features/projects/services/user_activates_prometheus_spec.rb
+++ b/spec/features/projects/services/user_activates_prometheus_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Prometheus' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_pushover_spec.rb b/spec/features/projects/services/user_activates_pushover_spec.rb
index 9b7e8d62792..24612ee1457 100644
--- a/spec/features/projects/services/user_activates_pushover_spec.rb
+++ b/spec/features/projects/services/user_activates_pushover_spec.rb
@@ -5,7 +5,7 @@ describe 'User activates Pushover' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/services/user_activates_slack_notifications_spec.rb b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
index fae9ebd1bd6..24b5d5259db 100644
--- a/spec/features/projects/services/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_notifications_spec.rb
@@ -6,7 +6,7 @@ describe 'User activates Slack notifications' do
let(:project) { create(:project, slack_service: service) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
@@ -29,7 +29,7 @@ describe 'User activates Slack notifications' do
context 'when service is already configured' do
before do
service.fields
- service.update_attributes(
+ service.update(
push_channel: 1,
issue_channel: 2,
merge_request_channel: 3,
diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
index f540b76c784..08cfddf7993 100644
--- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
@@ -6,7 +6,7 @@ describe 'Slack slash commands' do
let(:service) { project.create_slack_slash_commands_service }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit edit_project_service_path(project, service)
end
diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb
index 5c5e8b66642..e9c8cf0fe34 100644
--- a/spec/features/projects/services/user_views_services_spec.rb
+++ b/spec/features/projects/services/user_views_services_spec.rb
@@ -5,7 +5,7 @@ describe 'User views services' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_settings_integrations_path(project))
diff --git a/spec/features/projects/settings/forked_project_settings_spec.rb b/spec/features/projects/settings/forked_project_settings_spec.rb
index a4d1b78b83b..df33d215602 100644
--- a/spec/features/projects/settings/forked_project_settings_spec.rb
+++ b/spec/features/projects/settings/forked_project_settings_spec.rb
@@ -7,8 +7,8 @@ describe 'Projects > Settings > For a forked project', :js do
let(:forked_project) { fork_project(original_project, user) }
before do
- original_project.add_master(user)
- forked_project.add_master(user)
+ original_project.add_maintainer(user)
+ forked_project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index 5178d63050e..8745ff72df0 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -21,8 +21,8 @@ describe 'Projects > Settings > Integration settings' do
end
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
context 'Webhooks' do
let(:hook) { create(:project_hook, :all_events_enabled, enable_ssl_verification: true, project: project) }
diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb
index 342be1d2a9d..befb306b48d 100644
--- a/spec/features/projects/settings/lfs_settings_spec.rb
+++ b/spec/features/projects/settings/lfs_settings_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
describe 'Projects > Settings > LFS settings' do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:role) { :master }
+ let(:role) { :maintainer }
context 'LFS enabled setting' do
before do
@@ -13,8 +13,8 @@ describe 'Projects > Settings > LFS settings' do
project.add_role(user, role)
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
it 'displays the correct elements', :js do
visit edit_project_path(project)
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index cfdae246c09..742ecf82c38 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -21,8 +21,8 @@ describe "Projects > Settings > Pipelines settings" do
end
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
it 'be allowed to change' do
visit project_settings_ci_cd_path(project)
diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb
index e53da997c1d..2ec94274f80 100644
--- a/spec/features/projects/settings/project_badges_spec.rb
+++ b/spec/features/projects/settings/project_badges_spec.rb
@@ -12,7 +12,7 @@ describe 'Project Badges' do
let!(:group_badge) { create(:group_badge, group: group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
sign_in(user)
visit(project_settings_badges_path(project))
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index f085e1aa50a..a0f5b234ebc 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -20,8 +20,8 @@ describe 'Projects > Settings > Repository settings' do
end
end
- context 'for master' do
- let(:role) { :master }
+ context 'for maintainer' do
+ let(:role) { :maintainer }
context 'Deploy Keys', :js do
let(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) }
@@ -124,7 +124,7 @@ describe 'Projects > Settings > Repository settings' do
let(:user2) { create(:user) }
before do
- project.add_master(user2)
+ project.add_maintainer(user2)
visit project_settings_repository_path(project)
end
diff --git a/spec/features/projects/settings/user_archives_project_spec.rb b/spec/features/projects/settings/user_archives_project_spec.rb
index 38c8a8c2468..5008eab4d39 100644
--- a/spec/features/projects/settings/user_archives_project_spec.rb
+++ b/spec/features/projects/settings/user_archives_project_spec.rb
@@ -4,7 +4,7 @@ describe 'Projects > Settings > User archives a project' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/projects/settings/user_changes_avatar_spec.rb b/spec/features/projects/settings/user_changes_avatar_spec.rb
index 2dcc79d8a12..64335163016 100644
--- a/spec/features/projects/settings/user_changes_avatar_spec.rb
+++ b/spec/features/projects/settings/user_changes_avatar_spec.rb
@@ -5,7 +5,7 @@ describe 'Projects > Settings > User changes avatar' do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
index 71a077039b7..ecfb49b9efe 100644
--- a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
+++ b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb
@@ -50,7 +50,7 @@ describe "User interacts with deploy keys", :js do
before do
create(:deploy_keys_project, project: another_project, deploy_key: deploy_key)
- another_project.add_master(user)
+ another_project.add_maintainer(user)
end
it "shows deploy keys" do
@@ -110,7 +110,7 @@ describe "User interacts with deploy keys", :js do
before do
create(:deploy_keys_project, project: another_project, deploy_key: deploy_key)
- another_project.add_master(user)
+ another_project.add_maintainer(user)
end
it_behaves_like "attaches a key"
diff --git a/spec/features/projects/settings/user_manages_group_links_spec.rb b/spec/features/projects/settings/user_manages_group_links_spec.rb
index 92ce2ca83c7..2f1824d7849 100644
--- a/spec/features/projects/settings/user_manages_group_links_spec.rb
+++ b/spec/features/projects/settings/user_manages_group_links_spec.rb
@@ -9,10 +9,10 @@ describe 'Projects > Settings > User manages group links' do
let(:group_market) { create(:group, name: 'Market', path: 'market') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
- share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
share_link.group_id = group_ops.id
share_link.save!
diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb
index d3003753ae6..b8ca11d53f0 100644
--- a/spec/features/projects/settings/user_manages_project_members_spec.rb
+++ b/spec/features/projects/settings/user_manages_project_members_spec.rb
@@ -9,7 +9,7 @@ describe 'Projects > Settings > User manages project members' do
let(:user_mike) { create(:user, name: 'Mike') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user_dmitriy)
sign_in(user)
end
@@ -30,7 +30,7 @@ describe 'Projects > Settings > User manages project members' do
end
it 'imports a team from another project' do
- project2.add_master(user)
+ project2.add_maintainer(user)
project2.add_reporter(user_mike)
visit(project_project_members_path(project))
@@ -54,7 +54,7 @@ describe 'Projects > Settings > User manages project members' do
group.add_owner(user)
group.add_developer(user_dmitriy)
- share_link = project.project_group_links.new(group_access: Gitlab::Access::MASTER)
+ share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
share_link.group_id = group.id
share_link.save!
diff --git a/spec/features/projects/settings/user_transfers_a_project_spec.rb b/spec/features/projects/settings/user_transfers_a_project_spec.rb
index 96b7cf1f93b..2fdbc04fa62 100644
--- a/spec/features/projects/settings/user_transfers_a_project_spec.rb
+++ b/spec/features/projects/settings/user_transfers_a_project_spec.rb
@@ -10,7 +10,7 @@ describe 'Projects > Settings > User transfers a project', :js do
sign_in(user)
end
- def transfer_project(project, group)
+ def transfer_project(project, group, confirm: true)
visit edit_project_path(project)
page.within('.js-project-transfer-form') do
@@ -21,6 +21,8 @@ describe 'Projects > Settings > User transfers a project', :js do
click_button('Transfer project')
+ return unless confirm
+
fill_in 'confirm_name_input', with: project.name
click_button 'Confirm'
@@ -28,6 +30,11 @@ describe 'Projects > Settings > User transfers a project', :js do
wait_for_requests
end
+ it 'focuses on the confirmation field' do
+ transfer_project(project, group, confirm: false)
+ expect(page).to have_selector '#confirm_name_input:focus'
+ end
+
it 'allows transferring a project to a group' do
old_path = project_path(project)
transfer_project(project, group)
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 2ec6990313f..1fbc108697f 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -59,12 +59,12 @@ describe 'Projects > Settings > Visibility settings', :js do
end
end
- context 'as master' do
- let(:master_user) { create(:user) }
+ context 'as maintainer' do
+ let(:maintainer_user) { create(:user) }
before do
- project.add_master(master_user)
- sign_in(master_user)
+ project.add_maintainer(maintainer_user)
+ sign_in(maintainer_user)
visit edit_project_path(project)
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 31b105229be..546619e88ec 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -16,4 +16,36 @@ describe 'Projects > Show > User manages notifications', :js do
expect(page).to have_content 'On mention'
end
end
+
+ context 'custom notification settings' do
+ let(:email_events) do
+ [
+ :new_note,
+ :new_issue,
+ :reopen_issue,
+ :close_issue,
+ :reassign_issue,
+ :issue_due,
+ :new_merge_request,
+ :push_to_merge_request,
+ :reopen_merge_request,
+ :close_merge_request,
+ :reassign_merge_request,
+ :merge_merge_request,
+ :failed_pipeline,
+ :success_pipeline
+ ]
+ end
+
+ it 'shows notification settings checkbox' do
+ first('.notifications-btn').click
+ page.find('a[data-notification-level="custom"]').click
+
+ page.within('.custom-notifications-form') do
+ email_events.each do |event_name|
+ expect(page).to have_selector("input[name='notification_setting[#{event_name}]']")
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
index aa23bef6fd8..d9d57298929 100644
--- a/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
+++ b/spec/features/projects/show/user_sees_deletion_failure_message_spec.rb
@@ -8,7 +8,7 @@ describe 'Projects > Show > User sees a deletion failure message' do
end
it 'shows error message if deletion for project fails' do
- project.update_attributes(delete_error: "Something went wrong", pending_delete: false)
+ project.update(delete_error: "Something went wrong", pending_delete: false)
visit project_path(project)
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index 7b9242f0631..0405e21a0d7 100644
--- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
+++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
@@ -38,9 +38,9 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_path(project)
@@ -138,10 +138,10 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false)
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 2388feeb980..6d8a72dd6a3 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -16,7 +16,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
context 'when a user is authenticated' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_snippets_path(project)
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index 004ac55b656..3cc797277dd 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > Project snippet', :js do
let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
index 01cf9740d1f..d82e350e0f7 100644
--- a/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_comments_on_snippet_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > User comments on a snippet', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippet_path(project, snippet))
diff --git a/spec/features/projects/snippets/user_deletes_snippet_spec.rb b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
index e64837ad59e..2bd8bb9d551 100644
--- a/spec/features/projects/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_deletes_snippet_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > User deletes a snippet' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippet_path(project, snippet))
diff --git a/spec/features/projects/snippets/user_updates_snippet_spec.rb b/spec/features/projects/snippets/user_updates_snippet_spec.rb
index eaedbbf32b6..33f77d55f89 100644
--- a/spec/features/projects/snippets/user_updates_snippet_spec.rb
+++ b/spec/features/projects/snippets/user_updates_snippet_spec.rb
@@ -6,7 +6,7 @@ describe 'Projects > Snippets > User updates a snippet' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippet_path(project, snippet))
diff --git a/spec/features/projects/snippets/user_views_snippets_spec.rb b/spec/features/projects/snippets/user_views_snippets_spec.rb
index 376b76e0001..1243db9d9f7 100644
--- a/spec/features/projects/snippets/user_views_snippets_spec.rb
+++ b/spec/features/projects/snippets/user_views_snippets_spec.rb
@@ -8,7 +8,7 @@ describe 'Projects > Snippets > User views snippets' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_snippets_path(project))
diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb
index eb2d3ff50a0..50e7e934cf6 100644
--- a/spec/features/projects/sub_group_issuables_spec.rb
+++ b/spec/features/projects/sub_group_issuables_spec.rb
@@ -7,7 +7,7 @@ describe 'Subgroup Issuables', :js, :nested_groups do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
end
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index d0902bce7f3..d3aa4912099 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -5,7 +5,7 @@ describe 'Multi-file editor new directory', :js do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tree_path(project, :master)
@@ -22,9 +22,7 @@ describe 'Multi-file editor new directory', :js do
end
it 'creates directory in current directory' do
- find('.add-to-tree').click
-
- click_link('New directory')
+ all('.ide-tree-header button').last.click
page.within('.modal') do
find('.form-control').set('folder name')
@@ -32,9 +30,7 @@ describe 'Multi-file editor new directory', :js do
click_button('Create directory')
end
- find('.add-to-tree').click
-
- click_link('New file')
+ first('.ide-tree-header button').click
page.within('.modal-dialog') do
find('.form-control').set('file name')
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 03a29fae0bd..f836783cbff 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -5,7 +5,7 @@ describe 'Multi-file editor new file', :js do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_path(project)
@@ -22,9 +22,7 @@ describe 'Multi-file editor new file', :js do
end
it 'creates file in current directory' do
- find('.add-to-tree').click
-
- click_link('New file')
+ first('.ide-tree-header button').click
page.within('.modal') do
find('.form-control').set('file name')
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index dc59666ffce..9e15163fd72 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -5,7 +5,7 @@ describe 'Projects tree' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tree_path(project, 'master')
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
index 804a4450ae2..dcf7d314f8e 100644
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -7,7 +7,7 @@ describe 'Multi-file editor upload file', :js do
let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tree_path(project, :master)
@@ -24,14 +24,10 @@ describe 'Multi-file editor upload file', :js do
end
it 'uploads text file' do
- find('.add-to-tree').click
-
# make the field visible so capybara can use it
execute_script('document.querySelector("#file-upload").classList.remove("hidden")')
attach_file('file-upload', txt_file)
- find('.add-to-tree').click
-
expect(page).to have_selector('.multi-file-tab', text: 'doc_sample.txt')
expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline))
end
diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb
index c8b3104b9fe..df9ee69aadb 100644
--- a/spec/features/projects/user_uses_shortcuts_spec.rb
+++ b/spec/features/projects/user_uses_shortcuts_spec.rb
@@ -5,7 +5,7 @@ describe 'User uses shortcuts', :js do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(project_path(project))
diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb
index 7b982301ffc..b7c0834d33a 100644
--- a/spec/features/projects/user_views_empty_project_spec.rb
+++ b/spec/features/projects/user_views_empty_project_spec.rb
@@ -15,9 +15,9 @@ describe 'User views an empty project' do
end
end
- describe 'as a master' do
+ describe 'as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allowing push to default branch'
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 84ec32b3fac..a48ad94e9fa 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -7,7 +7,7 @@ describe 'View on environment', :js do
let(:user) { project.creator }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when the branch has a route map' do
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index ef15e7e1ff9..ed5f8105487 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -9,11 +9,12 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
[relative link 1](../relative)
[relative link 2](./relative)
[relative link 3](./e/f/relative)
+[spaced link](title with spaces)
HEREDOC
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
@@ -42,6 +43,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -64,6 +66,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -86,6 +89,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
@@ -119,6 +123,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -136,6 +141,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
@@ -153,6 +159,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
+ expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 733e6c89de7..149eeb4f9ba 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -2,16 +2,22 @@ require "spec_helper"
describe "User creates wiki page" do
let(:user) { create(:user) }
+ let(:wiki) { ProjectWiki.new(project, user) }
+ let(:project) { create(:project) }
before do
- project.add_master(user)
- sign_in(user)
+ project.add_maintainer(user)
- visit(project_wikis_path(project))
- click_link "Create your first page"
+ sign_in(user)
end
context "when wiki is empty" do
+ before do
+ visit(project_wikis_path(project))
+
+ click_link "Create your first page"
+ end
+
context "in a user namespace" do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
@@ -165,7 +171,9 @@ describe "User creates wiki page" do
context "when wiki is not empty", :js do
before do
- create(:wiki_page, wiki: create(:project, :wiki_repo, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" })
+ create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'Home page' })
+
+ visit(project_wikis_path(project))
end
context "in a user namespace" do
@@ -290,4 +298,34 @@ describe "User creates wiki page" do
end
end
end
+
+ describe 'sidebar feature' do
+ context 'when there are some existing pages' do
+ before do
+ create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: 'home' })
+ create(:wiki_page, wiki: wiki, attrs: { title: 'another', content: 'another' })
+ end
+
+ it 'renders a default sidebar when there is no customized sidebar' do
+ visit(project_wikis_path(project))
+
+ expect(page).to have_content('Another')
+ expect(page).to have_content('More Pages')
+ end
+
+ context 'when there is a customized sidebar' do
+ before do
+ create(:wiki_page, wiki: wiki, attrs: { title: '_sidebar', content: 'My customized sidebar' })
+ end
+
+ it 'renders my customized sidebar instead of the default one' do
+ visit(project_wikis_path(project))
+
+ expect(page).to have_content('My customized sidebar')
+ expect(page).to have_content('More Pages')
+ expect(page).not_to have_content('Another')
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index 2ccbc15b6da..2840d28cf30 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -4,7 +4,7 @@ describe 'User updates wiki page' do
shared_examples 'wiki page user update' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
index 92b50169476..fb0ebe22bf7 100644
--- a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
@@ -4,7 +4,7 @@ describe 'Projects > Wiki > User views wiki in project page' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 1de7d9a56a8..0ef7f35f64a 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -11,7 +11,7 @@ describe 'User views a wiki page' do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 8636d17f2c4..39b47d99040 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -151,10 +151,16 @@ describe 'Project' do
before do
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
visit edit_project_path(project)
end
+ it 'focuses on the confirmation field' do
+ click_button 'Remove project'
+
+ expect(page).to have_selector '#confirm_name_input:focus'
+ end
+
it 'removes a project' do
expect { remove_with_confirm('Remove project', project.path) }.to change { Project.count }.by(-1)
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
@@ -169,7 +175,7 @@ describe 'Project' do
let(:project) { create(:forked_project_with_submodules) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_path(project)
end
@@ -198,7 +204,7 @@ describe 'Project' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in user
visit project_path(project)
end
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 83a5f88f0b5..63c38a25f4b 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -28,9 +28,9 @@ describe 'Protected Branches', :js do
end
end
- context 'logged in as master' do
+ context 'logged in as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb
index 9b6864eb90f..54ebda9dcab 100644
--- a/spec/features/reportable_note/commit_spec.rb
+++ b/spec/features/reportable_note/commit_spec.rb
@@ -7,7 +7,7 @@ describe 'Reportable note on commit', :js do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index f5a1950e48e..bce1f7a3780 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -7,7 +7,7 @@ describe 'Reportable note on issue', :js do
let!(:note) { create(:note_on_issue, noteable: issue, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
index 1f69257f7ed..d00324156c4 100644
--- a/spec/features/reportable_note/merge_request_spec.rb
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -6,7 +6,7 @@ describe 'Reportable note on merge request', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index 98ef50b78de..06218d9b286 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -5,7 +5,7 @@ describe 'Reportable note on snippets', :js do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 443c2b9acae..0c6cf3dc477 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -11,7 +11,7 @@ describe 'Runners' do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'user can see a button to install runners on kubernetes clusters' do
@@ -25,7 +25,7 @@ describe 'Runners' do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when a project_type runner is activated on the project' do
@@ -125,7 +125,7 @@ describe 'Runners' do
let!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
before do
- another_project.add_master(user)
+ another_project.add_maintainer(user)
end
it 'user enables and disables a specific runner' do
@@ -165,7 +165,7 @@ describe 'Runners' do
let(:project) { create(:project, shared_runners_enabled: false) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'user enables shared runners' do
@@ -179,14 +179,14 @@ describe 'Runners' do
context 'group runners in project settings' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
let(:group) { create :group }
context 'as project and group maintainer' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
context 'project with a group but no group runner' do
@@ -260,7 +260,7 @@ describe 'Runners' do
context 'group runners in group settings' do
let(:group) { create(:group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
context 'group with no runners' do
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index 9e089c5a6cb..ecec2f3e043 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -6,7 +6,7 @@ describe 'User searches for code' do
context 'when signed in' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb
index d6120ff8517..4bff269f89e 100644
--- a/spec/features/search/user_searches_for_issues_spec.rb
+++ b/spec/features/search/user_searches_for_issues_spec.rb
@@ -8,7 +8,7 @@ describe 'User searches for issues', :js do
context 'when signed in' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb
index 68e2f7a857d..75d44e413cb 100644
--- a/spec/features/search/user_searches_for_merge_requests_spec.rb
+++ b/spec/features/search/user_searches_for_merge_requests_spec.rb
@@ -7,7 +7,7 @@ describe 'User searches for merge requests', :js do
let!(:merge_request2) { create(:merge_request, :simple, title: 'Bar', source_project: project, target_project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb
index fc6cd81eb68..7d52c4c8bcc 100644
--- a/spec/features/search/user_searches_for_milestones_spec.rb
+++ b/spec/features/search/user_searches_for_milestones_spec.rb
@@ -7,7 +7,7 @@ describe 'User searches for milestones', :js do
let!(:milestone2) { create(:milestone, title: 'Bar', project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb
index 5098fb49ee1..3ee753b7d23 100644
--- a/spec/features/search/user_searches_for_wiki_pages_spec.rb
+++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb
@@ -6,7 +6,7 @@ describe 'User searches for wiki pages', :js do
let!(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'test_wiki', content: 'Some Wiki content' }) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit(search_path)
diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb
index 5067f0b0a49..51b32ba6c03 100644
--- a/spec/features/security/group/internal_access_spec.rb
+++ b/spec/features/security/group/internal_access_spec.rb
@@ -23,7 +23,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -38,7 +38,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -54,7 +54,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -69,7 +69,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -84,7 +84,7 @@ describe 'Internal Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
it { is_expected.to be_denied_for(:reporter).of(group) }
it { is_expected.to be_denied_for(:guest).of(group) }
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index ff32413dc7e..4705cd12d23 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -23,7 +23,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -38,7 +38,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -54,7 +54,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -69,7 +69,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -84,7 +84,7 @@ describe 'Private Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
it { is_expected.to be_denied_for(:reporter).of(group) }
it { is_expected.to be_denied_for(:guest).of(group) }
diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb
index 16d114fb3f7..3a53c3c2bc7 100644
--- a/spec/features/security/group/public_access_spec.rb
+++ b/spec/features/security/group/public_access_spec.rb
@@ -23,7 +23,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -38,7 +38,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -54,7 +54,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -69,7 +69,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:maintainer).of(group) }
it { is_expected.to be_allowed_for(:developer).of(group) }
it { is_expected.to be_allowed_for(:reporter).of(group) }
it { is_expected.to be_allowed_for(:guest).of(group) }
@@ -84,7 +84,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(group) }
- it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:maintainer).of(group) }
it { is_expected.to be_denied_for(:developer).of(group) }
it { is_expected.to be_denied_for(:reporter).of(group) }
it { is_expected.to be_denied_for(:guest).of(group) }
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index a7928857b7d..001e6c10eb2 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -17,7 +17,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -31,7 +31,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -45,7 +45,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -59,7 +59,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -73,7 +73,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -130,7 +130,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -144,7 +144,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -158,7 +158,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -172,7 +172,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -187,7 +187,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -201,7 +201,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -215,7 +215,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -229,7 +229,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -243,7 +243,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -262,7 +262,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -281,7 +281,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -295,7 +295,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -309,7 +309,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -324,7 +324,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -343,7 +343,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -359,7 +359,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -381,7 +381,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -397,7 +397,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -419,7 +419,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -435,7 +435,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -450,7 +450,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -464,7 +464,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -479,7 +479,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -494,7 +494,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -508,7 +508,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -530,7 +530,7 @@ describe "Internal Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index a4396b20afd..c6618355eea 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -17,7 +17,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -31,7 +31,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -45,7 +45,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -59,7 +59,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -73,7 +73,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -130,7 +130,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -144,7 +144,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -158,7 +158,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -172,7 +172,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -187,7 +187,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -201,7 +201,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -215,7 +215,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -234,7 +234,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -253,7 +253,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -267,7 +267,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -281,7 +281,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -308,7 +308,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -334,7 +334,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -362,7 +362,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -395,7 +395,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -425,7 +425,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -440,7 +440,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -455,7 +455,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -469,7 +469,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -483,7 +483,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -497,7 +497,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -511,7 +511,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -533,7 +533,7 @@ describe "Private Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index fccdeb0e5b7..3717dc13f1e 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -17,7 +17,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -31,7 +31,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -45,7 +45,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -59,7 +59,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -73,7 +73,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -129,7 +129,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -144,7 +144,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -163,7 +163,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -179,7 +179,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -201,7 +201,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -217,7 +217,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -239,7 +239,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -255,7 +255,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -270,7 +270,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -284,7 +284,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -299,7 +299,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -314,7 +314,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -328,7 +328,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -344,7 +344,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -357,7 +357,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -371,7 +371,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -385,7 +385,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -400,7 +400,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -414,7 +414,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -428,7 +428,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -442,7 +442,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -456,7 +456,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -475,7 +475,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -494,7 +494,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -508,7 +508,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_denied_for(:developer).of(project) }
it { is_expected.to be_denied_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -530,7 +530,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb
index d7dc99c0a57..b87eb86b88b 100644
--- a/spec/features/security/project/snippet/internal_access_spec.rb
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -13,7 +13,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -27,7 +27,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -42,7 +42,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -56,7 +56,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -72,7 +72,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -86,7 +86,7 @@ describe "Internal Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb
index 3ec1a388185..ead91d9a5fa 100644
--- a/spec/features/security/project/snippet/private_access_spec.rb
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -12,7 +12,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -26,7 +26,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -40,7 +40,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -54,7 +54,7 @@ describe "Private Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
index 39b104bfe27..9bab3a474b8 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -14,7 +14,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -28,7 +28,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_denied_for(:guest).of(project) }
@@ -43,7 +43,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -57,7 +57,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -71,7 +71,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -87,7 +87,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -101,7 +101,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
@@ -115,7 +115,7 @@ describe "Public Project Snippets Access" do
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
- it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:maintainer).of(project) }
it { is_expected.to be_allowed_for(:developer).of(project) }
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index db141ef7096..3d05474dca2 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -5,7 +5,7 @@ describe 'GPG signed commits', :js do
it 'changes from unverified to verified when the user changes his email to match the gpg key' do
user = create :user, email: 'unrelated.user@example.org'
- project.add_master(user)
+ project.add_maintainer(user)
Sidekiq::Testing.inline! do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
@@ -23,7 +23,7 @@ describe 'GPG signed commits', :js do
# user changes his email which makes the gpg key verified
Sidekiq::Testing.inline! do
user.skip_reconfirmation!
- user.update_attributes!(email: GpgHelpers::User1.emails.first)
+ user.update!(email: GpgHelpers::User1.emails.first)
end
visit project_commits_path(project, :'signed-commits')
@@ -36,7 +36,7 @@ describe 'GPG signed commits', :js do
it 'changes from unverified to verified when the user adds the missing gpg key' do
user = create :user, email: GpgHelpers::User1.emails.first
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
@@ -86,7 +86,7 @@ describe 'GPG signed commits', :js do
before do
user = create :user
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index 74b693f3eff..f31457db92f 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -68,6 +68,26 @@ describe 'Snippet', :js do
end
end
+ context 'with cached Redcarpet html' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+ let(:file_name) { 'test.md' }
+ let(:content) { "1. one\n - sublist\n" }
+
+ it 'renders correctly' do
+ expect(page).to have_xpath("//ol//li//ul")
+ end
+ end
+
+ context 'with cached CommonMark html' do
+ let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
+ let(:file_name) { 'test.md' }
+ let(:content) { "1. one\n - sublist\n" }
+
+ it 'renders correctly' do
+ expect(page).not_to have_xpath("//ol//li//ul")
+ end
+ end
+
context 'switching to the simple viewer' do
before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index b4e8253057b..db2970f3340 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe 'Master creates tag' do
+describe 'Maintainer creates tag' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index 1443e259ed9..8d567e925ef 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe 'Master deletes tag' do
+describe 'Maintainer deletes tag' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tags_path(project)
end
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb
index 4c0be6be96c..d8b5b3c4cc4 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/master_updates_tag_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe 'Master updates tag' do
+describe 'Maintainer updates tag' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
visit project_tags_path(project)
end
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 02ce0242614..3f4fe549f3e 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
-describe 'Master views tags' do
+describe 'Maintainer views tags' do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 64d05c57d3c..9c9127980a1 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -65,7 +65,7 @@ describe 'Task Lists' do
before do
Warden.test_mode!
- project.add_master(user)
+ project.add_maintainer(user)
project.add_guest(user2)
login_as(user)
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 36dafccd186..919859c145a 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -10,8 +10,8 @@ describe 'Triggers', :js do
sign_in(user)
@project = create(:project)
- @project.add_master(user)
- @project.add_master(user2)
+ @project.add_maintainer(user)
+ @project.add_maintainer(user2)
@project.add_guest(guest_user)
visit project_settings_ci_cd_path(@project)
diff --git a/spec/features/user_sees_revert_modal_spec.rb b/spec/features/user_sees_revert_modal_spec.rb
new file mode 100644
index 00000000000..11a9e470f76
--- /dev/null
+++ b/spec/features/user_sees_revert_modal_spec.rb
@@ -0,0 +1,25 @@
+require 'rails_helper'
+
+describe 'Merge request > User sees revert modal', :js do
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ sign_in(user)
+ visit(project_merge_request_path(project, merge_request))
+ click_button('Merge')
+ visit(merge_request_path(merge_request))
+ click_link('Revert')
+ end
+
+ it 'shows the revert modal' do
+ expect(page).to have_content('Revert this merge request')
+ end
+
+ it 'closes the revert modal with escape keypress' do
+ find('#modal-revert-commit').send_keys(:escape)
+
+ expect(page).not_to have_content('Revert this merge request')
+ end
+end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index b5bbb2c0ea5..3e2fb704bc6 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -14,4 +14,28 @@ describe 'User page' do
expect(page).to have_link('Snippets')
end
end
+
+ context 'signup disabled' do
+ it 'shows the sign in link' do
+ stub_application_setting(signup_enabled: false)
+
+ visit(user_path(user))
+
+ page.within '.navbar-nav' do
+ expect(page).to have_link('Sign in')
+ end
+ end
+ end
+
+ context 'signup enabled' do
+ it 'shows the sign in and register link' do
+ stub_application_setting(signup_enabled: true)
+
+ visit(user_path(user))
+
+ page.within '.navbar-nav' do
+ expect(page).to have_link('Sign in / Register')
+ end
+ end
+ end
end
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 5478e38ce70..6a9b281fb4c 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -4,19 +4,19 @@ describe 'Users > User browses projects on user page', :js do
let!(:user) { create :user }
let!(:private_project) do
create :project, :private, name: 'private', namespace: user.namespace do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
let!(:internal_project) do
create :project, :internal, name: 'internal', namespace: user.namespace do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
let!(:public_project) do
create :project, :public, name: 'public', namespace: user.namespace do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb
index 650f7229647..605777462bb 100644
--- a/spec/finders/access_requests_finder_spec.rb
+++ b/spec/finders/access_requests_finder_spec.rb
@@ -51,7 +51,7 @@ describe AccessRequestsFinder do
context 'when current user can see access requests' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
group.add_owner(user)
end
@@ -78,7 +78,7 @@ describe AccessRequestsFinder do
context 'when current user can see access requests' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
group.add_owner(user)
end
diff --git a/spec/finders/admin/projects_finder_spec.rb b/spec/finders/admin/projects_finder_spec.rb
index 7901d5fee28..44cc8debd04 100644
--- a/spec/finders/admin/projects_finder_spec.rb
+++ b/spec/finders/admin/projects_finder_spec.rb
@@ -54,7 +54,7 @@ describe Admin::ProjectsFinder do
context 'filter by visibility_level' do
before do
- private_project.add_master(user)
+ private_project.add_maintainer(user)
end
context 'private' do
diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
index c784fb87972..1ff65a8101b 100644
--- a/spec/finders/concerns/finder_with_cross_project_access_spec.rb
+++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
@@ -25,7 +25,7 @@ describe FinderWithCrossProjectAccess do
let!(:result) { create(:issue) }
before do
- result.project.add_master(user)
+ result.project.add_maintainer(user)
end
def expect_access_check_on_result
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
index 60ea98e61c7..9155a8d6fe9 100644
--- a/spec/finders/contributed_projects_finder_spec.rb
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -10,9 +10,9 @@ describe ContributedProjectsFinder do
let!(:private_project) { create(:project, :private) }
before do
- private_project.add_master(source_user)
+ private_project.add_maintainer(source_user)
private_project.add_developer(current_user)
- public_project.add_master(source_user)
+ public_project.add_maintainer(source_user)
create(:push_event, project: public_project, author: source_user)
create(:push_event, project: private_project, author: source_user)
diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments_finder_spec.rb
index 3a8a1e7de74..3cd421f22eb 100644
--- a/spec/finders/environments_finder_spec.rb
+++ b/spec/finders/environments_finder_spec.rb
@@ -7,7 +7,7 @@ describe EnvironmentsFinder do
let(:environment) { create(:environment, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'tagged deployment' do
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 63e15b365a4..f545da3aee4 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -9,9 +9,9 @@ describe GroupMembersFinder, '#execute' do
let(:user4) { create(:user) }
it 'returns members for top-level group' do
- member1 = group.add_master(user1)
- member2 = group.add_master(user2)
- member3 = group.add_master(user3)
+ member1 = group.add_maintainer(user1)
+ member2 = group.add_maintainer(user2)
+ member3 = group.add_maintainer(user3)
result = described_class.new(group).execute
@@ -19,11 +19,11 @@ describe GroupMembersFinder, '#execute' do
end
it 'returns members for nested group', :nested_groups do
- group.add_master(user2)
+ group.add_maintainer(user2)
nested_group.request_access(user4)
- member1 = group.add_master(user1)
- member3 = nested_group.add_master(user2)
- member4 = nested_group.add_master(user3)
+ member1 = group.add_maintainer(user1)
+ member3 = nested_group.add_maintainer(user2)
+ member4 = nested_group.add_maintainer(user3)
result = described_class.new(nested_group).execute
@@ -31,11 +31,11 @@ describe GroupMembersFinder, '#execute' do
end
it 'returns members for descendant groups if requested', :nested_groups do
- member1 = group.add_master(user2)
- member2 = group.add_master(user1)
- nested_group.add_master(user2)
- member3 = nested_group.add_master(user3)
- member4 = nested_group.add_master(user4)
+ member1 = group.add_maintainer(user2)
+ member2 = group.add_maintainer(user1)
+ nested_group.add_maintainer(user2)
+ member3 = nested_group.add_maintainer(user3)
+ member4 = nested_group.add_maintainer(user4)
result = described_class.new(group).execute(include_descendants: true)
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
index be80ee7d767..d6d95906f5e 100644
--- a/spec/finders/group_projects_finder_spec.rb
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -17,16 +17,16 @@ describe GroupProjectsFinder do
let!(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
before do
- shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
- shared_project_2.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
- shared_project_3.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
+ shared_project_1.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
+ shared_project_2.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
+ shared_project_3.project_group_links.create(group_access: Gitlab::Access::MAINTAINER, group: group)
end
subject { finder.execute }
describe 'with a group member current user' do
before do
- group.add_master(current_user)
+ group.add_maintainer(current_user)
end
context "only shared" do
@@ -68,7 +68,7 @@ describe GroupProjectsFinder do
describe 'without group member current_user' do
before do
- shared_project_2.add_master(current_user)
+ shared_project_2.add_maintainer(current_user)
current_user.reload
end
@@ -81,7 +81,7 @@ describe GroupProjectsFinder do
context "with external user" do
before do
- current_user.update_attributes(external: true)
+ current_user.update(external: true)
end
it { is_expected.to match_array([shared_project_2, shared_project_1]) }
@@ -93,8 +93,8 @@ describe GroupProjectsFinder do
context "without external user" do
before do
- private_project.add_master(current_user)
- subgroup_private_project.add_master(current_user)
+ private_project.add_maintainer(current_user)
+ subgroup_private_project.add_maintainer(current_user)
end
context 'with subgroups projects', :nested_groups do
@@ -112,7 +112,7 @@ describe GroupProjectsFinder do
context "with external user" do
before do
- current_user.update_attributes(external: true)
+ current_user.update(external: true)
end
context 'with subgroups projects', :nested_groups do
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 74e91b02f0f..07a2fa86dd7 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -26,7 +26,7 @@ describe IssuesFinder do
let(:issues) { described_class.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
before(:context) do
- project1.add_master(user)
+ project1.add_maintainer(user)
project2.add_developer(user)
project2.add_developer(user2)
project3.add_developer(user)
diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb
index 29a47e005a6..ae3e55f90f1 100644
--- a/spec/finders/joined_groups_finder_spec.rb
+++ b/spec/finders/joined_groups_finder_spec.rb
@@ -15,7 +15,7 @@ describe JoinedGroupsFinder do
context 'without a user' do
before do
- public_group.add_master(profile_owner)
+ public_group.add_maintainer(profile_owner)
end
it 'only shows public groups from profile owner' do
@@ -25,9 +25,9 @@ describe JoinedGroupsFinder do
context "with a user" do
before do
- private_group.add_master(profile_owner)
- internal_group.add_master(profile_owner)
- public_group.add_master(profile_owner)
+ private_group.add_maintainer(profile_owner)
+ internal_group.add_maintainer(profile_owner)
+ public_group.add_maintainer(profile_owner)
end
context "when the profile visitor is in the private group" do
@@ -53,7 +53,7 @@ describe JoinedGroupsFinder do
context 'external users' do
before do
- profile_visitor.update_attributes(external: true)
+ profile_visitor.update(external: true)
end
context 'if not a member' do
@@ -64,7 +64,7 @@ describe JoinedGroupsFinder do
context "if authorized" do
before do
- internal_group.add_master(profile_visitor)
+ internal_group.add_maintainer(profile_visitor)
end
it "shows internal groups if authorized" do
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 2fc5299b0f4..db48f00cd74 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -11,9 +11,9 @@ describe MembersFinder, '#execute' do
it 'returns members for project and parent groups', :nested_groups do
nested_group.request_access(user1)
- member1 = group.add_master(user2)
- member2 = nested_group.add_master(user3)
- member3 = project.add_master(user4)
+ member1 = group.add_maintainer(user2)
+ member2 = nested_group.add_maintainer(user3)
+ member3 = project.add_maintainer(user4)
result = described_class.new(project, user2).execute
@@ -23,9 +23,9 @@ describe MembersFinder, '#execute' do
it 'includes nested group members if asked', :nested_groups do
project = create(:project, namespace: group)
nested_group.request_access(user1)
- member1 = group.add_master(user2)
- member2 = nested_group.add_master(user3)
- member3 = project.add_master(user4)
+ member1 = group.add_maintainer(user2)
+ member2 = nested_group.add_maintainer(user3)
+ member3 = project.add_maintainer(user4)
result = described_class.new(project, user2).execute(include_descendants: true)
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 669ec602f11..35d0eeda8f6 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -24,7 +24,7 @@ describe MergeRequestsFinder do
let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) }
before do
- project1.add_master(user)
+ project1.add_maintainer(user)
project2.add_developer(user)
project3.add_developer(user)
project2.add_developer(user2)
@@ -142,7 +142,7 @@ describe MergeRequestsFinder do
end
before do
- new_project.add_master(user)
+ new_project.add_maintainer(user)
end
it 'filters by created_after' do
diff --git a/spec/finders/move_to_project_finder_spec.rb b/spec/finders/move_to_project_finder_spec.rb
index 74639d4147f..1511cb0e04c 100644
--- a/spec/finders/move_to_project_finder_spec.rb
+++ b/spec/finders/move_to_project_finder_spec.rb
@@ -8,7 +8,7 @@ describe MoveToProjectFinder do
let(:guest_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
- let(:master_project) { create(:project) }
+ let(:maintainer_project) { create(:project) }
subject { described_class.new(user) }
@@ -23,9 +23,9 @@ describe MoveToProjectFinder do
it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
- expect(subject.execute(project).to_a).to eq([master_project, developer_project, reporter_project])
+ expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project, reporter_project])
end
it 'does not include the source project' do
@@ -45,7 +45,7 @@ describe MoveToProjectFinder do
it 'does not return projects for which issues are disabled' do
reporter_project.add_reporter(user)
- reporter_project.update_attributes(issues_enabled: false)
+ reporter_project.update(issues_enabled: false)
other_reporter_project = create(:project)
other_reporter_project.add_reporter(user)
@@ -57,9 +57,9 @@ describe MoveToProjectFinder do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
- expect(subject.execute(project).to_a).to eq([master_project, developer_project])
+ expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project])
end
it 'returns projects after the given offset id' do
@@ -67,9 +67,9 @@ describe MoveToProjectFinder do
reporter_project.add_reporter(user)
developer_project.add_developer(user)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
- expect(subject.execute(project, search: nil, offset_id: master_project.id).to_a).to eq([developer_project, reporter_project])
+ expect(subject.execute(project, search: nil, offset_id: maintainer_project.id).to_a).to eq([developer_project, reporter_project])
expect(subject.execute(project, search: nil, offset_id: developer_project.id).to_a).to eq([reporter_project])
expect(subject.execute(project, search: nil, offset_id: reporter_project.id).to_a).to be_empty
end
@@ -84,10 +84,10 @@ describe MoveToProjectFinder do
it 'returns projects matching a search query' do
foo_project = create(:project)
- foo_project.add_master(user)
+ foo_project.add_maintainer(user)
wadus_project = create(:project, name: 'wadus')
- wadus_project.add_master(user)
+ wadus_project.add_maintainer(user)
expect(subject.execute(project).to_a).to eq([wadus_project, foo_project])
expect(subject.execute(project, search: 'wadus').to_a).to eq([wadus_project])
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 232f35c86f9..b776e9d856a 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -5,7 +5,7 @@ describe NotesFinder do
let(:project) { create(:project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index 00c551a1f65..ef7dd0cd4a8 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -35,7 +35,7 @@ describe PersonalProjectsFinder do
context 'external' do
before do
- current_user.update_attributes(external: true)
+ current_user.update(external: true)
end
it { is_expected.to eq([public_project, private_project]) }
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 0dfe6ba9c32..7931ad9b9f0 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -41,7 +41,7 @@ describe ProjectsFinder do
describe 'with private projects' do
before do
- private_project.add_master(user)
+ private_project.add_maintainer(user)
end
it { is_expected.to match_array([public_project, internal_project, private_project]) }
@@ -56,7 +56,7 @@ describe ProjectsFinder do
describe 'filter by visibility_level' do
before do
- private_project.add_master(user)
+ private_project.add_maintainer(user)
end
context 'private' do
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 6061021d3b0..9747b9402a7 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -5,76 +5,12 @@ describe TodosFinder do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
- let(:issue) { create(:issue, project: project) }
- let(:merge_request) { create(:merge_request, source_project: project) }
let(:finder) { described_class }
before do
group.add_developer(user)
end
- describe '#execute' do
- context 'visibility' do
- let(:private_group_access) { create(:group, :private) }
- let(:private_group_hidden) { create(:group, :private) }
- let(:public_project) { create(:project, :public) }
- let(:private_project_hidden) { create(:project) }
- let(:public_group) { create(:group) }
-
- let!(:todo1) { create(:todo, user: user, project: project, group: nil) }
- let!(:todo2) { create(:todo, user: user, project: public_project, group: nil) }
- let!(:todo3) { create(:todo, user: user, project: private_project_hidden, group: nil) }
- let!(:todo4) { create(:todo, user: user, project: nil, group: group) }
- let!(:todo5) { create(:todo, user: user, project: nil, group: private_group_access) }
- let!(:todo6) { create(:todo, user: user, project: nil, group: private_group_hidden) }
- let!(:todo7) { create(:todo, user: user, project: nil, group: public_group) }
-
- before do
- private_group_access.add_developer(user)
- end
-
- it 'returns only todos with a target a user has access to' do
- todos = finder.new(user).execute
-
- expect(todos).to match_array([todo1, todo2, todo4, todo5, todo7])
- end
- end
-
- context 'filtering' do
- let!(:todo1) { create(:todo, user: user, project: project, target: issue) }
- let!(:todo2) { create(:todo, user: user, group: group, target: merge_request) }
-
- it 'returns correct todos when filtered by a project' do
- todos = finder.new(user, { project_id: project.id }).execute
-
- expect(todos).to match_array([todo1])
- end
-
- it 'returns correct todos when filtered by a group' do
- todos = finder.new(user, { group_id: group.id }).execute
-
- expect(todos).to match_array([todo1, todo2])
- end
-
- it 'returns correct todos when filtered by a type' do
- todos = finder.new(user, { type: 'Issue' }).execute
-
- expect(todos).to match_array([todo1])
- end
-
- context 'with subgroups', :nested_groups do
- let(:subgroup) { create(:group, parent: group) }
- let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) }
-
- it 'returns todos from subgroups when filtered by a group' do
- todos = finder.new(user, { group_id: group.id }).execute
-
- expect(todos).to match_array([todo1, todo2, todo3])
- end
- end
- end
- end
-
describe '#sort' do
context 'by date' do
let!(:todo1) { create(:todo, user: user, project: project) }
diff --git a/spec/fixtures/aosp_manifest.xml b/spec/fixtures/aosp_manifest.xml
new file mode 100644
index 00000000000..cfd0094b735
--- /dev/null
+++ b/spec/fixtures/aosp_manifest.xml
@@ -0,0 +1,685 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+
+ <remote name="aosp"
+ fetch=".."
+ review="https://android-review.googlesource.com/" />
+ <default revision="master"
+ remote="aosp"
+ sync-j="4" />
+
+ <project path="build/make" name="platform/build" groups="pdk" >
+ <copyfile src="core/root.mk" dest="Makefile" />
+ <linkfile src="CleanSpec.mk" dest="build/CleanSpec.mk" />
+ <linkfile src="buildspec.mk.default" dest="build/buildspec.mk.default" />
+ <linkfile src="core" dest="build/core" />
+ <linkfile src="envsetup.sh" dest="build/envsetup.sh" />
+ <linkfile src="target" dest="build/target" />
+ <linkfile src="tools" dest="build/tools" />
+ </project>
+ <project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" />
+ <project path="build/kati" name="platform/build/kati" groups="pdk,tradefed" />
+ <project path="build/soong" name="platform/build/soong" groups="pdk,tradefed" >
+ <linkfile src="root.bp" dest="Android.bp" />
+ <linkfile src="bootstrap.bash" dest="bootstrap.bash" />
+ </project>
+ <project path="art" name="platform/art" groups="pdk" />
+ <project path="bionic" name="platform/bionic" groups="pdk" />
+ <project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />
+ <project path="compatibility/cdd" name="platform/compatibility/cdd" groups="pdk" />
+ <project path="cts" name="platform/cts" groups="cts,pdk-cw-fs,pdk-fs" />
+ <project path="dalvik" name="platform/dalvik" groups="pdk-cw-fs,pdk-fs" />
+ <project path="developers/build" name="platform/developers/build" groups="developers" />
+ <project path="developers/demos" name="platform/developers/demos" groups="developers" />
+ <project path="developers/samples/android" name="platform/developers/samples/android" groups="developers" />
+ <project path="development" name="platform/development" groups="developers,pdk-cw-fs,pdk-fs" />
+ <project path="device/asus/fugu" name="device/asus/fugu" groups="device,fugu,broadcom_pdk" />
+ <project path="device/asus/fugu-kernel" name="device/asus/fugu-kernel" groups="device,fugu,broadcom_pdk" clone-depth="1" />
+ <project path="device/common" name="device/common" groups="pdk-cw-fs,pdk" />
+ <project path="device/generic/arm64" name="device/generic/arm64" groups="pdk" />
+ <project path="device/generic/armv7-a-neon" name="device/generic/armv7-a-neon" groups="pdk" />
+ <project path="device/generic/car" name="device/generic/car" groups="pdk" />
+ <project path="device/generic/common" name="device/generic/common" groups="pdk" />
+ <project path="device/generic/goldfish" name="device/generic/goldfish" groups="pdk" />
+ <project path="device/generic/goldfish-opengl" name="device/generic/goldfish-opengl" groups="pdk" />
+ <project path="device/generic/mini-emulator-arm64" name="device/generic/mini-emulator-arm64" groups="pdk" />
+ <project path="device/generic/mini-emulator-armv7-a-neon" name="device/generic/mini-emulator-armv7-a-neon" groups="pdk" />
+ <project path="device/generic/mini-emulator-x86" name="device/generic/mini-emulator-x86" groups="pdk" />
+ <project path="device/generic/mini-emulator-x86_64" name="device/generic/mini-emulator-x86_64" groups="pdk" />
+ <project path="device/generic/qemu" name="device/generic/qemu" groups="pdk" />
+ <project path="device/generic/uml" name="device/generic/uml" groups="device,pdk" />
+ <project path="device/generic/x86" name="device/generic/x86" groups="pdk" />
+ <project path="device/generic/x86_64" name="device/generic/x86_64" groups="pdk" />
+ <project path="device/google/accessory/arduino" name="device/google/accessory/arduino" groups="device,pdk" />
+ <project path="device/google/accessory/demokit" name="device/google/accessory/demokit" groups="device,pdk" />
+ <project path="device/google/atv" name="device/google/atv" groups="device,broadcom_pdk,generic_fs,pdk" />
+ <project path="device/google/contexthub" name="device/google/contexthub" groups="device,marlin,pdk" />
+ <project path="device/google/cuttlefish" name="device/google/cuttlefish" groups="device" />
+ <project path="device/google/cuttlefish_common" name="device/google/cuttlefish_common" groups="device" />
+ <project path="device/google/cuttlefish_kernel" name="device/google/cuttlefish_kernel" groups="device" clone-depth="1" />
+ <project path="device/google/dragon" name="device/google/dragon" groups="device,dragon" />
+ <project path="device/google/dragon-kernel" name="device/google/dragon-kernel" groups="device,dragon" clone-depth="1" />
+ <project path="device/google/marlin" name="device/google/marlin" groups="device,marlin,pdk" />
+ <project path="device/google/marlin-kernel" name="device/google/marlin-kernel" groups="device,marlin,pdk" clone-depth="1" />
+ <project path="device/google/muskie" name="device/google/muskie" groups="device,muskie" />
+ <project path="device/google/taimen" name="device/google/taimen" groups="device,taimen" />
+ <project path="device/google/vrservices" name="device/google/vrservices" groups="pdk" clone-depth="1" />
+ <project path="device/google/wahoo" name="device/google/wahoo" groups="device,wahoo" />
+ <project path="device/google/wahoo-kernel" name="device/google/wahoo-kernel" groups="device,wahoo" clone-depth="1" />
+ <project path="device/huawei/angler" name="device/huawei/angler" groups="device,angler,broadcom_pdk" />
+ <project path="device/huawei/angler-kernel" name="device/huawei/angler-kernel" groups="device,angler,broadcom_pdk" clone-depth="1" />
+ <project path="device/lge/bullhead" name="device/lge/bullhead" groups="device,bullhead" />
+ <project path="device/lge/bullhead-kernel" name="device/lge/bullhead-kernel" groups="device,bullhead" clone-depth="1" />
+ <project path="device/linaro/bootloader/arm-trusted-firmware" name="device/linaro/bootloader/arm-trusted-firmware" />
+ <project path="device/linaro/bootloader/edk2" name="device/linaro/bootloader/edk2" />
+ <project path="device/linaro/bootloader/OpenPlatformPkg" name="device/linaro/bootloader/OpenPlatformPkg" />
+ <project path="device/linaro/hikey" name="device/linaro/hikey" groups="device,hikey,pdk" />
+ <project path="device/linaro/hikey-kernel" name="device/linaro/hikey-kernel" groups="device,hikey,pdk" clone-depth="1" />
+ <project path="device/sample" name="device/sample" groups="pdk" />
+ <project path="external/aac" name="platform/external/aac" groups="pdk" />
+ <project path="external/abi-compliance-checker" name="platform/external/abi-compliance-checker" groups="pdk" />
+ <project path="external/abi-dumper" name="platform/external/abi-dumper" groups="pdk" />
+ <project path="external/adt-infra" name="platform/external/adt-infra" groups="adt-infra,notdefault,pdk-fs" />
+ <project path="external/android-clat" name="platform/external/android-clat" groups="pdk" />
+ <project path="external/androidplot" name="platform/external/androidplot" groups="pdk" />
+ <project path="external/annotation-tools" name="platform/external/annotation-tools" groups="pdk" />
+ <project path="external/ant-glob" name="platform/external/ant-glob" groups="pdk" />
+ <project path="external/antlr" name="platform/external/antlr" groups="pdk" />
+ <project path="external/apache-commons-math" name="platform/external/apache-commons-math" groups="pdk" />
+ <project path="external/apache-harmony" name="platform/external/apache-harmony" groups="pdk" />
+ <project path="external/apache-http" name="platform/external/apache-http" groups="pdk" />
+ <project path="external/apache-xml" name="platform/external/apache-xml" groups="pdk" />
+ <project path="external/archive-patcher" name="platform/external/archive-patcher" groups="pdk" />
+ <project path="external/arm-neon-tests" name="platform/external/arm-neon-tests" groups="vendor" />
+ <project path="external/autotest" name="platform/external/autotest" groups="pdk-fs" />
+ <project path="external/avb" name="platform/external/avb" groups="pdk" />
+ <project path="external/bart" name="platform/external/bart" groups="pdk" />
+ <project path="external/blktrace" name="platform/external/blktrace" groups="pdk" />
+ <project path="external/boringssl" name="platform/external/boringssl" groups="pdk" />
+ <project path="external/bouncycastle" name="platform/external/bouncycastle" groups="pdk" />
+ <project path="external/brotli" name="platform/external/brotli" groups="pdk" />
+ <project path="external/bsdiff" name="platform/external/bsdiff" groups="pdk" />
+ <project path="external/bzip2" name="platform/external/bzip2" groups="pdk" />
+ <project path="external/caliper" name="platform/external/caliper" groups="pdk" />
+ <project path="external/cblas" name="platform/external/cblas" groups="pdk" />
+ <project path="external/chromium-libpac" name="platform/external/chromium-libpac" groups="pdk" />
+ <project path="external/chromium-trace" name="platform/external/chromium-trace" groups="pdk" />
+ <project path="external/chromium-webview" name="platform/external/chromium-webview" groups="pdk" clone-depth="1" />
+ <project path="external/clang" name="platform/external/clang" groups="pdk" />
+ <project path="external/cldr" name="platform/external/cldr" groups="pdk" />
+ <project path="external/cmockery" name="platform/external/cmockery" groups="pdk" />
+ <project path="external/cn-cbor" name="platform/external/cn-cbor" groups="pdk" />
+ <project path="external/compiler-rt" name="platform/external/compiler-rt" groups="pdk" />
+ <project path="external/conscrypt" name="platform/external/conscrypt" groups="pdk" />
+ <project path="external/crcalc" name="platform/external/crcalc" groups="pdk" />
+ <project path="external/cros/system_api" name="platform/external/cros/system_api" groups="pdk" />
+ <project path="external/curl" name="platform/external/curl" groups="pdk" />
+ <project path="external/dagger2" name="platform/external/dagger2" groups="pdk" />
+ <project path="external/deqp" name="platform/external/deqp" groups="pdk-fs" />
+ <project path="external/desugar" name="platform/external/desugar" groups="pdk" />
+ <project path="external/devlib" name="platform/external/devlib" groups="pdk" />
+ <project path="external/dexmaker" name="platform/external/dexmaker" groups="pdk" />
+ <project path="external/dhcpcd-6.8.2" name="platform/external/dhcpcd-6.8.2" groups="pdk" />
+ <project path="external/dlmalloc" name="platform/external/dlmalloc" groups="pdk" />
+ <project path="external/dng_sdk" name="platform/external/dng_sdk" groups="pdk" />
+ <project path="external/dnsmasq" name="platform/external/dnsmasq" groups="pdk" />
+ <project path="external/doclava" name="platform/external/doclava" groups="pdk" />
+ <project path="external/dokka" name="platform/external/dokka" groups="pdk" />
+ <project path="external/drm_hwcomposer" name="platform/external/drm_hwcomposer" groups="drm_hwcomposer,pdk-fs" />
+ <project path="external/droiddriver" name="platform/external/droiddriver" groups="pdk" />
+ <project path="external/drrickorang" name="platform/external/drrickorang" groups="pdk" />
+ <project path="external/dtc" name="platform/external/dtc" groups="pdk"/>
+ <project path="external/e2fsprogs" name="platform/external/e2fsprogs" groups="pdk" />
+ <project path="external/easymock" name="platform/external/easymock" groups="pdk" />
+ <project path="external/eigen" name="platform/external/eigen" groups="pdk" />
+ <project path="external/elfutils" name="platform/external/elfutils" groups="pdk" />
+ <project path="external/emma" name="platform/external/emma" groups="pdk" />
+ <project path="external/error_prone" name="platform/external/error_prone" groups="pdk" />
+ <project path="external/esd" name="platform/external/esd" groups="pdk" />
+ <project path="external/expat" name="platform/external/expat" groups="pdk" />
+ <project path="external/eyes-free" name="platform/external/eyes-free" groups="pdk" />
+ <project path="external/f2fs-tools" name="platform/external/f2fs-tools" groups="pdk" />
+ <project path="external/fdlibm" name="platform/external/fdlibm" groups="pdk" />
+ <project path="external/fec" name="platform/external/fec" groups="pdk" />
+ <project path="external/flac" name="platform/external/flac" groups="pdk" />
+ <project path="external/flatbuffers" name="platform/external/flatbuffers" groups="pdk" />
+ <project path="external/fonttools" name="platform/external/fonttools" groups="pdk" />
+ <project path="external/freetype" name="platform/external/freetype" groups="pdk" />
+ <project path="external/fsck_msdos" name="platform/external/fsck_msdos" groups="pdk" />
+ <project path="external/gemmlowp" name="platform/external/gemmlowp" groups="pdk" />
+ <project path="external/gflags" name="platform/external/gflags" groups="pdk" />
+ <project path="external/giflib" name="platform/external/giflib" groups="pdk,qcom_msm8x26" />
+ <project path="external/glide" name="platform/external/glide" groups="pdk" />
+ <project path="external/golang-protobuf" name="platform/external/golang-protobuf" groups="pdk" />
+ <project path="external/google-benchmark" name="platform/external/google-benchmark" groups="pdk" />
+ <project path="external/google-breakpad" name="platform/external/google-breakpad" groups="pdk-fs" />
+ <project path="external/google-fonts/carrois-gothic-sc" name="platform/external/google-fonts/carrois-gothic-sc" groups="pdk" />
+ <project path="external/google-fonts/coming-soon" name="platform/external/google-fonts/coming-soon" groups="pdk" />
+ <project path="external/google-fonts/cutive-mono" name="platform/external/google-fonts/cutive-mono" groups="pdk" />
+ <project path="external/google-fonts/dancing-script" name="platform/external/google-fonts/dancing-script" groups="pdk" />
+ <project path="external/google-styleguide" name="platform/external/google-styleguide" groups="pdk" />
+ <project path="external/google-tv-pairing-protocol" name="platform/external/google-tv-pairing-protocol" groups="pdk" />
+ <project path="external/googletest" name="platform/external/googletest" groups="pdk" />
+ <project path="external/gptfdisk" name="platform/external/gptfdisk" groups="pdk" />
+ <project path="external/guava" name="platform/external/guava" groups="pdk" />
+ <project path="external/guice" name="platform/external/guice" groups="pdk" />
+ <project path="external/hamcrest" name="platform/external/hamcrest" groups="pdk" />
+ <project path="external/harfbuzz_ng" name="platform/external/harfbuzz_ng" groups="pdk,qcom_msm8x26" />
+ <project path="external/hyphenation-patterns" name="platform/external/hyphenation-patterns" groups="pdk" />
+ <project path="external/icu" name="platform/external/icu" groups="pdk" />
+ <project path="external/ImageMagick" name="platform/external/ImageMagick" groups="pdk" />
+ <project path="external/ims" name="platform/external/ims" groups="pdk" />
+ <project path="external/iproute2" name="platform/external/iproute2" groups="pdk" />
+ <project path="external/ipsec-tools" name="platform/external/ipsec-tools" groups="pdk" />
+ <project path="external/iptables" name="platform/external/iptables" groups="pdk" />
+ <project path="external/iputils" name="platform/external/iputils" groups="pdk" />
+ <project path="external/iw" name="platform/external/iw" groups="pdk" />
+ <project path="external/jacoco" name="platform/external/jacoco" groups="pdk" />
+ <project path="external/jarjar" name="platform/external/jarjar" groups="pdk" />
+ <project path="external/javaparser" name="platform/external/javaparser" groups="pdk" />
+ <project path="external/javasqlite" name="platform/external/javasqlite" groups="pdk" />
+ <project path="external/javassist" name="platform/external/javassist" groups="pdk" />
+ <project path="external/jcommander" name="platform/external/jcommander" groups="pdk" />
+ <project path="external/jdiff" name="platform/external/jdiff" groups="pdk" />
+ <project path="external/jemalloc" name="platform/external/jemalloc" groups="pdk" />
+ <project path="external/jline" name="platform/external/jline" groups="pdk,tradefed,pdk-fs" />
+ <project path="external/jmdns" name="platform/external/jmdns" groups="pdk" />
+ <project path="external/jsilver" name="platform/external/jsilver" groups="pdk" />
+ <project path="external/jsmn" name="platform/external/jsmn" groups="pdk" />
+ <project path="external/jsoncpp" name="platform/external/jsoncpp" groups="pdk" />
+ <project path="external/jsr305" name="platform/external/jsr305" groups="pdk" />
+ <project path="external/jsr330" name="platform/external/jsr330" groups="pdk" />
+ <project path="external/junit" name="platform/external/junit" groups="pdk" />
+ <project path="external/junit-params" name="platform/external/junit-params" groups="pdk" />
+ <project path="external/kernel-headers" name="platform/external/kernel-headers" groups="pdk" />
+ <project path="external/kmod" name="platform/external/kmod" groups="pdk" />
+ <project path="external/kotlinc" name="platform/external/kotlinc" groups="pdk" />
+ <project path="external/ksoap2" name="platform/external/ksoap2" groups="pdk" />
+ <project path="external/libavc" name="platform/external/libavc" groups="pdk" />
+ <project path="external/libbackup" name="platform/external/libbackup" groups="pdk" />
+ <project path="external/libbrillo" name="platform/external/libbrillo" groups="pdk" />
+ <project path="external/libcap" name="platform/external/libcap" groups="pdk" />
+ <project path="external/libcap-ng" name="platform/external/libcap-ng" groups="pdk" />
+ <project path="external/libchrome" name="platform/external/libchrome" groups="pdk" />
+ <project path="external/libconstrainedcrypto" name="platform/external/libconstrainedcrypto" groups="pdk" />
+ <project path="external/libcups" name="platform/external/libcups" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/libcxx" name="platform/external/libcxx" groups="pdk" />
+ <project path="external/libcxxabi" name="platform/external/libcxxabi" groups="pdk" />
+ <project path="external/libdaemon" name="platform/external/libdaemon" groups="pdk" />
+ <project path="external/libdivsufsort" name="platform/external/libdivsufsort" groups="pdk" />
+ <project path="external/libdrm" name="platform/external/libdrm" groups="pdk" />
+ <project path="external/libedit" name="platform/external/libedit" groups="pdk" />
+ <project path="external/libese" name="platform/external/libese" groups="pdk" />
+ <project path="external/libevent" name="platform/external/libevent" groups="pdk" />
+ <project path="external/libexif" name="platform/external/libexif" groups="pdk" />
+ <project path="external/libgsm" name="platform/external/libgsm" groups="pdk" />
+ <project path="external/libhevc" name="platform/external/libhevc" groups="pdk" />
+ <project path="external/libjpeg-turbo" name="platform/external/libjpeg-turbo" groups="pdk" />
+ <project path="external/libldac" name="platform/external/libldac" groups="pdk" />
+ <project path="external/libmicrohttpd" name="platform/external/libmicrohttpd" groups="pdk" />
+ <project path="external/libmpeg2" name="platform/external/libmpeg2" groups="pdk" />
+ <project path="external/libmtp" name="platform/external/libmtp" groups="pdk" />
+ <project path="external/libnetfilter_conntrack" name="platform/external/libnetfilter_conntrack" groups="pdk" />
+ <project path="external/libnfnetlink" name="platform/external/libnfnetlink" groups="pdk" />
+ <project path="external/libnl" name="platform/external/libnl" groups="pdk" />
+ <project path="external/libogg" name="platform/external/libogg" groups="pdk" />
+ <project path="external/libopus" name="platform/external/libopus" groups="pdk" />
+ <project path="external/libpcap" name="platform/external/libpcap" groups="pdk" />
+ <project path="external/libphonenumber" name="platform/external/libphonenumber" groups="pdk" />
+ <project path="external/libpng" name="platform/external/libpng" groups="pdk" />
+ <project path="external/libtextclassifier" name="platform/external/libtextclassifier" groups="pdk" />
+ <project path="external/libunwind" name="platform/external/libunwind" groups="pdk" />
+ <project path="external/libunwind_llvm" name="platform/external/libunwind_llvm" groups="pdk" />
+ <project path="external/libusb" name="platform/external/libusb" groups="pdk" />
+ <project path="external/libusb-compat" name="platform/external/libusb-compat" groups="pdk" />
+ <project path="external/libvncserver" name="platform/external/libvncserver" groups="pdk" />
+ <project path="external/libvorbis" name="platform/external/libvorbis" groups="pdk" />
+ <project path="external/libvpx" name="platform/external/libvpx" groups="pdk" />
+ <project path="external/libvterm" name="platform/external/libvterm" groups="pdk" />
+ <project path="external/libxcam" name="platform/external/libxcam" groups="pdk" />
+ <project path="external/libxml2" name="platform/external/libxml2" groups="pdk,libxml2" />
+ <project path="external/libyuv" name="platform/external/libyuv" groups="pdk,libyuv" />
+ <project path="external/linux-kselftest" name="platform/external/linux-kselftest" groups="vts,pdk" />
+ <project path="external/lisa" name="platform/external/lisa" groups="pdk" />
+ <project path="external/llvm" name="platform/external/llvm" groups="pdk" />
+ <project path="external/lmfit" name="platform/external/lmfit" groups="pdk" />
+ <project path="external/ltp" name="platform/external/ltp" groups="vts,pdk" />
+ <project path="external/lz4" name="platform/external/lz4" groups="pdk" />
+ <project path="external/lzma" name="platform/external/lzma" groups="pdk" />
+ <project path="external/markdown" name="platform/external/markdown" groups="pdk" />
+ <project path="external/mdnsresponder" name="platform/external/mdnsresponder" groups="pdk" />
+ <project path="external/mesa3d" name="platform/external/mesa3d" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/Microsoft-GSL" name="platform/external/Microsoft-GSL" groups="pdk" />
+ <project path="external/minijail" name="platform/external/minijail" groups="pdk" />
+ <project path="external/mksh" name="platform/external/mksh" groups="pdk" />
+ <project path="external/mmc-utils" name="platform/external/mmc-utils" groups="pdk" />
+ <project path="external/mockftpserver" name="platform/external/mockftpserver" groups="pdk" />
+ <project path="external/mockito" name="platform/external/mockito" groups="pdk" />
+ <project path="external/mockwebserver" name="platform/external/mockwebserver" groups="pdk" />
+ <project path="external/modp_b64" name="platform/external/modp_b64" groups="pdk" />
+ <project path="external/mp4parser" name="platform/external/mp4parser" groups="pdk" />
+ <project path="external/mtpd" name="platform/external/mtpd" groups="pdk" />
+ <project path="external/nanohttpd" name="platform/external/nanohttpd" groups="pdk" />
+ <project path="external/nanopb-c" name="platform/external/nanopb-c" groups="pdk" />
+ <project path="external/naver-fonts" name="platform/external/naver-fonts" groups="pdk" />
+ <project path="external/neven" name="platform/external/neven" groups="pdk" />
+ <project path="external/nfacct" name="platform/external/nfacct" groups="pdk" />
+ <project path="external/nist-pkits" name="platform/external/nist-pkits" groups="pdk" />
+ <project path="external/nist-sip" name="platform/external/nist-sip" groups="pdk" />
+ <project path="external/noto-fonts" name="platform/external/noto-fonts" groups="pdk" />
+ <project path="external/oauth" name="platform/external/oauth" groups="pdk" />
+ <project path="external/objenesis" name="platform/external/objenesis" groups="pdk" />
+ <project path="external/oj-libjdwp" name="platform/external/oj-libjdwp" groups="pdk" />
+ <project path="external/okhttp" name="platform/external/okhttp" groups="pdk" />
+ <project path="external/one-true-awk" name="platform/external/one-true-awk" groups="pdk" />
+ <project path="external/opencv" name="platform/external/opencv" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/owasp/sanitizer" name="platform/external/owasp/sanitizer" groups="pdk" />
+ <project path="external/parameter-framework" name="platform/external/parameter-framework" groups="pdk" />
+ <project path="external/pcre" name="platform/external/pcre" groups="pdk" />
+ <project path="external/pdfium" name="platform/external/pdfium" groups="pdk" />
+ <project path="external/perf_data_converter" name="platform/external/perf_data_converter" groups="pdk" />
+ <project path="external/perfetto" name="platform/external/perfetto" groups="pdk" />
+ <project path="external/piex" name="platform/external/piex" groups="pdk" />
+ <project path="external/ply" name="platform/external/ply" groups="pdk" />
+ <project path="external/ppp" name="platform/external/ppp" groups="pdk" />
+ <project path="external/proguard" name="platform/external/proguard" groups="pdk" />
+ <project path="external/protobuf" name="platform/external/protobuf" groups="pdk" />
+ <project path="external/puffin" name="platform/external/puffin" groups="pdk" />
+ <project path="external/python/appdirs" name="platform/external/python/appdirs" groups="vts,pdk" />
+ <project path="external/python/cachetools" name="platform/external/python/cachetools" groups="vts,pdk" />
+ <project path="external/python/cpython2" name="platform/external/python/cpython2" groups="pdk" />
+ <project path="external/python/cpython3" name="platform/external/python/cpython3" groups="pdk" />
+ <project path="external/python/dateutil" name="platform/external/python/dateutil" groups="pdk" />
+ <project path="external/python/dill" name="platform/external/python/dill" groups="vts,pdk" />
+ <project path="external/python/enum" name="platform/external/python/enum" groups="vts,pdk" />
+ <project path="external/python/enum34" name="platform/external/python/enum34" groups="vts,pdk" />
+ <project path="external/python/future" name="platform/external/python/future" groups="vts,pdk" />
+ <project path="external/python/futures" name="platform/external/python/futures" groups="vts,pdk" />
+ <project path="external/python/gapic-google-cloud-pubsub-v1" name="platform/external/python/gapic-google-cloud-pubsub-v1" groups="vts,pdk" />
+ <project path="external/python/google-api-python-client" name="platform/external/python/google-api-python-client" groups="vts,pdk" />
+ <project path="external/python/google-auth" name="platform/external/python/google-auth" groups="vts,pdk" />
+ <project path="external/python/google-auth-httplib2" name="platform/external/python/google-auth-httplib2" groups="vts,pdk" />
+ <project path="external/python/google-cloud-core" name="platform/external/python/google-cloud-core" groups="vts,pdk" />
+ <project path="external/python/google-cloud-pubsub" name="platform/external/python/google-cloud-pubsub" groups="vts,pdk" />
+ <project path="external/python/google-gax" name="platform/external/python/google-gax" groups="vts,pdk" />
+ <project path="external/python/googleapis" name="platform/external/python/googleapis" groups="vts,pdk" />
+ <project path="external/python/grpc-google-iam-v1" name="platform/external/python/grpc-google-iam-v1" groups="vts,pdk" />
+ <project path="external/python/grpcio" name="platform/external/python/grpcio" groups="vts,pdk" />
+ <project path="external/python/httplib2" name="platform/external/python/httplib2" groups="vts,pdk" />
+ <project path="external/python/matplotlib" name="platform/external/python/matplotlib" groups="vts,pdk" />
+ <project path="external/python/numpy" name="platform/external/python/numpy" groups="vts,pdk" />
+ <project path="external/python/oauth2client" name="platform/external/python/oauth2client" groups="vts,pdk" />
+ <project path="external/python/olefile" name="platform/external/python/olefile" groups="vts,pdk" />
+ <project path="external/python/packaging" name="platform/external/python/packaging" groups="vts,pdk" />
+ <project path="external/python/parse" name="platform/external/python/parse" groups="vts,pdk" />
+ <project path="external/python/Pillow" name="platform/external/python/Pillow" groups="vts,pdk" />
+ <project path="external/python/ply" name="platform/external/python/ply" groups="vts,pdk" />
+ <project path="external/python/proto-google-cloud-pubsub-v1" name="platform/external/python/proto-google-cloud-pubsub-v1" groups="vts,pdk" />
+ <project path="external/python/protobuf" name="platform/external/python/protobuf" groups="vts,pdk" />
+ <project path="external/python/pyasn1" name="platform/external/python/pyasn1" groups="vts,pdk" />
+ <project path="external/python/pyasn1-modules" name="platform/external/python/pyasn1-modules" groups="vts,pdk" />
+ <project path="external/python/pyparsing" name="platform/external/python/pyparsing" groups="vts,pdk" />
+ <project path="external/python/requests" name="platform/external/python/requests" groups="vts,pdk" />
+ <project path="external/python/rsa" name="platform/external/python/rsa" groups="vts,pdk" />
+ <project path="external/python/scipy" name="platform/external/python/scipy" groups="vts,pdk" />
+ <project path="external/python/setuptools" name="platform/external/python/setuptools" groups="vts,pdk" />
+ <project path="external/python/six" name="platform/external/python/six" groups="vts,pdk" />
+ <project path="external/python/uritemplates" name="platform/external/python/uritemplates" groups="vts,pdk" />
+ <project path="external/replicaisland" name="platform/external/replicaisland" groups="pdk" />
+ <project path="external/rmi4utils" name="platform/external/rmi4utils" groups="pdk" />
+ <project path="external/robolectric" name="platform/external/robolectric" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/roboto-fonts" name="platform/external/roboto-fonts" groups="pdk" />
+ <project path="external/rootdev" name="platform/external/rootdev" groups="pdk" />
+ <project path="external/safe-iop" name="platform/external/safe-iop" groups="pdk" />
+ <project path="external/scapy" name="platform/external/scapy" groups="pdk-fs" />
+ <project path="external/scrypt" name="platform/external/scrypt" groups="pdk" />
+ <project path="external/seccomp-tests" name="platform/external/seccomp-tests" groups="pdk" />
+ <project path="external/selinux" name="platform/external/selinux" groups="pdk" />
+ <project path="external/sfntly" name="platform/external/sfntly" groups="pdk,qcom_msm8x26" />
+ <project path="external/shaderc/spirv-headers" name="platform/external/shaderc/spirv-headers" groups="pdk" />
+ <project path="external/shflags" name="platform/external/shflags" groups="pdk" />
+ <project path="external/skia" name="platform/external/skia" groups="pdk,qcom_msm8x26" />
+ <project path="external/sl4a" name="platform/external/sl4a" groups="pdk" />
+ <project path="external/slf4j" name="platform/external/slf4j" groups="pdk" />
+ <project path="external/smali" name="platform/external/smali" groups="pdk" />
+ <project path="external/snakeyaml" name="platform/external/snakeyaml" groups="pdk" />
+ <project path="external/sonic" name="platform/external/sonic" groups="pdk" />
+ <project path="external/sonivox" name="platform/external/sonivox" groups="pdk" />
+ <project path="external/speex" name="platform/external/speex" groups="pdk" />
+ <project path="external/spirv-llvm" name="platform/external/spirv-llvm" groups="pdk" />
+ <project path="external/sqlite" name="platform/external/sqlite" groups="pdk" />
+ <project path="external/squashfs-tools" name="platform/external/squashfs-tools" groups="pdk" />
+ <project path="external/strace" name="platform/external/strace" groups="pdk" />
+ <project path="external/stressapptest" name="platform/external/stressapptest" groups="pdk" />
+ <project path="external/subsampling-scale-image-view" name="platform/external/subsampling-scale-image-view" clone-depth="1" />
+ <project path="external/swiftshader" name="platform/external/swiftshader" groups="pdk" />
+ <project path="external/syslinux" name="platform/external/syslinux" groups="pdk" />
+ <project path="external/tagsoup" name="platform/external/tagsoup" groups="pdk" />
+ <project path="external/tcpdump" name="platform/external/tcpdump" groups="pdk" />
+ <project path="external/tensorflow" name="platform/external/tensorflow" groups="pdk" />
+ <project path="external/testng" name="platform/external/testng" groups="pdk" />
+ <project path="external/tinyalsa" name="platform/external/tinyalsa" groups="pdk" />
+ <project path="external/tinycompress" name="platform/external/tinycompress" groups="pdk" />
+ <project path="external/tinyxml" name="platform/external/tinyxml" groups="pdk" />
+ <project path="external/tinyxml2" name="platform/external/tinyxml2" groups="pdk" />
+ <project path="external/toolchain-utils" name="platform/external/toolchain-utils" />
+ <project path="external/toybox" name="platform/external/toybox" groups="pdk" />
+ <project path="external/tpm2" name="platform/external/tpm2" groups="pdk" />
+ <project path="external/trappy" name="platform/external/trappy" groups="pdk" />
+ <project path="external/tremolo" name="platform/external/tremolo" groups="pdk" />
+ <project path="external/turbine" name="platform/external/turbine" groups="pdk" />
+ <project path="external/unicode" name="platform/external/unicode" groups="pdk" />
+ <project path="external/universal-tween-engine" name="platform/external/universal-tween-engine" />
+ <project path="external/v4l2_codec2" name="platform/external/v4l2_codec2" groups="pdk" />
+ <project path="external/v8" name="platform/external/v8" groups="pdk" />
+ <project path="external/valgrind" name="platform/external/valgrind" groups="pdk" />
+ <project path="external/vboot_reference" name="platform/external/vboot_reference" groups="vboot,pdk-fs" />
+ <project path="external/vixl" name="platform/external/vixl" groups="pdk" />
+ <project path="external/vogar" name="platform/external/vogar" groups="pdk" />
+ <project path="external/volley" name="platform/external/volley" groups="pdk" />
+ <project path="external/vulkan-validation-layers" name="platform/external/vulkan-validation-layers" groups="pdk" />
+ <project path="external/walt" name="platform/external/walt" groups="pdk" />
+ <project path="external/webp" name="platform/external/webp" groups="pdk,qcom_msm8x26" />
+ <project path="external/webrtc" name="platform/external/webrtc" groups="pdk" />
+ <project path="external/webview_support_interfaces" name="platform/external/webview_support_interfaces" groups="pdk" />
+ <project path="external/wpa_supplicant_8" name="platform/external/wpa_supplicant_8" groups="pdk" />
+ <project path="external/wycheproof" name="platform/external/wycheproof" groups="pdk" />
+ <project path="external/x264" name="platform/external/x264" groups="pdk" />
+ <project path="external/xmlrpcpp" name="platform/external/xmlrpcpp" groups="pdk" />
+ <project path="external/xmp_toolkit" name="platform/external/xmp_toolkit" groups="pdk" />
+ <project path="external/xz-embedded" name="platform/external/xz-embedded" groups="pdk" />
+ <project path="external/zlib" name="platform/external/zlib" groups="pdk" />
+ <project path="external/zopfli" name="platform/external/zopfli" groups="pdk" />
+ <project path="external/zxing" name="platform/external/zxing" groups="pdk" />
+ <project path="frameworks/av" name="platform/frameworks/av" groups="pdk" />
+ <project path="frameworks/base" name="platform/frameworks/base" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/compile/libbcc" name="platform/frameworks/compile/libbcc" groups="pdk" />
+ <project path="frameworks/compile/mclinker" name="platform/frameworks/compile/mclinker" groups="pdk" />
+ <project path="frameworks/compile/slang" name="platform/frameworks/compile/slang" groups="pdk" />
+ <project path="frameworks/data-binding" name="platform/frameworks/data-binding" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/ex" name="platform/frameworks/ex" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/hardware/interfaces" name="platform/frameworks/hardware/interfaces" groups="pdk" />
+ <project path="frameworks/layoutlib" name="platform/frameworks/layoutlib" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/minikin" name="platform/frameworks/minikin" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/ml" name="platform/frameworks/ml" groups="pdk" />
+ <project path="frameworks/multidex" name="platform/frameworks/multidex" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/native" name="platform/frameworks/native" groups="pdk" />
+ <project path="frameworks/opt/bitmap" name="platform/frameworks/opt/bitmap" groups="pdk-fs" />
+ <project path="frameworks/opt/calendar" name="platform/frameworks/opt/calendar" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/car/services" name="platform/frameworks/opt/car/services" groups="pdk-fs" />
+ <project path="frameworks/opt/chips" name="platform/frameworks/opt/chips" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/colorpicker" name="platform/frameworks/opt/colorpicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/datetimepicker" name="platform/frameworks/opt/datetimepicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/inputmethodcommon" name="platform/frameworks/opt/inputmethodcommon" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/ethernet" name="platform/frameworks/opt/net/ethernet" groups="pdk-fs" />
+ <project path="frameworks/opt/net/ims" name="platform/frameworks/opt/net/ims" groups="frameworks_ims,pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/lowpan" name="platform/frameworks/opt/net/lowpan" groups="pdk-fs" />
+ <project path="frameworks/opt/net/voip" name="platform/frameworks/opt/net/voip" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/wifi" name="platform/frameworks/opt/net/wifi" groups="pdk" />
+ <project path="frameworks/opt/photoviewer" name="platform/frameworks/opt/photoviewer" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/setupwizard" name="platform/frameworks/opt/setupwizard" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/telephony" name="platform/frameworks/opt/telephony" groups="pdk" />
+ <project path="frameworks/opt/timezonepicker" name="platform/frameworks/opt/timezonepicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/vcard" name="platform/frameworks/opt/vcard" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/rs" name="platform/frameworks/rs" groups="pdk" />
+ <project path="frameworks/support" name="platform/frameworks/support" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/webview" name="platform/frameworks/webview" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/wilhelm" name="platform/frameworks/wilhelm" groups="pdk-cw-fs,pdk-fs" />
+ <project path="hardware/akm" name="platform/hardware/akm" groups="pdk" />
+ <project path="hardware/broadcom/libbt" name="platform/hardware/broadcom/libbt" groups="pdk" />
+ <project path="hardware/broadcom/wlan" name="platform/hardware/broadcom/wlan" groups="pdk,broadcom_wlan" />
+ <project path="hardware/google/apf" name="platform/hardware/google/apf" groups="pdk" />
+ <project path="hardware/google/easel" name="platform/hardware/google/easel" groups="pdk,easel" />
+ <project path="hardware/google/interfaces" name="platform/hardware/google/interfaces" groups="pdk" />
+ <project path="hardware/intel/audio_media" name="platform/hardware/intel/audio_media" groups="intel,pdk" />
+ <project path="hardware/intel/bootstub" name="platform/hardware/intel/bootstub" groups="intel,pdk" />
+ <project path="hardware/intel/common/libmix" name="platform/hardware/intel/common/libmix" groups="intel,pdk" />
+ <project path="hardware/intel/common/libstagefrighthw" name="platform/hardware/intel/common/libstagefrighthw" groups="intel,pdk" />
+ <project path="hardware/intel/common/libva" name="platform/hardware/intel/common/libva" groups="intel,pdk" />
+ <project path="hardware/intel/common/libwsbm" name="platform/hardware/intel/common/libwsbm" groups="intel,pdk" />
+ <project path="hardware/intel/common/omx-components" name="platform/hardware/intel/common/omx-components" groups="intel,pdk" />
+ <project path="hardware/intel/common/utils" name="platform/hardware/intel/common/utils" groups="intel,pdk" />
+ <project path="hardware/intel/common/wrs_omxil_core" name="platform/hardware/intel/common/wrs_omxil_core" groups="intel,pdk" />
+ <project path="hardware/intel/img/hwcomposer" name="platform/hardware/intel/img/hwcomposer" groups="intel,pdk" />
+ <project path="hardware/intel/img/psb_headers" name="platform/hardware/intel/img/psb_headers" groups="intel,pdk" />
+ <project path="hardware/intel/img/psb_video" name="platform/hardware/intel/img/psb_video" groups="intel,pdk" />
+ <project path="hardware/interfaces" name="platform/hardware/interfaces" groups="pdk" />
+ <project path="hardware/invensense" name="platform/hardware/invensense" groups="invensense,pdk" />
+ <project path="hardware/libhardware" name="platform/hardware/libhardware" groups="pdk" />
+ <project path="hardware/libhardware_legacy" name="platform/hardware/libhardware_legacy" groups="pdk" />
+ <project path="hardware/marvell/bt" name="platform/hardware/marvell/bt" groups="marvell_bt,pdk" />
+ <project path="hardware/nxp/nfc" name="platform/hardware/nxp/nfc" groups="pdk" />
+ <project path="hardware/nxp/secure_element" name="platform/hardware/nxp/secure_element" groups="pdk" />
+ <project path="hardware/qcom/audio" name="platform/hardware/qcom/audio" groups="qcom,qcom_audio,pdk" />
+ <project path="hardware/qcom/bootctrl" name="platform/hardware/qcom/bootctrl" groups="pdk" />
+ <project path="hardware/qcom/bt" name="platform/hardware/qcom/bt" groups="qcom,pdk" />
+ <project path="hardware/qcom/camera" name="platform/hardware/qcom/camera" groups="qcom_camera,pdk" />
+ <project path="hardware/qcom/data/ipacfg-mgr" name="platform/hardware/qcom/data/ipacfg-mgr" groups="qcom,pdk" />
+ <project path="hardware/qcom/display" name="platform/hardware/qcom/display" groups="pdk,qcom,qcom_display" />
+ <project path="hardware/qcom/gps" name="platform/hardware/qcom/gps" groups="qcom,qcom_gps,pdk" />
+ <project path="hardware/qcom/keymaster" name="platform/hardware/qcom/keymaster" groups="qcom,qcom_keymaster,pdk" />
+ <project path="hardware/qcom/media" name="platform/hardware/qcom/media" groups="qcom,pdk" />
+ <project path="hardware/qcom/msm8960" name="platform/hardware/qcom/msm8960" groups="qcom_msm8960,pdk" />
+ <project path="hardware/qcom/msm8994" name="platform/hardware/qcom/msm8994" groups="qcom_msm8994,pdk" />
+ <project path="hardware/qcom/msm8996" name="platform/hardware/qcom/msm8996" groups="qcom_msm8996,pdk" />
+ <project path="hardware/qcom/msm8998" name="platform/hardware/qcom/msm8998" groups="qcom_msm8998,pdk" />
+ <project path="hardware/qcom/msm8x09" name="platform/hardware/qcom/msm8x09" groups="qcom_msm8x09" />
+ <project path="hardware/qcom/msm8x26" name="platform/hardware/qcom/msm8x26" groups="qcom_msm8x26,pdk" />
+ <project path="hardware/qcom/msm8x27" name="platform/hardware/qcom/msm8x27" groups="qcom_msm8x27,pdk" />
+ <project path="hardware/qcom/msm8x84" name="platform/hardware/qcom/msm8x84" groups="qcom_msm8x84,pdk" />
+ <project path="hardware/qcom/neuralnetworks/hvxservice" name="platform/hardware/qcom/neuralnetworks/hvxservice" groups="wahoo" />
+ <project path="hardware/qcom/power" name="platform/hardware/qcom/power" groups="qcom,pdk" />
+ <project path="hardware/qcom/wlan" name="platform/hardware/qcom/wlan" groups="qcom_wlan,pdk" />
+ <project path="hardware/ril" name="platform/hardware/ril" groups="pdk" />
+ <project path="hardware/st/nfc" name="platform/hardware/st/nfc" groups="pdk" />
+ <project path="kernel/configs" name="kernel/configs" groups="vts,pdk" />
+ <project path="kernel/tests" name="kernel/tests" />
+ <project path="libcore" name="platform/libcore" groups="pdk" />
+ <project path="libnativehelper" name="platform/libnativehelper" groups="pdk" />
+ <project path="packages/apps/BasicSmsReceiver" name="platform/packages/apps/BasicSmsReceiver" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Bluetooth" name="platform/packages/apps/Bluetooth" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Browser2" name="platform/packages/apps/Browser2" groups="pdk-fs" />
+ <project path="packages/apps/Calendar" name="platform/packages/apps/Calendar" groups="pdk-fs" />
+ <project path="packages/apps/Camera2" name="platform/packages/apps/Camera2" groups="pdk-fs" />
+ <project path="packages/apps/Car/Dialer" name="platform/packages/apps/Car/Dialer" groups="pdk-fs" />
+ <project path="packages/apps/Car/Hvac" name="platform/packages/apps/Car/Hvac" groups="pdk-fs" />
+ <project path="packages/apps/Car/LatinIME" name="platform/packages/apps/Car/LatinIME" groups="pdk-fs" />
+ <project path="packages/apps/Car/Launcher" name="platform/packages/apps/Car/Launcher" groups="pdk-fs" />
+ <project path="packages/apps/Car/LensPicker" name="platform/packages/apps/Car/LensPicker" groups="pdk-fs" />
+ <project path="packages/apps/Car/libs" name="platform/packages/apps/Car/libs" groups="pdk-fs" />
+ <project path="packages/apps/Car/LocalMediaPlayer" name="platform/packages/apps/Car/LocalMediaPlayer" groups="pdk-fs" />
+ <project path="packages/apps/Car/Media" name="platform/packages/apps/Car/Media" groups="pdk-fs" />
+ <project path="packages/apps/Car/Messenger" name="platform/packages/apps/Car/Messenger" groups="pdk-fs" />
+ <project path="packages/apps/Car/Overview" name="platform/packages/apps/Car/Overview" groups="pdk-fs" />
+ <project path="packages/apps/Car/Radio" name="platform/packages/apps/Car/Radio" groups="pdk-fs" />
+ <project path="packages/apps/Car/Settings" name="platform/packages/apps/Car/Settings" groups="pdk-fs" />
+ <project path="packages/apps/Car/Stream" name="platform/packages/apps/Car/Stream" groups="pdk-fs" />
+ <project path="packages/apps/Car/SystemUpdater" name="platform/packages/apps/Car/SystemUpdater" groups="pdk-fs" />
+ <project path="packages/apps/CarrierConfig" name="platform/packages/apps/CarrierConfig" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/CellBroadcastReceiver" name="platform/packages/apps/CellBroadcastReceiver" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/CertInstaller" name="platform/packages/apps/CertInstaller" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Contacts" name="platform/packages/apps/Contacts" groups="pdk-fs" />
+ <project path="packages/apps/DeskClock" name="platform/packages/apps/DeskClock" groups="pdk-fs" />
+ <project path="packages/apps/DevCamera" name="platform/packages/apps/DevCamera" groups="pdk" />
+ <project path="packages/apps/Dialer" name="platform/packages/apps/Dialer" groups="pdk-fs" />
+ <project path="packages/apps/DocumentsUI" name="platform/packages/apps/DocumentsUI" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Email" name="platform/packages/apps/Email" groups="pdk-fs" />
+ <project path="packages/apps/EmergencyInfo" name="platform/packages/apps/EmergencyInfo" groups="pdk-fs" />
+ <project path="packages/apps/ExactCalculator" name="platform/packages/apps/ExactCalculator" groups="pdk-fs" />
+ <project path="packages/apps/Gallery" name="platform/packages/apps/Gallery" groups="pdk-fs" />
+ <project path="packages/apps/Gallery2" name="platform/packages/apps/Gallery2" groups="pdk-fs" />
+ <project path="packages/apps/HTMLViewer" name="platform/packages/apps/HTMLViewer" groups="pdk-fs" />
+ <project path="packages/apps/KeyChain" name="platform/packages/apps/KeyChain" groups="pdk-fs" />
+ <project path="packages/apps/Launcher2" name="platform/packages/apps/Launcher2" groups="pdk-fs" />
+ <project path="packages/apps/Launcher3" name="platform/packages/apps/Launcher3" groups="pdk-fs" />
+ <project path="packages/apps/LegacyCamera" name="platform/packages/apps/LegacyCamera" groups="pdk-fs" />
+ <project path="packages/apps/ManagedProvisioning" name="platform/packages/apps/ManagedProvisioning" groups="pdk-fs" />
+ <project path="packages/apps/Messaging" name="platform/packages/apps/Messaging" groups="pdk-fs" />
+ <project path="packages/apps/Music" name="platform/packages/apps/Music" groups="pdk-fs" />
+ <project path="packages/apps/MusicFX" name="platform/packages/apps/MusicFX" groups="pdk-fs" />
+ <project path="packages/apps/Nfc" name="platform/packages/apps/Nfc" groups="apps_nfc,pdk-fs" />
+ <project path="packages/apps/OneTimeInitializer" name="platform/packages/apps/OneTimeInitializer" groups="pdk-fs" />
+ <project path="packages/apps/PackageInstaller" name="platform/packages/apps/PackageInstaller" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/PhoneCommon" name="platform/packages/apps/PhoneCommon" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Protips" name="platform/packages/apps/Protips" groups="pdk-fs" />
+ <project path="packages/apps/Provision" name="platform/packages/apps/Provision" groups="pdk-fs" />
+ <project path="packages/apps/QuickSearchBox" name="platform/packages/apps/QuickSearchBox" groups="pdk-fs" />
+ <project path="packages/apps/SafetyRegulatoryInfo" name="platform/packages/apps/SafetyRegulatoryInfo" groups="pdk-fs" />
+ <project path="packages/apps/SecureElement" name="platform/packages/apps/SecureElement" groups="apps_se,pdk-fs" />
+ <project path="packages/apps/Settings" name="platform/packages/apps/Settings" groups="pdk-fs" />
+ <project path="packages/apps/SoundRecorder" name="platform/packages/apps/SoundRecorder" groups="pdk-fs" />
+ <project path="packages/apps/SpareParts" name="platform/packages/apps/SpareParts" groups="pdk-fs" />
+ <project path="packages/apps/Stk" name="platform/packages/apps/Stk" groups="apps_stk,pdk-fs" />
+ <project path="packages/apps/StorageManager" name="platform/packages/apps/StorageManager" groups="pdk-fs" />
+ <project path="packages/apps/Tag" name="platform/packages/apps/Tag" groups="pdk-fs" />
+ <project path="packages/apps/Terminal" name="platform/packages/apps/Terminal" groups="pdk-fs" />
+ <project path="packages/apps/Test/connectivity" name="platform/packages/apps/Test/connectivity" groups="pdk" />
+ <project path="packages/apps/TimeZoneData" name="platform/packages/apps/TimeZoneData" groups="pdk" />
+ <project path="packages/apps/TimeZoneUpdater" name="platform/packages/apps/TimeZoneUpdater" groups="pdk" />
+ <project path="packages/apps/Traceur" name="platform/packages/apps/Traceur" groups="pdk-fs" />
+ <project path="packages/apps/TvSettings" name="platform/packages/apps/TvSettings" groups="pdk-fs" />
+ <project path="packages/apps/TV" name="platform/packages/apps/TV" />
+ <project path="packages/apps/UnifiedEmail" name="platform/packages/apps/UnifiedEmail" groups="pdk-fs" />
+ <project path="packages/apps/WallpaperPicker" name="platform/packages/apps/WallpaperPicker" groups="pdk-fs" />
+ <project path="packages/experimental" name="platform/packages/experimental" />
+ <project path="packages/inputmethods/LatinIME" name="platform/packages/inputmethods/LatinIME" groups="pdk-fs" />
+ <project path="packages/inputmethods/OpenWnn" name="platform/packages/inputmethods/OpenWnn" groups="pdk-fs" />
+ <project path="packages/providers/ApplicationsProvider" name="platform/packages/providers/ApplicationsProvider" groups="pdk-fs" />
+ <project path="packages/providers/BlockedNumberProvider" name="platform/packages/providers/BlockedNumberProvider" groups="pdk-fs" />
+ <project path="packages/providers/BookmarkProvider" name="platform/packages/providers/BookmarkProvider" groups="pdk-fs" />
+ <project path="packages/providers/CalendarProvider" name="platform/packages/providers/CalendarProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/CallLogProvider" name="platform/packages/providers/CallLogProvider" groups="pdk-fs" />
+ <project path="packages/providers/ContactsProvider" name="platform/packages/providers/ContactsProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/DownloadProvider" name="platform/packages/providers/DownloadProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/MediaProvider" name="platform/packages/providers/MediaProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/PartnerBookmarksProvider" name="platform/packages/providers/PartnerBookmarksProvider" groups="pdk-fs" />
+ <project path="packages/providers/TelephonyProvider" name="platform/packages/providers/TelephonyProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/TvProvider" name="platform/packages/providers/TvProvider" groups="pdk-fs" />
+ <project path="packages/providers/UserDictionaryProvider" name="platform/packages/providers/UserDictionaryProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/screensavers/Basic" name="platform/packages/screensavers/Basic" groups="pdk-fs" />
+ <project path="packages/screensavers/PhotoTable" name="platform/packages/screensavers/PhotoTable" groups="pdk-fs" />
+ <project path="packages/screensavers/WebView" name="platform/packages/screensavers/WebView" groups="pdk-fs" />
+ <project path="packages/services/BuiltInPrintService" name="platform/packages/services/BuiltInPrintService" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Car" name="platform/packages/services/Car" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Mms" name="platform/packages/services/Mms" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/NetworkRecommendation" name="platform/packages/services/NetworkRecommendation" groups="pdk-fs" />
+ <project path="packages/services/Telecomm" name="platform/packages/services/Telecomm" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Telephony" name="platform/packages/services/Telephony" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/wallpapers/LivePicker" name="platform/packages/wallpapers/LivePicker" groups="pdk-fs" />
+ <project path="pdk" name="platform/pdk" groups="pdk" />
+ <project path="platform_testing" name="platform/platform_testing" groups="pdk-fs,pdk-cw-fs,cts" />
+ <project path="prebuilts/abi-dumps/ndk" name="platform/prebuilts/abi-dumps/ndk" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/abi-dumps/vndk" name="platform/prebuilts/abi-dumps/vndk" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/android-emulator" name="platform/prebuilts/android-emulator" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/build-tools" name="platform/prebuilts/build-tools" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/checkstyle" name="platform/prebuilts/checkstyle" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/clang-tools" name="platform/prebuilts/clang-tools" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/clang/host/darwin-x86" name="platform/prebuilts/clang/host/darwin-x86" groups="pdk,darwin" clone-depth="1" />
+ <project path="prebuilts/clang/host/linux-x86" name="platform/prebuilts/clang/host/linux-x86" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/deqp" name="platform/prebuilts/deqp" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/devtools" name="platform/prebuilts/devtools" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9" groups="pdk,darwin,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9" name="platform/prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9" groups="pdk,darwin,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1" name="platform/prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1" groups="pdk,darwin" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9" groups="pdk,darwin,mips" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9" groups="pdk,darwin,x86" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9" groups="pdk,linux,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9" groups="pdk,linux,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8" groups="pdk,linux" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9" groups="pdk,linux,mips" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9" groups="pdk,linux,x86" clone-depth="1" />
+ <project path="prebuilts/gdb/darwin-x86" name="platform/prebuilts/gdb/darwin-x86" groups="darwin" clone-depth="1" />
+ <project path="prebuilts/gdb/linux-x86" name="platform/prebuilts/gdb/linux-x86" groups="linux" clone-depth="1" />
+ <project path="prebuilts/go/darwin-x86" name="platform/prebuilts/go/darwin-x86" groups="darwin,pdk,tradefed" clone-depth="1" />
+ <project path="prebuilts/go/linux-x86" name="platform/prebuilts/go/linux-x86" groups="linux,pdk,tradefed" clone-depth="1" />
+ <project path="prebuilts/gradle-plugin" name="platform/prebuilts/gradle-plugin" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/jdk/jdk8" name="platform/prebuilts/jdk/jdk8" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/jdk/jdk9" name="platform/prebuilts/jdk/jdk9" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/libs/libedit" name="platform/prebuilts/libs/libedit" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/android" name="platform/prebuilts/maven_repo/android" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/bumptech" name="platform/prebuilts/maven_repo/bumptech" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/google-play-service-client-libraries-3p" name="platform/prebuilts/maven_repo/google-play-service-client-libraries-3p" clone-depth="1" />
+ <project path="prebuilts/misc" name="platform/prebuilts/misc" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/ndk" name="platform/prebuilts/ndk" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/python/darwin-x86/2.7.5" name="platform/prebuilts/python/darwin-x86/2.7.5" groups="darwin,pdk,pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/python/linux-x86/2.7.5" name="platform/prebuilts/python/linux-x86/2.7.5" groups="linux,pdk,pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/qemu-kernel" name="platform/prebuilts/qemu-kernel" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/r8" name="platform/prebuilts/r8" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/sdk" name="platform/prebuilts/sdk" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/tools" name="platform/prebuilts/tools" groups="pdk,tools" clone-depth="1" />
+ <project path="sdk" name="platform/sdk" groups="pdk-cw-fs,pdk-fs" />
+ <project path="system/bt" name="platform/system/bt" groups="pdk" />
+ <project path="system/ca-certificates" name="platform/system/ca-certificates" groups="pdk" />
+ <project path="system/chre" name="platform/system/chre" groups="pdk" />
+ <project path="system/connectivity/wificond" name="platform/system/connectivity/wificond" groups="pdk" />
+ <project path="system/connectivity/wifilogd" name="platform/system/connectivity/wifilogd" groups="pdk" />
+ <project path="system/core" name="platform/system/core" groups="pdk" />
+ <project path="system/extras" name="platform/system/extras" groups="pdk" />
+ <project path="system/gatekeeper" name="platform/system/gatekeeper" groups="pdk" />
+ <project path="system/hardware/interfaces" name="platform/system/hardware/interfaces" groups="pdk" />
+ <project path="system/hwservicemanager" name="platform/system/hwservicemanager" groups="pdk" />
+ <project path="system/iot/attestation" name="platform/system/iot/attestation" groups="pdk" />
+ <project path="system/keymaster" name="platform/system/keymaster" groups="pdk" />
+ <project path="system/libfmq" name="platform/system/libfmq" groups="pdk" />
+ <project path="system/libhidl" name="platform/system/libhidl" groups="pdk" />
+ <project path="system/libhwbinder" name="platform/system/libhwbinder" groups="pdk" />
+ <project path="system/libufdt" name="platform/system/libufdt" groups="pdk" />
+ <project path="system/libvintf" name="platform/system/libvintf" groups="pdk" />
+ <project path="system/media" name="platform/system/media" groups="pdk" />
+ <project path="system/netd" name="platform/system/netd" groups="pdk" />
+ <project path="system/nfc" name="platform/system/nfc" groups="pdk" />
+ <project path="system/nvram" name="platform/system/nvram" groups="pdk" />
+ <project path="system/security" name="platform/system/security" groups="pdk" />
+ <project path="system/sepolicy" name="platform/system/sepolicy" groups="pdk" />
+ <project path="system/timezone" name="platform/system/timezone" groups="pdk" />
+ <project path="system/tools/aidl" name="platform/system/tools/aidl" groups="pdk-cw-fs,pdk-fs" />
+ <project path="system/tools/bpt" name="platform/system/tools/bpt" groups="pdk" />
+ <project path="system/tools/hidl" name="platform/system/tools/hidl" groups="pdk" />
+ <project path="system/update_engine" name="platform/system/update_engine" groups="pdk" />
+ <project path="system/vold" name="platform/system/vold" groups="pdk" />
+ <project path="test/framework" name="platform/test/framework" groups="vts,pdk" />
+ <project path="test/mlts/benchmark" name="platform/test/mlts/benchmark" groups="pdk" />
+ <project path="test/mlts/models" name="platform/test/mlts/models" groups="pdk" />
+ <project path="test/sts" name="platform/test/sts" groups="sts,pdk" />
+ <project path="test/vti/alert" name="platform/test/vti/alert" groups="vts,pdk" />
+ <project path="test/vti/dashboard" name="platform/test/vti/dashboard" groups="vts,pdk" />
+ <project path="test/vti/fuzz_test_serving" name="platform/test/vti/fuzz_test_serving" groups="vts,pdk" />
+ <project path="test/vti/test_serving" name="platform/test/vti/test_serving" groups="vts,pdk" />
+ <project path="test/vts" name="platform/test/vts" groups="vts,pdk" />
+ <project path="test/vts-testcase/fuzz" name="platform/test/vts-testcase/fuzz" groups="vts,pdk" />
+ <project path="test/vts-testcase/hal" name="platform/test/vts-testcase/hal" groups="vts,pdk" />
+ <project path="test/vts-testcase/hal-trace" name="platform/test/vts-testcase/hal-trace" groups="vts,pdk" />
+ <project path="test/vts-testcase/kernel" name="platform/test/vts-testcase/kernel" groups="vts,pdk" />
+ <project path="test/vts-testcase/nbu" name="platform/test/vts-testcase/nbu" groups="vts,pdk" />
+ <project path="test/vts-testcase/performance" name="platform/test/vts-testcase/performance" groups="vts,pdk" />
+ <project path="test/vts-testcase/security" name="platform/test/vts-testcase/security" groups="vts,pdk" />
+ <project path="test/vts-testcase/vndk" name="platform/test/vts-testcase/vndk" groups="vts,pdk" />
+ <project path="toolchain/benchmark" name="toolchain/benchmark" />
+ <project path="toolchain/binutils" name="toolchain/binutils" groups="pdk" />
+ <project path="toolchain/pgo-profiles" name="toolchain/pgo-profiles" groups="pdk" />
+ <project path="tools/acloud" name="platform/tools/acloud" groups="tools,vts,pdk,tradefed" />
+ <project path="tools/adt/idea" name="platform/tools/adt/idea" groups="notdefault,tools" />
+ <project path="tools/apksig" name="platform/tools/apksig" groups="pdk,tradefed" />
+ <project path="tools/apkzlib" name="platform/tools/apkzlib" groups="pdk,tradefed" />
+ <project path="tools/base" name="platform/tools/base" groups="notdefault,tools" />
+ <project path="tools/build" name="platform/tools/build" groups="notdefault,tools" />
+ <project path="tools/dexter" name="platform/tools/dexter" groups="tools,pdk-fs" />
+ <project path="tools/external/fat32lib" name="platform/tools/external/fat32lib" groups="tools" />
+ <project path="tools/external/gradle" name="platform/tools/external/gradle" groups="tools" clone-depth="1" />
+ <project path="tools/idea" name="platform/tools/idea" groups="notdefault,tools" />
+ <project path="tools/loganalysis" name="platform/tools/loganalysis" groups="nopresubmit,pdk,tradefed" />
+ <project path="tools/metalava" name="platform/tools/metalava" groups="tools" />
+ <project path="tools/motodev" name="platform/tools/motodev" groups="notdefault,motodev" />
+ <project path="tools/repohooks" name="platform/tools/repohooks" groups="adt-infra,cts,developers,motodev,pdk,tools,tradefed" />
+ <project path="tools/security" name="platform/tools/security" groups="pdk,tools" />
+ <project path="tools/studio/cloud" name="platform/tools/studio/cloud" groups="notdefault,tools" />
+ <project path="tools/swt" name="platform/tools/swt" groups="notdefault,tools" />
+ <project path="tools/test/connectivity" name="platform/tools/test/connectivity" groups="pdk" />
+ <project path="tools/test/graphicsbenchmark" name="platform/tools/test/graphicsbenchmark" groups="pdk" />
+ <project path="tools/tradefederation/core" name="platform/tools/tradefederation" groups="pdk,tradefed" />
+ <project path="tools/tradefederation/contrib" name="platform/tools/tradefederation/contrib" groups="pdk,tradefed" />
+
+ <repo-hooks in-project="platform/tools/repohooks" enabled-list="pre-upload" />
+
+</manifest>
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 38ce92a5dc7..3b741d51598 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -29,8 +29,10 @@
"merge_when_pipeline_succeeds": { "type": "boolean" },
"source_branch": { "type": "string" },
"source_project_id": { "type": "integer" },
+ "source_project_full_path": { "type": ["string", "null"]},
"target_branch": { "type": "string" },
"target_project_id": { "type": "integer" },
+ "target_project_full_path": { "type": ["string", "null"]},
"allow_collaboration": { "type": "boolean"},
"metrics": {
"oneOf": [
diff --git a/spec/fixtures/project_export.tar.gz b/spec/fixtures/project_export.tar.gz
new file mode 100644
index 00000000000..72ab2d71f35
--- /dev/null
+++ b/spec/fixtures/project_export.tar.gz
Binary files differ
diff --git a/spec/fixtures/trace/sample_trace b/spec/fixtures/trace/sample_trace
index c65cf05d5ca..7bfe3f83b7b 100644
--- a/spec/fixtures/trace/sample_trace
+++ b/spec/fixtures/trace/sample_trace
@@ -41,7 +41,7 @@ From https://gitlab.com/gitlab-org/gitlab-ce
section_end:1522927113:get_sources
section_start:1522927113:restore_cache
Checking cache for ruby-2.3.6-with-yarn...
-Downloading cache.zip from http://runners-cache-5-internal.gitlab.com:444/runner/project/13083/ruby-2.3.6-with-yarn
+Downloading cache.zip from http://runners-cache-5-internal.gitlab.com:444/runner/project/13083/ruby-2.3.6-with-yarn
Successfully extracted cache
section_end:1522927128:restore_cache
section_start:1522927128:download_artifacts
@@ -51,7 +51,7 @@ Downloading artifacts from coordinator... ok  id=61303215 respon
Downloading artifacts from coordinator... ok  id=61303216 responseStatus=200 OK token=iy2yYbq8
Downloading artifacts for setup-test-env (61303217)...
Downloading artifacts from coordinator... ok  id=61303217 responseStatus=200 OK token=ur1g79-4
-WARNING: tmp/tests/gitlab-shell/.gitlab_shell_secret: chmod tmp/tests/gitlab-shell/.gitlab_shell_secret: no such file or directory (suppressing repeats)
+WARNING: tmp/tests/gitlab-shell/.gitlab_shell_secret: chmod tmp/tests/gitlab-shell/.gitlab_shell_secret: no such file or directory (suppressing repeats)
section_end:1522927141:download_artifacts
section_start:1522927141:build_script
$ bundle --version
@@ -1486,7 +1486,7 @@ Gitlab::ImportExport::ProjectTreeSaver
overrides the project description
group members
does not export group members if it has no permission
- does not export group members as master
+ does not export group members as maintainer
exports group members as group owner
as admin
exports group members as admin
@@ -1690,7 +1690,7 @@ GroupsController
and logged in as Developer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
- and logged in as Master
+ and logged in as Maintainer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
and can_create_group is false
@@ -1706,7 +1706,7 @@ GroupsController
and logged in as Developer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
- and logged in as Master
+ and logged in as Maintainer
behaves like member without ability to create subgroups
renders the 404 page (PENDING: around hook at ./spec/spec_helper.rb:186 did not execute the example)
GET #activity
@@ -2324,7 +2324,7 @@ Editing file blob
shows blob editor with same branch
with protected branch
shows blob editor with patch branch
- as master
+ as maintainer
shows blob editor with same branch
Boards::Lists::MoveService
@@ -2880,7 +2880,7 @@ API::V3::Environments
won't update the external_url if only the name is passed
returns a 404 if the environment does not exist
DELETE /projects/:id/environments/:environment_id
- as a master
+ as a maintainer
returns a 200 for an existing environment
returns a 404 for non existing id
a non member
@@ -3001,11 +3001,11 @@ LfsFileLock
#can_be_unlocked_by?
when it's forced
can be unlocked by the author
- can be unlocked by a master
+ can be unlocked by a maintainer
can't be unlocked by other user
when it isn't forced
can be unlocked by the author
- can't be unlocked by a master
+ can't be unlocked by a maintainer
can't be unlocked by other user
Gitlab::Ci::Config::Entry::Boolean
@@ -3069,7 +3069,7 @@ Pending: (Failures listed here are expected and do not affect your suite's statu
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
- 5) GroupsController GET #new when creating subgroups and can_create_group is true and logged in as Master behaves like member without ability to create subgroups renders the 404 page
+ 5) GroupsController GET #new when creating subgroups and can_create_group is true and logged in as Maintainer behaves like member without ability to create subgroups renders the 404 page
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
@@ -3089,7 +3089,7 @@ Pending: (Failures listed here are expected and do not affect your suite's statu
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
- 10) GroupsController GET #new when creating subgroups and can_create_group is false and logged in as Master behaves like member without ability to create subgroups renders the 404 page
+ 10) GroupsController GET #new when creating subgroups and can_create_group is false and logged in as Maintainer behaves like member without ability to create subgroups renders the 404 page
# around hook at ./spec/spec_helper.rb:186 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:25
@@ -3237,7 +3237,7 @@ Pending: (Failures listed here are expected and do not affect your suite's statu
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:212
- 47) Groups::TransferService#execute when transferring a subgroup into another group when the group is allowed to be transferred should update parent group to the new parent
+ 47) Groups::TransferService#execute when transferring a subgroup into another group when the group is allowed to be transferred should update parent group to the new parent
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:216
@@ -3435,10 +3435,10 @@ section_end:1522927515:after_script
section_end:1522927516:archive_cache
section_start:1522927516:upload_artifacts
Uploading artifacts...
-coverage/: found 5 matching files 
-knapsack/: found 5 matching files 
-rspec_flaky/: found 4 matching files 
-WARNING: tmp/capybara/: no matching files 
+coverage/: found 5 matching files 
+knapsack/: found 5 matching files 
+rspec_flaky/: found 4 matching files 
+WARNING: tmp/capybara/: no matching files 
Uploading artifacts to coordinator... ok  id=61303283 responseStatus=201 Created token=rusBKvxM
section_end:1522927520:upload_artifacts
Job succeeded
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index a3e010c3206..1c216b3fe97 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -65,9 +65,9 @@ describe BlobHelper do
describe "#sanitize_svg_data" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
- let(:data) { open(input_svg_path).read }
+ let(:data) { File.read(input_svg_path) }
let(:expected_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
- let(:expected) { open(expected_svg_path).read }
+ let(:expected) { File.read(expected_svg_path) }
it 'retains essential elements' do
expect(sanitize_svg_data(data)).to eq(expected)
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index d246beb9888..77410e0070c 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -21,27 +21,6 @@ describe IssuablesHelper do
end
end
- describe '#group_dropdown_label' do
- let(:group) { create(:group) }
- let(:default) { 'default label' }
-
- it 'returns default group label when group_id is nil' do
- expect(group_dropdown_label(nil, default)).to eq('default label')
- end
-
- it 'returns "any group" when group_id is 0' do
- expect(group_dropdown_label('0', default)).to eq('Any group')
- end
-
- it 'returns group full path when a group was found for the provided id' do
- expect(group_dropdown_label(group.id, default)).to eq(group.full_name)
- end
-
- it 'returns default label when a group was not found for the provided id' do
- expect(group_dropdown_label(9999, default)).to eq('default label')
- end
- end
-
describe '#issuable_labels_tooltip' do
it 'returns label text with no labels' do
expect(issuable_labels_tooltip([])).to eq("Labels")
@@ -184,6 +163,7 @@ describe IssuablesHelper do
issuableRef: "##{issue.iid}",
markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
markdownDocsPath: '/help/user/markdown',
+ markdownVersion: 11,
issuableTemplates: [],
projectPath: @project.path,
projectNamespace: @project.namespace.path,
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 1a720aae55c..597648b064d 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -11,7 +11,7 @@ describe MarkupHelper do
before do
# Ensure the generated reference links aren't redacted
- project.add_master(user)
+ project.add_maintainer(user)
# Helper expects a @project instance variable
helper.instance_variable_set(:@project, project)
@@ -205,7 +205,9 @@ describe MarkupHelper do
it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown)
- expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true)
+ expect(helper).to receive(:markdown_unsafe).with('wiki content',
+ pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
+ issuable_state_filter_enabled: true, markdown_engine: :redcarpet)
helper.render_wiki_content(@wiki)
end
@@ -236,19 +238,32 @@ describe MarkupHelper do
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
end
- it "delegates to #markdown_unsafe when file name corresponds to Markdown" do
+ it 'delegates to #markdown_unsafe when file name corresponds to Markdown' do
expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
expect(helper.markup('foo.md', content)).to eq('NOEL')
end
- it "delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc" do
+ it 'delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc' do
expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
expect(helper.markup('foo.adoc', content)).to eq('NOEL')
end
+
+ it 'uses passed in rendered content' do
+ expect(helper).not_to receive(:gitlab_markdown?)
+ expect(helper).not_to receive(:markdown_unsafe)
+
+ expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>')
+ end
+
+ it 'defaults to Redcarpet' do
+ expect(helper).to receive(:markdown_unsafe).with(content, hash_including(markdown_engine: :redcarpet)).and_return('NOEL')
+
+ expect(helper.markup('foo.md', content)).to eq('NOEL')
+ end
end
describe '#first_line_in_markdown' do
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 460d3b6a7e4..343e140f5fb 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -28,6 +28,16 @@ describe NamespacesHelper do
expect(options).not_to include(admin_group.name)
expect(options).to include(user_group.name)
+ expect(options).to include(user.name)
+ end
+
+ it 'returns only groups if groups_only option is true' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options(nil, groups_only: true)
+
+ expect(options).not_to include(user.name)
+ expect(options).to include(user_group.name)
end
context 'when nested groups are available', :nested_groups do
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index b992bdb4a5e..21461e46cf4 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -6,18 +6,18 @@ describe NotesHelper do
let(:owner) { create(:owner) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:owner_note) { create(:note, author: owner, project: project) }
- let(:master_note) { create(:note, author: master, project: project) }
+ let(:maintainer_note) { create(:note, author: maintainer, project: project) }
let(:reporter_note) { create(:note, author: reporter, project: project) }
- let!(:notes) { [owner_note, master_note, reporter_note] }
+ let!(:notes) { [owner_note, maintainer_note, reporter_note] }
before do
group.add_owner(owner)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
end
@@ -25,16 +25,16 @@ describe NotesHelper do
describe "#notes_max_access_for_users" do
it 'returns access levels' do
expect(helper.note_max_access_for_user(owner_note)).to eq(Gitlab::Access::OWNER)
- expect(helper.note_max_access_for_user(master_note)).to eq(Gitlab::Access::MASTER)
+ expect(helper.note_max_access_for_user(maintainer_note)).to eq(Gitlab::Access::MAINTAINER)
expect(helper.note_max_access_for_user(reporter_note)).to eq(Gitlab::Access::REPORTER)
end
it 'handles access in different projects' do
second_project = create(:project)
- second_project.add_reporter(master)
- other_note = create(:note, author: master, project: second_project)
+ second_project.add_reporter(maintainer)
+ other_note = create(:note, author: maintainer, project: second_project)
- expect(helper.note_max_access_for_user(master_note)).to eq(Gitlab::Access::MASTER)
+ expect(helper.note_max_access_for_user(maintainer_note)).to eq(Gitlab::Access::MAINTAINER)
expect(helper.note_max_access_for_user(other_note)).to eq(Gitlab::Access::REPORTER)
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index beb6e8ea273..cbd4ff0fb4a 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -290,33 +290,6 @@ describe ProjectsHelper do
end
end
- describe '#sanitizerepo_repo_path' do
- let(:project) { create(:project, :repository) }
- let(:storage_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages.default.legacy_disk_path
- end
- end
-
- before do
- allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
- end
-
- it 'removes the repo path' do
- repo = File.join(storage_path, 'namespace/test.git')
- import_error = "Could not clone #{repo}\n"
-
- expect(sanitize_repo_path(project, import_error)).to eq('Could not clone [REPOS PATH]/namespace/test.git')
- end
-
- it 'removes the temporary repo path used for uploads/exports' do
- repo = '/base/repo/export/path/tmp/project_exports/uploads/test.tar.gz'
- import_error = "Unable to decompress #{repo}\n"
-
- expect(sanitize_repo_path(project, import_error)).to eq('Unable to decompress [REPO EXPORT PATH]/uploads/test.tar.gz')
- end
- end
-
describe '#last_push_event' do
let(:user) { double(:user, fork_of: nil) }
let(:project) { double(:project, id: 1) }
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index 5a2e4b34069..a64f8a11ef2 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -92,11 +92,10 @@ describe SubmoduleHelper do
context 'in-repository submodule' do
let(:group) { create(:group, name: "Master Project", path: "master-project") }
let(:project) { create(:project, group: group) }
- before do
- self.instance_variable_set(:@project, project)
- end
it 'in-repository' do
+ allow(repo).to receive(:project).and_return(project)
+
stub_url('./')
expect(submodule_links(submodule_item)).to eq(["/master-project/#{project.path}", "/master-project/#{project.path}/tree/hash"])
end
@@ -167,32 +166,28 @@ describe SubmoduleHelper do
let(:project) { create(:project, group: group) }
let(:commit_id) { sample_commit[:id] }
- before do
- self.instance_variable_set(:@project, project)
- end
-
it 'one level down' do
- result = relative_self_links('../test.git', commit_id)
+ result = relative_self_links('../test.git', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'with trailing whitespace' do
- result = relative_self_links('../test.git ', commit_id)
+ result = relative_self_links('../test.git ', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'two levels down' do
- result = relative_self_links('../../test.git', commit_id)
+ result = relative_self_links('../../test.git', commit_id, project)
expect(result).to eq(["/#{group.path}/test", "/#{group.path}/test/tree/#{commit_id}"])
end
it 'one level down with namespace and repo' do
- result = relative_self_links('../foobar/test.git', commit_id)
+ result = relative_self_links('../foobar/test.git', commit_id, project)
expect(result).to eq(["/foobar/test", "/foobar/test/tree/#{commit_id}"])
end
it 'two levels down with namespace and repo' do
- result = relative_self_links('../foobar/baz/test.git', commit_id)
+ result = relative_self_links('../foobar/baz/test.git', commit_id, project)
expect(result).to eq(["/baz/test", "/baz/test/tree/#{commit_id}"])
end
@@ -201,7 +196,7 @@ describe SubmoduleHelper do
let(:project) { create(:project, namespace: user.namespace) }
it 'one level down with personal project' do
- result = relative_self_links('../test.git', commit_id)
+ result = relative_self_links('../test.git', commit_id, project)
expect(result).to eq(["/#{user.username}/test", "/#{user.username}/test/tree/#{commit_id}"])
end
end
diff --git a/spec/helpers/time_helper_spec.rb b/spec/helpers/time_helper_spec.rb
index 21f35585367..0b371d69ecf 100644
--- a/spec/helpers/time_helper_spec.rb
+++ b/spec/helpers/time_helper_spec.rb
@@ -4,10 +4,12 @@ describe TimeHelper do
describe "#time_interval_in_words" do
it "returns minutes and seconds" do
intervals_in_words = {
- 100 => "1 minute 40 seconds",
- 100.32 => "1 minute 40 seconds",
- 121 => "2 minutes 1 second",
- 3721 => "62 minutes 1 second",
+ 60 => "1 minute",
+ 100 => "1 minute and 40 seconds",
+ 100.32 => "1 minute and 40 seconds",
+ 120 => "2 minutes",
+ 121 => "2 minutes and 1 second",
+ 3721 => "62 minutes and 1 second",
0 => "0 seconds"
}
diff --git a/spec/javascripts/diffs/components/changed_files_spec.js b/spec/javascripts/diffs/components/changed_files_spec.js
index 2d57af6137c..f737e8fa38e 100644
--- a/spec/javascripts/diffs/components/changed_files_spec.js
+++ b/spec/javascripts/diffs/components/changed_files_spec.js
@@ -1,12 +1,17 @@
import Vue from 'vue';
-import $ from 'jquery';
+import Vuex from 'vuex';
import { mountComponentWithStore } from 'spec/helpers';
-import store from '~/diffs/store';
-import ChangedFiles from '~/diffs/components/changed_files.vue';
+import diffsModule from '~/diffs/store/modules';
+import changedFiles from '~/diffs/components/changed_files.vue';
describe('ChangedFiles', () => {
- const Component = Vue.extend(ChangedFiles);
- const createComponent = props => mountComponentWithStore(Component, { props, store });
+ const Component = Vue.extend(changedFiles);
+ const store = new Vuex.Store({
+ modules: {
+ diffs: diffsModule,
+ },
+ });
+
let vm;
beforeEach(() => {
@@ -14,6 +19,7 @@ describe('ChangedFiles', () => {
<div id="dummy-element"></div>
<div class="js-tabs-affix"></div>
`);
+
const props = {
diffFiles: [
{
@@ -26,7 +32,8 @@ describe('ChangedFiles', () => {
},
],
};
- vm = createComponent(props);
+
+ vm = mountComponentWithStore(Component, { props, store });
});
describe('with single file added', () => {
@@ -40,58 +47,56 @@ describe('ChangedFiles', () => {
});
});
- describe('template', () => {
- describe('diff view mode buttons', () => {
- let inlineButton;
- let parallelButton;
+ describe('diff view mode buttons', () => {
+ let inlineButton;
+ let parallelButton;
- beforeEach(() => {
- inlineButton = vm.$el.querySelector('.js-inline-diff-button');
- parallelButton = vm.$el.querySelector('.js-parallel-diff-button');
- });
+ beforeEach(() => {
+ inlineButton = vm.$el.querySelector('.js-inline-diff-button');
+ parallelButton = vm.$el.querySelector('.js-parallel-diff-button');
+ });
+
+ it('should have Inline and Side-by-side buttons', () => {
+ expect(inlineButton).toBeDefined();
+ expect(parallelButton).toBeDefined();
+ });
+
+ it('should add active class to Inline button', done => {
+ vm.$store.state.diffs.diffViewType = 'inline';
+
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(true);
+ expect(parallelButton.classList.contains('active')).toEqual(false);
- it('should have Inline and Side-by-side buttons', () => {
- expect(inlineButton).toBeDefined();
- expect(parallelButton).toBeDefined();
+ done();
});
+ });
- it('should add active class to Inline button', done => {
- vm.$store.state.diffs.diffViewType = 'inline';
+ it('should toggle active state of buttons when diff view type changed', done => {
+ vm.$store.state.diffs.diffViewType = 'parallel';
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(true);
- expect(parallelButton.classList.contains('active')).toEqual(false);
+ vm.$nextTick(() => {
+ expect(inlineButton.classList.contains('active')).toEqual(false);
+ expect(parallelButton.classList.contains('active')).toEqual(true);
- done();
- });
+ done();
});
+ });
- it('should toggle active state of buttons when diff view type changed', done => {
- vm.$store.state.diffs.diffViewType = 'parallel';
+ describe('clicking them', () => {
+ it('should toggle the diff view type', done => {
+ parallelButton.click();
vm.$nextTick(() => {
expect(inlineButton.classList.contains('active')).toEqual(false);
expect(parallelButton.classList.contains('active')).toEqual(true);
- done();
- });
- });
-
- describe('clicking them', () => {
- it('should toggle the diff view type', done => {
- $(parallelButton).click();
+ inlineButton.click();
vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(false);
- expect(parallelButton.classList.contains('active')).toEqual(true);
-
- $(inlineButton).click();
-
- vm.$nextTick(() => {
- expect(inlineButton.classList.contains('active')).toEqual(true);
- expect(parallelButton.classList.contains('active')).toEqual(false);
- done();
- });
+ expect(inlineButton.classList.contains('active')).toEqual(true);
+ expect(parallelButton.classList.contains('active')).toEqual(false);
+ done();
});
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 05f5d47ce42..241ff07026e 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -1,7 +1,10 @@
import Vue from 'vue';
+import Vuex from 'vuex';
+import diffsModule from '~/diffs/store/modules';
+import notesModule from '~/notes/stores/modules';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
const discussionFixture = 'merge_requests/diff_discussion.json';
@@ -9,13 +12,19 @@ describe('diff_file_header', () => {
let vm;
let props;
const Component = Vue.extend(DiffFileHeader);
+ const store = new Vuex.Store({
+ modules: {
+ diffs: diffsModule,
+ notes: notesModule,
+ },
+ });
beforeEach(() => {
const diffDiscussionMock = getJSONFixture(discussionFixture)[0];
const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true });
props = {
diffFile,
- currentUser: {},
+ canCurrentUserFork: false,
};
});
@@ -26,13 +35,13 @@ describe('diff_file_header', () => {
describe('computed', () => {
describe('icon', () => {
beforeEach(() => {
- props.diffFile.blob.icon = 'dummy icon';
+ props.diffFile.blob.icon = 'file-text-o';
});
it('returns the blob icon for files', () => {
props.diffFile.submodule = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.icon).toBe(props.diffFile.blob.icon);
});
@@ -40,7 +49,7 @@ describe('diff_file_header', () => {
it('returns the archive icon for submodules', () => {
props.diffFile.submodule = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.icon).toBe('archive');
});
@@ -58,7 +67,7 @@ describe('diff_file_header', () => {
it('returns the fileHash for files', () => {
props.diffFile.submodule = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleLink).toBe(`#${props.diffFile.fileHash}`);
});
@@ -66,7 +75,7 @@ describe('diff_file_header', () => {
it('returns the submoduleTreeUrl for submodules', () => {
props.diffFile.submodule = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleLink).toBe(props.diffFile.submoduleTreeUrl);
});
@@ -77,7 +86,7 @@ describe('diff_file_header', () => {
submoduleTreeUrl: null,
});
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleLink).toBe(props.diffFile.submoduleLink);
});
@@ -94,7 +103,7 @@ describe('diff_file_header', () => {
it('returns the filePath for files', () => {
props.diffFile.submodule = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.filePath).toBe(props.diffFile.filePath);
});
@@ -102,7 +111,7 @@ describe('diff_file_header', () => {
it('appends the truncated blob id for submodules', () => {
props.diffFile.submodule = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.filePath).toBe(
`${props.diffFile.filePath} @ ${props.diffFile.blob.id.substr(0, 8)}`,
@@ -114,7 +123,7 @@ describe('diff_file_header', () => {
it('returns a link tag if fileHash is set', () => {
props.diffFile.fileHash = 'some hash';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleTag).toBe('a');
});
@@ -122,7 +131,7 @@ describe('diff_file_header', () => {
it('returns a span tag if fileHash is not set', () => {
props.diffFile.fileHash = null;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.titleTag).toBe('span');
});
@@ -137,7 +146,7 @@ describe('diff_file_header', () => {
});
it('returns true if file is stored in LFS', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.isUsingLfs).toBe(true);
});
@@ -145,7 +154,7 @@ describe('diff_file_header', () => {
it('returns false if file is not stored externally', () => {
props.diffFile.storedExternally = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.isUsingLfs).toBe(false);
});
@@ -153,7 +162,7 @@ describe('diff_file_header', () => {
it('returns false if file is not stored in LFS', () => {
props.diffFile.externalStorage = 'not lfs';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.isUsingLfs).toBe(false);
});
@@ -163,7 +172,7 @@ describe('diff_file_header', () => {
it('returns chevron-down if the diff is expanded', () => {
props.expanded = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.collapseIcon).toBe('chevron-down');
});
@@ -171,49 +180,18 @@ describe('diff_file_header', () => {
it('returns chevron-right if the diff is collapsed', () => {
props.expanded = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.collapseIcon).toBe('chevron-right');
});
});
- describe('isDiscussionsExpanded', () => {
- beforeEach(() => {
- Object.assign(props, {
- discussionsExpanded: true,
- expanded: true,
- });
- });
-
- it('returns true if diff and discussion are expanded', () => {
- vm = mountComponent(Component, props);
-
- expect(vm.isDiscussionsExpanded).toBe(true);
- });
-
- it('returns false if discussion is collapsed', () => {
- props.discussionsExpanded = false;
-
- vm = mountComponent(Component, props);
-
- expect(vm.isDiscussionsExpanded).toBe(false);
- });
-
- it('returns false if diff is collapsed', () => {
- props.expanded = false;
-
- vm = mountComponent(Component, props);
-
- expect(vm.isDiscussionsExpanded).toBe(false);
- });
- });
-
describe('viewFileButtonText', () => {
it('contains the truncated content SHA', () => {
const dummySha = 'deebd00f is no SHA';
props.diffFile.contentSha = dummySha;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.viewFileButtonText).not.toContain(dummySha);
expect(vm.viewFileButtonText).toContain(dummySha.substr(0, 8));
@@ -225,7 +203,7 @@ describe('diff_file_header', () => {
const dummySha = 'deadabba sings no more';
props.diffFile.diffRefs.baseSha = dummySha;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.viewReplacedFileButtonText).not.toContain(dummySha);
expect(vm.viewReplacedFileButtonText).toContain(dummySha.substr(0, 8));
@@ -234,25 +212,25 @@ describe('diff_file_header', () => {
});
describe('methods', () => {
- describe('handleToggle', () => {
+ describe('handleToggleFile', () => {
beforeEach(() => {
spyOn(vm, '$emit').and.stub();
});
it('emits toggleFile if checkTarget is false', () => {
- vm.handleToggle(null, false);
+ vm.handleToggleFile(null, false);
expect(vm.$emit).toHaveBeenCalledWith('toggleFile');
});
it('emits toggleFile if checkTarget is true and event target is header', () => {
- vm.handleToggle({ target: vm.$refs.header }, true);
+ vm.handleToggleFile({ target: vm.$refs.header }, true);
expect(vm.$emit).toHaveBeenCalledWith('toggleFile');
});
it('does not emit toggleFile if checkTarget is true and event target is not header', () => {
- vm.handleToggle({ target: 'not header' }, true);
+ vm.handleToggleFile({ target: 'not header' }, true);
expect(vm.$emit).not.toHaveBeenCalled();
});
@@ -266,7 +244,7 @@ describe('diff_file_header', () => {
it('is visible if collapsible is true', () => {
props.collapsible = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(collapseToggle()).not.toBe(null);
});
@@ -274,14 +252,14 @@ describe('diff_file_header', () => {
it('is hidden if collapsible is false', () => {
props.collapsible = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(collapseToggle()).toBe(null);
});
});
it('displays an file icon in the title', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('svg.js-file-icon use').getAttribute('xlink:href')).toContain(
'ruby',
);
@@ -293,7 +271,7 @@ describe('diff_file_header', () => {
it('displays the path of a added file', () => {
props.diffFile.renamedFile = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(1);
expect(filePaths()[0]).toHaveText(props.diffFile.filePath);
@@ -303,7 +281,7 @@ describe('diff_file_header', () => {
props.diffFile.renamedFile = false;
props.diffFile.deletedFile = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(1);
expect(filePaths()[0]).toHaveText(`${props.diffFile.filePath} deleted`);
@@ -312,7 +290,7 @@ describe('diff_file_header', () => {
it('displays old and new path if the file was renamed', () => {
props.diffFile.renamedFile = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(filePaths()).toHaveLength(2);
expect(filePaths()[0]).toHaveText(props.diffFile.oldPath);
@@ -321,7 +299,7 @@ describe('diff_file_header', () => {
});
it('displays a copy to clipboard button', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
const button = vm.$el.querySelector('.btn-clipboard');
expect(button).not.toBe(null);
@@ -332,7 +310,7 @@ describe('diff_file_header', () => {
it('it displays old and new file mode if it changed', () => {
props.diffFile.modeChanged = true;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
const { fileMode } = vm.$refs;
expect(fileMode).not.toBe(undefined);
@@ -343,7 +321,7 @@ describe('diff_file_header', () => {
it('does not display the file mode if it has not changed', () => {
props.diffFile.modeChanged = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
const { fileMode } = vm.$refs;
expect(fileMode).toBe(undefined);
@@ -359,7 +337,7 @@ describe('diff_file_header', () => {
externalStorage: 'lfs',
});
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(lfsLabel()).not.toBe(null);
expect(lfsLabel()).toHaveText('LFS');
@@ -368,7 +346,7 @@ describe('diff_file_header', () => {
it('does not display the LFS label for files stored in repository', () => {
props.diffFile.storedExternally = false;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(lfsLabel()).toBe(null);
});
@@ -376,7 +354,7 @@ describe('diff_file_header', () => {
describe('edit button', () => {
it('should not render edit button if addMergeRequestButtons is not true', () => {
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
});
@@ -384,7 +362,7 @@ describe('diff_file_header', () => {
it('should show edit button when file is editable', () => {
props.addMergeRequestButtons = true;
props.diffFile.editPath = '/';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toContainText('Edit');
});
@@ -393,7 +371,7 @@ describe('diff_file_header', () => {
props.addMergeRequestButtons = true;
props.diffFile.deletedFile = true;
props.diffFile.editPath = '/';
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
});
@@ -413,7 +391,7 @@ describe('diff_file_header', () => {
props.diffFile.externalUrl = url;
props.diffFile.formattedExternalUrl = title;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector(`a[href="${url}"]`)).not.toBe(null);
expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).not.toBe(null);
@@ -423,11 +401,39 @@ describe('diff_file_header', () => {
props.diffFile.externalUrl = '';
props.diffFile.formattedExternalUrl = title;
- vm = mountComponent(Component, props);
+ vm = mountComponentWithStore(Component, { props, store });
expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).toBe(null);
});
});
});
+
+ describe('handles toggle discussions', () => {
+ it('dispatches toggleFileDiscussions when user clicks on toggle discussions button', () => {
+ const propsCopy = Object.assign({}, props);
+ propsCopy.diffFile.submodule = false;
+ propsCopy.diffFile.blob = {
+ id: '848ed9407c6730ff16edb3dd24485a0eea24292a',
+ path: 'lib/base.js',
+ name: 'base.js',
+ mode: '100644',
+ readableText: true,
+ icon: 'file-text-o',
+ };
+ propsCopy.addMergeRequestButtons = true;
+ propsCopy.diffFile.deletedFile = true;
+
+ vm = mountComponentWithStore(Component, {
+ props: propsCopy,
+ store,
+ });
+
+ spyOn(vm, 'toggleFileDiscussions');
+
+ vm.$el.querySelector('.js-btn-vue-toggle-comments').click();
+
+ expect(vm.toggleFileDiscussions).toHaveBeenCalled();
+ });
+ });
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 1c1edfac68c..7a4616ec8eb 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -11,7 +11,7 @@ describe('DiffFile', () => {
beforeEach(() => {
vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
file: getDiffFileMock(),
- currentUser: {},
+ canCurrentUserFork: false,
}).$mount();
});
@@ -31,12 +31,12 @@ describe('DiffFile', () => {
describe('collapsed', () => {
it('should not have file content', done => {
- expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
+ expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1);
expect(vm.file.collapsed).toEqual(false);
vm.file.collapsed = true;
vm.$nextTick(() => {
- expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(1);
+ expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0);
done();
});
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
index 81cd4f9769a..4600aaea70b 100644
--- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -15,7 +15,7 @@ describe('DiffLineNoteForm', () => {
diffLines = diffFile.highlightedDiffLines;
component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, {
- diffFile,
+ diffFileHash: diffFile.fileHash,
diffLines,
line: diffLines[0],
noteTargetLine: diffLines[0],
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index e1adf60962e..b02328dd359 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -13,7 +13,7 @@ describe('InlineDiffView', () => {
beforeEach(() => {
const diffFile = getDiffFileMock();
- store.dispatch('setInlineDiffViewType');
+ store.dispatch('diffs/setInlineDiffViewType');
component = createComponentWithStore(Vue.extend(InlineDiffView), store, {
diffFile,
diffLines: diffFile.highlightedDiffLines,
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index 6829c1e956a..c1560dac1a0 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -191,4 +191,48 @@ describe('DiffsStoreActions', () => {
);
});
});
+
+ describe('toggleFileDiscussions', () => {
+ it('should dispatch collapseDiscussion when all discussions are expanded', () => {
+ const getters = {
+ getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]),
+ diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(true),
+ diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(false),
+ };
+
+ const dispatch = jasmine.createSpy('dispatch');
+
+ actions.toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith('collapseDiscussion', { discussionId: 1 }, { root: true });
+ });
+
+ it('should dispatch expandDiscussion when all discussions are collapsed', () => {
+ const getters = {
+ getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ id: 1 }]),
+ diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false),
+ diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(true),
+ };
+
+ const dispatch = jasmine.createSpy();
+
+ actions.toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true });
+ });
+
+ it('should dispatch expandDiscussion when some discussions are collapsed and others are expanded for the collapsed discussion', () => {
+ const getters = {
+ getDiffFileDiscussions: jasmine.createSpy().and.returnValue([{ expanded: false, id: 1 }]),
+ diffHasAllExpandedDiscussions: jasmine.createSpy().and.returnValue(false),
+ diffHasAllCollpasedDiscussions: jasmine.createSpy().and.returnValue(false),
+ };
+
+ const dispatch = jasmine.createSpy();
+
+ actions.toggleFileDiscussions({ getters, dispatch });
+
+ expect(dispatch).toHaveBeenCalledWith('expandDiscussion', { discussionId: 1 }, { root: true });
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index 7945ddea911..6210d4a7124 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -1,24 +1,206 @@
-import getters from '~/diffs/store/getters';
+import * as getters from '~/diffs/store/getters';
+import state from '~/diffs/store/modules/diff_state';
import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import discussion from '../mock_data/diff_discussions';
+
+describe('Diffs Module Getters', () => {
+ let localState;
+ let discussionMock;
+ let discussionMock1;
+
+ const diffFileMock = {
+ fileHash: '9732849daca6ae818696d9575f5d1207d1a7f8bb',
+ };
+
+ beforeEach(() => {
+ localState = state();
+ discussionMock = Object.assign({}, discussion);
+ discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+
+ discussionMock1 = Object.assign({}, discussion);
+ discussionMock1.diff_file.file_hash = diffFileMock.fileHash;
+ });
-describe('DiffsStoreGetters', () => {
describe('isParallelView', () => {
it('should return true if view set to parallel view', () => {
- expect(getters.isParallelView({ diffViewType: PARALLEL_DIFF_VIEW_TYPE })).toBeTruthy();
+ localState.diffViewType = PARALLEL_DIFF_VIEW_TYPE;
+
+ expect(getters.isParallelView(localState)).toEqual(true);
});
it('should return false if view not to parallel view', () => {
- expect(getters.isParallelView({ diffViewType: 'foo' })).toBeFalsy();
+ localState.diffViewType = INLINE_DIFF_VIEW_TYPE;
+
+ expect(getters.isParallelView(localState)).toEqual(false);
});
});
describe('isInlineView', () => {
it('should return true if view set to inline view', () => {
- expect(getters.isInlineView({ diffViewType: INLINE_DIFF_VIEW_TYPE })).toBeTruthy();
+ localState.diffViewType = INLINE_DIFF_VIEW_TYPE;
+
+ expect(getters.isInlineView(localState)).toEqual(true);
});
it('should return false if view not to inline view', () => {
- expect(getters.isInlineView({ diffViewType: PARALLEL_DIFF_VIEW_TYPE })).toBeFalsy();
+ localState.diffViewType = PARALLEL_DIFF_VIEW_TYPE;
+
+ expect(getters.isInlineView(localState)).toEqual(false);
+ });
+ });
+
+ describe('areAllFilesCollapsed', () => {
+ it('returns true when all files are collapsed', () => {
+ localState.diffFiles = [{ collapsed: true }, { collapsed: true }];
+ expect(getters.areAllFilesCollapsed(localState)).toEqual(true);
+ });
+
+ it('returns false when at least one file is not collapsed', () => {
+ localState.diffFiles = [{ collapsed: false }, { collapsed: true }];
+ expect(getters.areAllFilesCollapsed(localState)).toEqual(false);
+ });
+ });
+
+ describe('commitId', () => {
+ it('returns commit id when is set', () => {
+ const commitID = '800f7a91';
+ localState.commit = {
+ id: commitID,
+ };
+
+ expect(getters.commitId(localState)).toEqual(commitID);
+ });
+
+ it('returns null when no commit is set', () => {
+ expect(getters.commitId(localState)).toEqual(null);
+ });
+ });
+
+ describe('diffHasAllExpandedDiscussions', () => {
+ it('returns true when all discussions are expanded', () => {
+ expect(
+ getters.diffHasAllExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when there are no discussions', () => {
+ expect(
+ getters.diffHasAllExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+
+ it('returns false when one discussions is collapsed', () => {
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasAllExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock1],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('diffHasAllCollpasedDiscussions', () => {
+ it('returns true when all discussions are collapsed', () => {
+ discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+ discussionMock.expanded = false;
+
+ expect(
+ getters.diffHasAllCollpasedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when there are no discussions', () => {
+ expect(
+ getters.diffHasAllCollpasedDiscussions(localState, {
+ getDiffFileDiscussions: () => [],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+
+ it('returns false when one discussions is expanded', () => {
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasAllCollpasedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock1],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('diffHasExpandedDiscussions', () => {
+ it('returns true when one of the discussions is expanded', () => {
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock],
+ })(diffFileMock),
+ ).toEqual(true);
+ });
+
+ it('returns false when there are no discussions', () => {
+ expect(
+ getters.diffHasExpandedDiscussions(localState, { getDiffFileDiscussions: () => [] })(
+ diffFileMock,
+ ),
+ ).toEqual(false);
+ });
+
+ it('returns false when no discussion is expanded', () => {
+ discussionMock.expanded = false;
+ discussionMock1.expanded = false;
+
+ expect(
+ getters.diffHasExpandedDiscussions(localState, {
+ getDiffFileDiscussions: () => [discussionMock, discussionMock1],
+ })(diffFileMock),
+ ).toEqual(false);
+ });
+ });
+
+ describe('getDiffFileDiscussions', () => {
+ it('returns an array with discussions when fileHash matches and the discussion belongs to a diff', () => {
+ discussionMock.diff_file.file_hash = diffFileMock.fileHash;
+
+ expect(
+ getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [discussionMock] })(
+ diffFileMock,
+ ).length,
+ ).toEqual(1);
+ });
+
+ it('returns an empty array when no discussions are found in the given diff', () => {
+ expect(
+ getters.getDiffFileDiscussions(localState, {}, {}, { discussions: [] })(diffFileMock)
+ .length,
+ ).toEqual(0);
+ });
+ });
+
+ describe('getDiffFileByHash', () => {
+ it('returns file by hash', () => {
+ const fileA = {
+ fileHash: '123',
+ };
+ const fileB = {
+ fileHash: '456',
+ };
+ localState.diffFiles = [fileA, fileB];
+
+ expect(getters.getDiffFileByHash(localState)('456')).toEqual(fileB);
+ });
+
+ it('returns null if no matching file is found', () => {
+ localState.diffFiles = [];
+ expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined();
});
});
});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 7a34126eef7..0b933dda431 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -104,7 +104,7 @@ describe('Environment item', () => {
},
],
},
- 'stop_action?': true,
+ has_stop_action: true,
environment_path: 'root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-10T15:55:58.778Z',
diff --git a/spec/javascripts/environments/environment_rollback_spec.js b/spec/javascripts/environments/environment_rollback_spec.js
index eb8e49d81fe..79f33c5bc8a 100644
--- a/spec/javascripts/environments/environment_rollback_spec.js
+++ b/spec/javascripts/environments/environment_rollback_spec.js
@@ -18,7 +18,7 @@ describe('Rollback Component', () => {
},
}).$mount();
- expect(component.$el.querySelector('span').textContent).toContain('Re-deploy');
+ expect(component.$el).toHaveSpriteIcon('repeat');
});
it('Should render Rollback label when isLastDeployment is false', () => {
@@ -30,6 +30,6 @@ describe('Rollback Component', () => {
},
}).$mount();
- expect(component.$el.querySelector('span').textContent).toContain('Rollback');
+ expect(component.$el).toHaveSpriteIcon('redo');
});
});
diff --git a/spec/javascripts/environments/environment_stop_spec.js b/spec/javascripts/environments/environment_stop_spec.js
index 3f95faf466a..4d9caa57566 100644
--- a/spec/javascripts/environments/environment_stop_spec.js
+++ b/spec/javascripts/environments/environment_stop_spec.js
@@ -4,7 +4,6 @@ import stopComp from '~/environments/components/environment_stop.vue';
describe('Stop Component', () => {
let StopComponent;
let component;
- const stopURL = '/stop';
beforeEach(() => {
StopComponent = Vue.extend(stopComp);
@@ -12,20 +11,13 @@ describe('Stop Component', () => {
component = new StopComponent({
propsData: {
- stopUrl: stopURL,
+ environment: {},
},
}).$mount();
});
- describe('computed', () => {
- it('title', () => {
- expect(component.title).toEqual('Stop');
- });
- });
-
it('should render a button to stop the environment', () => {
expect(component.$el.tagName).toEqual('BUTTON');
- expect(component.$el.getAttribute('data-original-title')).toEqual('Stop');
- expect(component.$el.getAttribute('aria-label')).toEqual('Stop');
+ expect(component.$el.getAttribute('data-original-title')).toEqual('Stop environment');
});
});
diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js
index 8a1e26935d9..7bb57f938b8 100644
--- a/spec/javascripts/environments/mock_data.js
+++ b/spec/javascripts/environments/mock_data.js
@@ -7,7 +7,7 @@ export const environmentsList = [
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -22,7 +22,7 @@ export const environmentsList = [
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
@@ -41,7 +41,7 @@ export const serverData = [
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -58,7 +58,7 @@ export const serverData = [
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
@@ -77,7 +77,7 @@ export const environment = {
external_url: null,
environment_type: null,
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/7',
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
@@ -95,7 +95,7 @@ export const folder = {
external_url: null,
environment_type: 'build',
last_deployment: null,
- 'stop_action?': false,
+ has_stop_action: false,
environment_path: '/root/review-app/environments/12',
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
diff --git a/spec/javascripts/fixtures/commit.rb b/spec/javascripts/fixtures/commit.rb
index 351db6ba184..24ab8159a18 100644
--- a/spec/javascripts/fixtures/commit.rb
+++ b/spec/javascripts/fixtures/commit.rb
@@ -14,7 +14,7 @@ describe Projects::CommitController, '(JavaScript fixtures)', type: :controller
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/javascripts/fixtures/groups.rb
index 35be52fbf97..a2035ceae15 100644
--- a/spec/javascripts/fixtures/groups.rb
+++ b/spec/javascripts/fixtures/groups.rb
@@ -13,7 +13,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
end
before do
- group.add_master(admin)
+ group.add_maintainer(admin)
sign_in(admin)
end
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb
index e8865b04874..57c78182abc 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/javascripts/fixtures/projects.rb
@@ -17,7 +17,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
before do
- project.add_master(admin)
+ project.add_maintainer(admin)
sign_in(admin)
end
diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js
new file mode 100644
index 00000000000..834f919524d
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/app_spec.js
@@ -0,0 +1,251 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import Vue from 'vue';
+import appComponent from '~/frequent_items/components/app.vue';
+import eventHub from '~/frequent_items/event_hub';
+import store from '~/frequent_items/store';
+import { FREQUENT_ITEMS, HOUR_IN_MS } from '~/frequent_items/constants';
+import { getTopFrequentItems } from '~/frequent_items/utils';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { currentSession, mockFrequentProjects, mockSearchedProjects } from '../mock_data';
+
+let session;
+const createComponentWithStore = (namespace = 'projects') => {
+ session = currentSession[namespace];
+ gon.api_version = session.apiVersion;
+ const Component = Vue.extend(appComponent);
+
+ return mountComponentWithStore(Component, {
+ store,
+ props: {
+ namespace,
+ currentUserName: session.username,
+ currentItem: session.project || session.group,
+ },
+ });
+};
+
+describe('Frequent Items App Component', () => {
+ let vm;
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ vm = createComponentWithStore();
+ });
+
+ afterEach(() => {
+ mock.restore();
+ vm.$destroy();
+ });
+
+ describe('methods', () => {
+ describe('dropdownOpenHandler', () => {
+ it('should fetch frequent items when no search has been previously made on desktop', () => {
+ spyOn(vm, 'fetchFrequentItems');
+
+ vm.dropdownOpenHandler();
+
+ expect(vm.fetchFrequentItems).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('logItemAccess', () => {
+ let storage;
+
+ beforeEach(() => {
+ storage = {};
+
+ spyOn(window.localStorage, 'setItem').and.callFake((storageKey, value) => {
+ storage[storageKey] = value;
+ });
+
+ spyOn(window.localStorage, 'getItem').and.callFake(storageKey => {
+ if (storage[storageKey]) {
+ return storage[storageKey];
+ }
+
+ return null;
+ });
+ });
+
+ it('should create a project store if it does not exist and adds a project', () => {
+ vm.logItemAccess(session.storageKey, session.project);
+
+ const projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects.length).toBe(1);
+ expect(projects[0].frequency).toBe(1);
+ expect(projects[0].lastAccessedOn).toBeDefined();
+ });
+
+ it('should prevent inserting same report multiple times into store', () => {
+ vm.logItemAccess(session.storageKey, session.project);
+ vm.logItemAccess(session.storageKey, session.project);
+
+ const projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects.length).toBe(1);
+ });
+
+ it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
+ let projects;
+ const newTimestamp = Date.now() + HOUR_IN_MS + 1;
+
+ vm.logItemAccess(session.storageKey, session.project);
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].frequency).toBe(1);
+
+ vm.logItemAccess(session.storageKey, {
+ ...session.project,
+ lastAccessedOn: newTimestamp,
+ });
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].frequency).toBe(2);
+ expect(projects[0].lastAccessedOn).not.toBe(session.project.lastAccessedOn);
+ });
+
+ it('should always update project metadata', () => {
+ let projects;
+ const oldProject = {
+ ...session.project,
+ };
+
+ const newProject = {
+ ...session.project,
+ name: 'New Name',
+ avatarUrl: 'new/avatar.png',
+ namespace: 'New / Namespace',
+ webUrl: 'http://localhost/new/web/url',
+ };
+
+ vm.logItemAccess(session.storageKey, oldProject);
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].name).toBe(oldProject.name);
+ expect(projects[0].avatarUrl).toBe(oldProject.avatarUrl);
+ expect(projects[0].namespace).toBe(oldProject.namespace);
+ expect(projects[0].webUrl).toBe(oldProject.webUrl);
+
+ vm.logItemAccess(session.storageKey, newProject);
+ projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects[0].name).toBe(newProject.name);
+ expect(projects[0].avatarUrl).toBe(newProject.avatarUrl);
+ expect(projects[0].namespace).toBe(newProject.namespace);
+ expect(projects[0].webUrl).toBe(newProject.webUrl);
+ });
+
+ it('should not add more than 20 projects in store', () => {
+ for (let id = 0; id < FREQUENT_ITEMS.MAX_COUNT; id += 1) {
+ const project = {
+ ...session.project,
+ id,
+ };
+ vm.logItemAccess(session.storageKey, project);
+ }
+
+ const projects = JSON.parse(storage[session.storageKey]);
+
+ expect(projects.length).toBe(FREQUENT_ITEMS.MAX_COUNT);
+ });
+ });
+ });
+
+ describe('created', () => {
+ it('should bind event listeners on eventHub', done => {
+ spyOn(eventHub, '$on');
+
+ createComponentWithStore().$mount();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$on).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function));
+ done();
+ });
+ });
+ });
+
+ describe('beforeDestroy', () => {
+ it('should unbind event listeners on eventHub', done => {
+ spyOn(eventHub, '$off');
+
+ vm.$mount();
+ vm.$destroy();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$off).toHaveBeenCalledWith('projects-dropdownOpen', jasmine.any(Function));
+ done();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render search input', () => {
+ expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
+ });
+
+ it('should render loading animation', done => {
+ vm.$store.dispatch('fetchSearchedItems');
+
+ Vue.nextTick(() => {
+ const loadingEl = vm.$el.querySelector('.loading-animation');
+
+ expect(loadingEl).toBeDefined();
+ expect(loadingEl.classList.contains('prepend-top-20')).toBe(true);
+ expect(loadingEl.querySelector('i').getAttribute('aria-label')).toBe('Loading projects');
+ done();
+ });
+ });
+
+ it('should render frequent projects list header', done => {
+ Vue.nextTick(() => {
+ const sectionHeaderEl = vm.$el.querySelector('.section-header');
+
+ expect(sectionHeaderEl).toBeDefined();
+ expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited');
+ done();
+ });
+ });
+
+ it('should render frequent projects list', done => {
+ const expectedResult = getTopFrequentItems(mockFrequentProjects);
+ spyOn(window.localStorage, 'getItem').and.callFake(() =>
+ JSON.stringify(mockFrequentProjects),
+ );
+
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
+
+ vm.fetchFrequentItems();
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
+ expectedResult.length,
+ );
+ done();
+ });
+ });
+
+ it('should render searched projects list', done => {
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects);
+
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1);
+
+ vm.$store.dispatch('setSearchQuery', 'gitlab');
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
+ })
+ .then(vm.$nextTick)
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
+ mockSearchedProjects.length,
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
index c193258474e..201aca77b10 100644
--- a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
+++ b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js
@@ -1,23 +1,21 @@
import Vue from 'vue';
-
-import projectsListItemComponent from '~/projects_dropdown/components/projects_list_item.vue';
-
+import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { mockProject } from '../mock_data';
+import { mockProject } from '../mock_data'; // can also use 'mockGroup', but not useful to test here
const createComponent = () => {
- const Component = Vue.extend(projectsListItemComponent);
+ const Component = Vue.extend(frequentItemsListItemComponent);
return mountComponent(Component, {
- projectId: mockProject.id,
- projectName: mockProject.name,
+ itemId: mockProject.id,
+ itemName: mockProject.name,
namespace: mockProject.namespace,
webUrl: mockProject.webUrl,
avatarUrl: mockProject.avatarUrl,
});
};
-describe('ProjectsListItemComponent', () => {
+describe('FrequentItemsListItemComponent', () => {
let vm;
beforeEach(() => {
@@ -32,22 +30,22 @@ describe('ProjectsListItemComponent', () => {
describe('hasAvatar', () => {
it('should return `true` or `false` if whether avatar is present or not', () => {
vm.avatarUrl = 'path/to/avatar.png';
- expect(vm.hasAvatar).toBeTruthy();
+ expect(vm.hasAvatar).toBe(true);
vm.avatarUrl = null;
- expect(vm.hasAvatar).toBeFalsy();
+ expect(vm.hasAvatar).toBe(false);
});
});
- describe('highlightedProjectName', () => {
+ describe('highlightedItemName', () => {
it('should enclose part of project name in <b> & </b> which matches with `matcher` prop', () => {
vm.matcher = 'lab';
- expect(vm.highlightedProjectName).toContain('<b>Lab</b>');
+ expect(vm.highlightedItemName).toContain('<b>Lab</b>');
});
it('should return project name as it is if `matcher` is not available', () => {
vm.matcher = null;
- expect(vm.highlightedProjectName).toBe(mockProject.name);
+ expect(vm.highlightedItemName).toBe(mockProject.name);
});
});
@@ -66,12 +64,12 @@ describe('ProjectsListItemComponent', () => {
describe('template', () => {
it('should render component element', () => {
- expect(vm.$el.classList.contains('projects-list-item-container')).toBeTruthy();
+ expect(vm.$el.classList.contains('frequent-items-list-item-container')).toBeTruthy();
expect(vm.$el.querySelectorAll('a').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-item-avatar-container').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-item-metadata-container').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-title').length).toBe(1);
- expect(vm.$el.querySelectorAll('.project-namespace').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-avatar-container').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-metadata-container').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-title').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.frequent-items-item-namespace').length).toBe(1);
});
});
});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
new file mode 100644
index 00000000000..3003b7ee000
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js
@@ -0,0 +1,84 @@
+import Vue from 'vue';
+import frequentItemsListComponent from '~/frequent_items/components/frequent_items_list.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { mockFrequentProjects } from '../mock_data';
+
+const createComponent = (namespace = 'projects') => {
+ const Component = Vue.extend(frequentItemsListComponent);
+
+ return mountComponent(Component, {
+ namespace,
+ items: mockFrequentProjects,
+ isFetchFailed: false,
+ hasSearchQuery: false,
+ matcher: 'lab',
+ });
+};
+
+describe('FrequentItemsListComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('isListEmpty', () => {
+ it('should return `true` or `false` representing whether if `items` is empty or not with projects', () => {
+ vm.items = [];
+ expect(vm.isListEmpty).toBe(true);
+
+ vm.items = mockFrequentProjects;
+ expect(vm.isListEmpty).toBe(false);
+ });
+ });
+
+ describe('fetched item messages', () => {
+ it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', () => {
+ vm.isFetchFailed = true;
+ expect(vm.listEmptyMessage).toBe('This feature requires browser localStorage support');
+
+ vm.isFetchFailed = false;
+ expect(vm.listEmptyMessage).toBe('Projects you visit often will appear here');
+ });
+ });
+
+ describe('searched item messages', () => {
+ it('should return appropriate empty list message based on value of `searchFailed` prop with projects', () => {
+ vm.hasSearchQuery = true;
+ vm.isFetchFailed = true;
+ expect(vm.listEmptyMessage).toBe('Something went wrong on our end.');
+
+ vm.isFetchFailed = false;
+ expect(vm.listEmptyMessage).toBe('Sorry, no projects matched your search');
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render component element with list of projects', done => {
+ vm.items = mockFrequentProjects;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.classList.contains('frequent-items-list-container')).toBe(true);
+ expect(vm.$el.querySelectorAll('ul.list-unstyled').length).toBe(1);
+ expect(vm.$el.querySelectorAll('li.frequent-items-list-item-container').length).toBe(5);
+ done();
+ });
+ });
+
+ it('should render component element with empty message', done => {
+ vm.items = [];
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelectorAll('li.section-empty').length).toBe(1);
+ expect(vm.$el.querySelectorAll('li.frequent-items-list-item-container').length).toBe(0);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
new file mode 100644
index 00000000000..6a11038e70a
--- /dev/null
+++ b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js
@@ -0,0 +1,77 @@
+import Vue from 'vue';
+import searchComponent from '~/frequent_items/components/frequent_items_search_input.vue';
+import eventHub from '~/frequent_items/event_hub';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const createComponent = (namespace = 'projects') => {
+ const Component = Vue.extend(searchComponent);
+
+ return mountComponent(Component, { namespace });
+};
+
+describe('FrequentItemsSearchInputComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('methods', () => {
+ describe('setFocus', () => {
+ it('should set focus to search input', () => {
+ spyOn(vm.$refs.search, 'focus');
+
+ vm.setFocus();
+ expect(vm.$refs.search.focus).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('mounted', () => {
+ it('should listen `dropdownOpen` event', done => {
+ spyOn(eventHub, '$on');
+ const vmX = createComponent();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$on).toHaveBeenCalledWith(
+ `${vmX.namespace}-dropdownOpen`,
+ jasmine.any(Function),
+ );
+ done();
+ });
+ });
+ });
+
+ describe('beforeDestroy', () => {
+ it('should unbind event listeners on eventHub', done => {
+ const vmX = createComponent();
+ spyOn(eventHub, '$off');
+
+ vmX.$mount();
+ vmX.$destroy();
+
+ Vue.nextTick(() => {
+ expect(eventHub.$off).toHaveBeenCalledWith(
+ `${vmX.namespace}-dropdownOpen`,
+ jasmine.any(Function),
+ );
+ done();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('should render component element', () => {
+ const inputEl = vm.$el.querySelector('input.form-control');
+
+ expect(vm.$el.classList.contains('search-input-container')).toBeTruthy();
+ expect(inputEl).not.toBe(null);
+ expect(inputEl.getAttribute('placeholder')).toBe('Search your projects');
+ expect(vm.$el.querySelector('.search-icon')).toBeDefined();
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/mock_data.js b/spec/javascripts/frequent_items/mock_data.js
new file mode 100644
index 00000000000..cf3602f42d6
--- /dev/null
+++ b/spec/javascripts/frequent_items/mock_data.js
@@ -0,0 +1,168 @@
+export const currentSession = {
+ groups: {
+ username: 'root',
+ storageKey: 'root/frequent-groups',
+ apiVersion: 'v4',
+ group: {
+ id: 1,
+ name: 'dummy-group',
+ full_name: 'dummy-parent-group',
+ webUrl: `${gl.TEST_HOST}/dummy-group`,
+ avatarUrl: null,
+ lastAccessedOn: Date.now(),
+ },
+ },
+ projects: {
+ username: 'root',
+ storageKey: 'root/frequent-projects',
+ apiVersion: 'v4',
+ project: {
+ id: 1,
+ name: 'dummy-project',
+ namespace: 'SampleGroup / Dummy-Project',
+ webUrl: `${gl.TEST_HOST}/samplegroup/dummy-project`,
+ avatarUrl: null,
+ lastAccessedOn: Date.now(),
+ },
+ },
+};
+
+export const mockNamespace = 'projects';
+
+export const mockStorageKey = 'test-user/frequent-projects';
+
+export const mockGroup = {
+ id: 1,
+ name: 'Sub451',
+ namespace: 'Commit451 / Sub451',
+ webUrl: `${gl.TEST_HOST}/Commit451/Sub451`,
+ avatarUrl: null,
+};
+
+export const mockRawGroup = {
+ id: 1,
+ name: 'Sub451',
+ full_name: 'Commit451 / Sub451',
+ web_url: `${gl.TEST_HOST}/Commit451/Sub451`,
+ avatar_url: null,
+};
+
+export const mockFrequentGroups = [
+ {
+ id: 3,
+ name: 'Subgroup451',
+ full_name: 'Commit451 / Subgroup451',
+ webUrl: '/Commit451/Subgroup451',
+ avatarUrl: null,
+ frequency: 7,
+ lastAccessedOn: 1497979281815,
+ },
+ {
+ id: 1,
+ name: 'Commit451',
+ full_name: 'Commit451',
+ webUrl: '/Commit451',
+ avatarUrl: null,
+ frequency: 3,
+ lastAccessedOn: 1497979281815,
+ },
+];
+
+export const mockSearchedGroups = [mockRawGroup];
+export const mockProcessedSearchedGroups = [mockGroup];
+
+export const mockProject = {
+ id: 1,
+ name: 'GitLab Community Edition',
+ namespace: 'gitlab-org / gitlab-ce',
+ webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ce`,
+ avatarUrl: null,
+};
+
+export const mockRawProject = {
+ id: 1,
+ name: 'GitLab Community Edition',
+ name_with_namespace: 'gitlab-org / gitlab-ce',
+ web_url: `${gl.TEST_HOST}/gitlab-org/gitlab-ce`,
+ avatar_url: null,
+};
+
+export const mockFrequentProjects = [
+ {
+ id: 1,
+ name: 'GitLab Community Edition',
+ namespace: 'gitlab-org / gitlab-ce',
+ webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ce`,
+ avatarUrl: null,
+ frequency: 1,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 2,
+ name: 'GitLab CI',
+ namespace: 'gitlab-org / gitlab-ci',
+ webUrl: `${gl.TEST_HOST}/gitlab-org/gitlab-ci`,
+ avatarUrl: null,
+ frequency: 9,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 3,
+ name: 'Typeahead.Js',
+ namespace: 'twitter / typeahead-js',
+ webUrl: `${gl.TEST_HOST}/twitter/typeahead-js`,
+ avatarUrl: '/uploads/-/system/project/avatar/7/TWBS.png',
+ frequency: 2,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 4,
+ name: 'Intel',
+ namespace: 'platform / hardware / bsp / intel',
+ webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/intel`,
+ avatarUrl: null,
+ frequency: 3,
+ lastAccessedOn: Date.now(),
+ },
+ {
+ id: 5,
+ name: 'v4.4',
+ namespace: 'platform / hardware / bsp / kernel / common / v4.4',
+ webUrl: `${gl.TEST_HOST}/platform/hardware/bsp/kernel/common/v4.4`,
+ avatarUrl: null,
+ frequency: 8,
+ lastAccessedOn: Date.now(),
+ },
+];
+
+export const mockSearchedProjects = [mockRawProject];
+export const mockProcessedSearchedProjects = [mockProject];
+
+export const unsortedFrequentItems = [
+ { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
+ { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
+ { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
+ { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
+ { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
+ { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
+ { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
+ { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
+ { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
+];
+
+/**
+ * This const has a specific order which tests authenticity
+ * of `getTopFrequentItems` method so
+ * DO NOT change order of items in this const.
+ */
+export const sortedFrequentItems = [
+ { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
+ { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
+ { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
+ { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
+ { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
+ { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
+ { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
+ { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
+ { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
+];
diff --git a/spec/javascripts/frequent_items/store/actions_spec.js b/spec/javascripts/frequent_items/store/actions_spec.js
new file mode 100644
index 00000000000..0a8525e77d6
--- /dev/null
+++ b/spec/javascripts/frequent_items/store/actions_spec.js
@@ -0,0 +1,225 @@
+import testAction from 'spec/helpers/vuex_action_helper';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import AccessorUtilities from '~/lib/utils/accessor';
+import * as actions from '~/frequent_items/store/actions';
+import * as types from '~/frequent_items/store/mutation_types';
+import state from '~/frequent_items/store/state';
+import {
+ mockNamespace,
+ mockStorageKey,
+ mockFrequentProjects,
+ mockSearchedProjects,
+} from '../mock_data';
+
+describe('Frequent Items Dropdown Store Actions', () => {
+ let mockedState;
+ let mock;
+
+ beforeEach(() => {
+ mockedState = state();
+ mock = new MockAdapter(axios);
+
+ mockedState.namespace = mockNamespace;
+ mockedState.storageKey = mockStorageKey;
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('setNamespace', () => {
+ it('should set namespace', done => {
+ testAction(
+ actions.setNamespace,
+ mockNamespace,
+ mockedState,
+ [{ type: types.SET_NAMESPACE, payload: mockNamespace }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setStorageKey', () => {
+ it('should set storage key', done => {
+ testAction(
+ actions.setStorageKey,
+ mockStorageKey,
+ mockedState,
+ [{ type: types.SET_STORAGE_KEY, payload: mockStorageKey }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestFrequentItems', () => {
+ it('should request frequent items', done => {
+ testAction(
+ actions.requestFrequentItems,
+ null,
+ mockedState,
+ [{ type: types.REQUEST_FREQUENT_ITEMS }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveFrequentItemsSuccess', () => {
+ it('should set frequent items', done => {
+ testAction(
+ actions.receiveFrequentItemsSuccess,
+ mockFrequentProjects,
+ mockedState,
+ [{ type: types.RECEIVE_FREQUENT_ITEMS_SUCCESS, payload: mockFrequentProjects }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveFrequentItemsError', () => {
+ it('should set frequent items error state', done => {
+ testAction(
+ actions.receiveFrequentItemsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_FREQUENT_ITEMS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchFrequentItems', () => {
+ it('should dispatch `receiveFrequentItemsSuccess`', done => {
+ mockedState.namespace = mockNamespace;
+ mockedState.storageKey = mockStorageKey;
+
+ testAction(
+ actions.fetchFrequentItems,
+ null,
+ mockedState,
+ [],
+ [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsSuccess', payload: [] }],
+ done,
+ );
+ });
+
+ it('should dispatch `receiveFrequentItemsError`', done => {
+ spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(false);
+ mockedState.namespace = mockNamespace;
+ mockedState.storageKey = mockStorageKey;
+
+ testAction(
+ actions.fetchFrequentItems,
+ null,
+ mockedState,
+ [],
+ [{ type: 'requestFrequentItems' }, { type: 'receiveFrequentItemsError' }],
+ done,
+ );
+ });
+ });
+
+ describe('requestSearchedItems', () => {
+ it('should request searched items', done => {
+ testAction(
+ actions.requestSearchedItems,
+ null,
+ mockedState,
+ [{ type: types.REQUEST_SEARCHED_ITEMS }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveSearchedItemsSuccess', () => {
+ it('should set searched items', done => {
+ testAction(
+ actions.receiveSearchedItemsSuccess,
+ mockSearchedProjects,
+ mockedState,
+ [{ type: types.RECEIVE_SEARCHED_ITEMS_SUCCESS, payload: mockSearchedProjects }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveSearchedItemsError', () => {
+ it('should set searched items error state', done => {
+ testAction(
+ actions.receiveSearchedItemsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_SEARCHED_ITEMS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchSearchedItems', () => {
+ beforeEach(() => {
+ gon.api_version = 'v4';
+ });
+
+ it('should dispatch `receiveSearchedItemsSuccess`', done => {
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(200, mockSearchedProjects);
+
+ testAction(
+ actions.fetchSearchedItems,
+ null,
+ mockedState,
+ [],
+ [
+ { type: 'requestSearchedItems' },
+ { type: 'receiveSearchedItemsSuccess', payload: mockSearchedProjects },
+ ],
+ done,
+ );
+ });
+
+ it('should dispatch `receiveSearchedItemsError`', done => {
+ gon.api_version = 'v4';
+ mock.onGet(/\/api\/v4\/projects.json(.*)$/).replyOnce(500);
+
+ testAction(
+ actions.fetchSearchedItems,
+ null,
+ mockedState,
+ [],
+ [{ type: 'requestSearchedItems' }, { type: 'receiveSearchedItemsError' }],
+ done,
+ );
+ });
+ });
+
+ describe('setSearchQuery', () => {
+ it('should commit query and dispatch `fetchSearchedItems` when query is present', done => {
+ testAction(
+ actions.setSearchQuery,
+ { query: 'test' },
+ mockedState,
+ [{ type: types.SET_SEARCH_QUERY, payload: { query: 'test' } }],
+ [{ type: 'fetchSearchedItems', payload: { query: 'test' } }],
+ done,
+ );
+ });
+
+ it('should commit query and dispatch `fetchFrequentItems` when query is empty', done => {
+ testAction(
+ actions.setSearchQuery,
+ null,
+ mockedState,
+ [{ type: types.SET_SEARCH_QUERY, payload: null }],
+ [{ type: 'fetchFrequentItems' }],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/store/getters_spec.js b/spec/javascripts/frequent_items/store/getters_spec.js
new file mode 100644
index 00000000000..1cd12eb6832
--- /dev/null
+++ b/spec/javascripts/frequent_items/store/getters_spec.js
@@ -0,0 +1,24 @@
+import state from '~/frequent_items/store/state';
+import * as getters from '~/frequent_items/store/getters';
+
+describe('Frequent Items Dropdown Store Getters', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('hasSearchQuery', () => {
+ it('should return `true` when search query is present', () => {
+ mockedState.searchQuery = 'test';
+
+ expect(getters.hasSearchQuery(mockedState)).toBe(true);
+ });
+
+ it('should return `false` when search query is empty', () => {
+ mockedState.searchQuery = '';
+
+ expect(getters.hasSearchQuery(mockedState)).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/store/mutations_spec.js b/spec/javascripts/frequent_items/store/mutations_spec.js
new file mode 100644
index 00000000000..d36964b2600
--- /dev/null
+++ b/spec/javascripts/frequent_items/store/mutations_spec.js
@@ -0,0 +1,117 @@
+import state from '~/frequent_items/store/state';
+import mutations from '~/frequent_items/store/mutations';
+import * as types from '~/frequent_items/store/mutation_types';
+import {
+ mockNamespace,
+ mockStorageKey,
+ mockFrequentProjects,
+ mockSearchedProjects,
+ mockProcessedSearchedProjects,
+ mockSearchedGroups,
+ mockProcessedSearchedGroups,
+} from '../mock_data';
+
+describe('Frequent Items dropdown mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_NAMESPACE', () => {
+ it('should set namespace', () => {
+ mutations[types.SET_NAMESPACE](stateCopy, mockNamespace);
+
+ expect(stateCopy.namespace).toEqual(mockNamespace);
+ });
+ });
+
+ describe('SET_STORAGE_KEY', () => {
+ it('should set storage key', () => {
+ mutations[types.SET_STORAGE_KEY](stateCopy, mockStorageKey);
+
+ expect(stateCopy.storageKey).toEqual(mockStorageKey);
+ });
+ });
+
+ describe('SET_SEARCH_QUERY', () => {
+ it('should set search query', () => {
+ const searchQuery = 'gitlab-ce';
+
+ mutations[types.SET_SEARCH_QUERY](stateCopy, searchQuery);
+
+ expect(stateCopy.searchQuery).toEqual(searchQuery);
+ });
+ });
+
+ describe('REQUEST_FREQUENT_ITEMS', () => {
+ it('should set view states when requesting frequent items', () => {
+ mutations[types.REQUEST_FREQUENT_ITEMS](stateCopy);
+
+ expect(stateCopy.isLoadingItems).toEqual(true);
+ expect(stateCopy.hasSearchQuery).toEqual(false);
+ });
+ });
+
+ describe('RECEIVE_FREQUENT_ITEMS_SUCCESS', () => {
+ it('should set view states when receiving frequent items', () => {
+ mutations[types.RECEIVE_FREQUENT_ITEMS_SUCCESS](stateCopy, mockFrequentProjects);
+
+ expect(stateCopy.items).toEqual(mockFrequentProjects);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(false);
+ expect(stateCopy.isFetchFailed).toEqual(false);
+ });
+ });
+
+ describe('RECEIVE_FREQUENT_ITEMS_ERROR', () => {
+ it('should set items and view states when error occurs retrieving frequent items', () => {
+ mutations[types.RECEIVE_FREQUENT_ITEMS_ERROR](stateCopy);
+
+ expect(stateCopy.items).toEqual([]);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(false);
+ expect(stateCopy.isFetchFailed).toEqual(true);
+ });
+ });
+
+ describe('REQUEST_SEARCHED_ITEMS', () => {
+ it('should set view states when requesting searched items', () => {
+ mutations[types.REQUEST_SEARCHED_ITEMS](stateCopy);
+
+ expect(stateCopy.isLoadingItems).toEqual(true);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_SEARCHED_ITEMS_SUCCESS', () => {
+ it('should set items and view states when receiving searched items', () => {
+ mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedProjects);
+
+ expect(stateCopy.items).toEqual(mockProcessedSearchedProjects);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ expect(stateCopy.isFetchFailed).toEqual(false);
+ });
+
+ it('should also handle the different `full_name` key for namespace in groups payload', () => {
+ mutations[types.RECEIVE_SEARCHED_ITEMS_SUCCESS](stateCopy, mockSearchedGroups);
+
+ expect(stateCopy.items).toEqual(mockProcessedSearchedGroups);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ expect(stateCopy.isFetchFailed).toEqual(false);
+ });
+ });
+
+ describe('RECEIVE_SEARCHED_ITEMS_ERROR', () => {
+ it('should set view states when error occurs retrieving searched items', () => {
+ mutations[types.RECEIVE_SEARCHED_ITEMS_ERROR](stateCopy);
+
+ expect(stateCopy.items).toEqual([]);
+ expect(stateCopy.isLoadingItems).toEqual(false);
+ expect(stateCopy.hasSearchQuery).toEqual(true);
+ expect(stateCopy.isFetchFailed).toEqual(true);
+ });
+ });
+});
diff --git a/spec/javascripts/frequent_items/utils_spec.js b/spec/javascripts/frequent_items/utils_spec.js
new file mode 100644
index 00000000000..cd27d79b29a
--- /dev/null
+++ b/spec/javascripts/frequent_items/utils_spec.js
@@ -0,0 +1,89 @@
+import bp from '~/breakpoints';
+import { isMobile, getTopFrequentItems, updateExistingFrequentItem } from '~/frequent_items/utils';
+import { HOUR_IN_MS, FREQUENT_ITEMS } from '~/frequent_items/constants';
+import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_data';
+
+describe('Frequent Items utils spec', () => {
+ describe('isMobile', () => {
+ it('returns true when the screen is small ', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
+
+ expect(isMobile()).toBe(true);
+ });
+
+ it('returns true when the screen is extra-small ', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('xs');
+
+ expect(isMobile()).toBe(true);
+ });
+
+ it('returns false when the screen is larger than small ', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('md');
+
+ expect(isMobile()).toBe(false);
+ });
+ });
+
+ describe('getTopFrequentItems', () => {
+ it('returns empty array if no items provided', () => {
+ const result = getTopFrequentItems();
+
+ expect(result.length).toBe(0);
+ });
+
+ it('returns correct amount of items for mobile', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
+ const result = getTopFrequentItems(unsortedFrequentItems);
+
+ expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_MOBILE);
+ });
+
+ it('returns correct amount of items for desktop', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('lg');
+ const result = getTopFrequentItems(unsortedFrequentItems);
+
+ expect(result.length).toBe(FREQUENT_ITEMS.LIST_COUNT_DESKTOP);
+ });
+
+ it('sorts frequent items in order of frequency and lastAccessedOn', () => {
+ spyOn(bp, 'getBreakpointSize').and.returnValue('lg');
+ const result = getTopFrequentItems(unsortedFrequentItems);
+ const expectedResult = sortedFrequentItems.slice(0, FREQUENT_ITEMS.LIST_COUNT_DESKTOP);
+
+ expect(result).toEqual(expectedResult);
+ });
+ });
+
+ describe('updateExistingFrequentItem', () => {
+ let mockedProject;
+
+ beforeEach(() => {
+ mockedProject = {
+ ...mockProject,
+ frequency: 1,
+ lastAccessedOn: 1497979281815,
+ };
+ });
+
+ it('updates item if accessed over an hour ago', () => {
+ const newTimestamp = Date.now() + HOUR_IN_MS + 1;
+ const newItem = {
+ ...mockedProject,
+ lastAccessedOn: newTimestamp,
+ };
+ const result = updateExistingFrequentItem(mockedProject, newItem);
+
+ expect(result.frequency).toBe(mockedProject.frequency + 1);
+ });
+
+ it('does not update item if accessed within the hour', () => {
+ const newItem = {
+ ...mockedProject,
+ lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS,
+ };
+ const result = updateExistingFrequentItem(mockedProject, newItem);
+
+ expect(result.frequency).toBe(mockedProject.frequency);
+ });
+ });
+});
diff --git a/spec/javascripts/helpers/vuex_action_helper.js b/spec/javascripts/helpers/vuex_action_helper.js
index d6ab0aeeed7..4ca7015184e 100644
--- a/spec/javascripts/helpers/vuex_action_helper.js
+++ b/spec/javascripts/helpers/vuex_action_helper.js
@@ -1,71 +1,103 @@
+const noop = () => {};
+
/**
- * helper for testing action with expected mutations inspired in
+ * Helper for testing action with expected mutations inspired in
* https://vuex.vuejs.org/en/testing.html
*
+ * @param {Function} action to be tested
+ * @param {Object} payload will be provided to the action
+ * @param {Object} state will be provided to the action
+ * @param {Array} [expectedMutations=[]] mutations expected to be committed
+ * @param {Array} [expectedActions=[]] actions expected to be dispatched
+ * @param {Function} [done=noop] to be executed after the tests
+ * @return {Promise}
+ *
* @example
* testAction(
* actions.actionName, // action
- * { }, // mocked response
- * state, // state
+ * { }, // mocked payload
+ * state, //state
+ * // expected mutations
* [
* { type: types.MUTATION}
- * { type: types.MUTATION_1, payload: {}}
- * ], // mutations
+ * { type: types.MUTATION_1, payload: jasmine.any(Number)}
+ * ],
+ * // expected actions
* [
- * { type: 'actionName', payload: {}},
- * { type: 'actionName1', payload: {}}
- * ] //actions
+ * { type: 'actionName', payload: {param: 'foobar'}},
+ * { type: 'actionName1'}
+ * ]
* done,
* );
+ *
+ * @example
+ * testAction(
+ * actions.actionName, // action
+ * { }, // mocked payload
+ * state, //state
+ * [ { type: types.MUTATION} ], // expected mutations
+ * [], // expected actions
+ * ).then(done)
+ * .catch(done.fail);
*/
-export default (action, payload, state, expectedMutations, expectedActions, done) => {
- let mutationsCount = 0;
- let actionsCount = 0;
+export default (
+ action,
+ payload,
+ state,
+ expectedMutations = [],
+ expectedActions = [],
+ done = noop,
+) => {
+ const mutations = [];
+ const actions = [];
// mock commit
const commit = (type, mutationPayload) => {
- const mutation = expectedMutations[mutationsCount];
-
- expect(mutation.type).toEqual(type);
+ const mutation = { type };
- if (mutation.payload) {
- expect(mutation.payload).toEqual(mutationPayload);
+ if (typeof mutationPayload !== 'undefined') {
+ mutation.payload = mutationPayload;
}
- mutationsCount += 1;
- if (mutationsCount >= expectedMutations.length) {
- done();
- }
+ mutations.push(mutation);
};
// mock dispatch
const dispatch = (type, actionPayload) => {
- const actionExpected = expectedActions[actionsCount];
-
- expect(actionExpected.type).toEqual(type);
+ const dispatchedAction = { type };
- if (actionExpected.payload) {
- expect(actionExpected.payload).toEqual(actionPayload);
+ if (typeof actionPayload !== 'undefined') {
+ dispatchedAction.payload = actionPayload;
}
- actionsCount += 1;
- if (actionsCount >= expectedActions.length) {
- done();
- }
+ actions.push(dispatchedAction);
};
- // call the action with mocked store and arguments
- action({ commit, state, dispatch, rootState: state }, payload);
-
- // check if no mutations should have been dispatched
- if (expectedMutations.length === 0) {
- expect(mutationsCount).toEqual(0);
+ const validateResults = () => {
+ expect({
+ mutations,
+ actions,
+ }).toEqual({
+ mutations: expectedMutations,
+ actions: expectedActions,
+ });
done();
- }
+ };
- // check if no mutations should have been dispatched
- if (expectedActions.length === 0) {
- expect(actionsCount).toEqual(0);
- done();
- }
+ return new Promise((resolve, reject) => {
+ try {
+ const result = action({ commit, state, dispatch, rootState: state }, payload);
+ resolve(result);
+ } catch (e) {
+ reject(e);
+ }
+ })
+ .catch(error => {
+ validateResults();
+ throw error;
+ })
+ .then(data => {
+ validateResults();
+ return data;
+ });
};
diff --git a/spec/javascripts/helpers/vuex_action_helper_spec.js b/spec/javascripts/helpers/vuex_action_helper_spec.js
new file mode 100644
index 00000000000..8d6ad6750c0
--- /dev/null
+++ b/spec/javascripts/helpers/vuex_action_helper_spec.js
@@ -0,0 +1,141 @@
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'spec/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import testAction from './vuex_action_helper';
+
+describe('VueX test helper (testAction)', () => {
+ let originalExpect;
+ let assertion;
+ let mock;
+ const noop = () => {};
+
+ beforeAll(() => {
+ mock = new MockAdapter(axios);
+ /*
+ In order to test the helper properly, we need to overwrite the jasmine `expect` helper.
+ We test that the testAction helper properly passes the dispatched actions/committed mutations
+ to the jasmine helper.
+ */
+ originalExpect = expect;
+ assertion = null;
+ global.expect = actual => ({
+ toEqual: () => {
+ originalExpect(actual).toEqual(assertion);
+ },
+ });
+ });
+
+ afterAll(() => {
+ mock.restore();
+ global.expect = originalExpect;
+ });
+
+ it('should properly pass on state and payload', () => {
+ const exampleState = { FOO: 12, BAR: 3 };
+ const examplePayload = { BAZ: 73, BIZ: 55 };
+
+ const action = ({ state }, payload) => {
+ originalExpect(state).toEqual(exampleState);
+ originalExpect(payload).toEqual(examplePayload);
+ };
+
+ assertion = { mutations: [], actions: [] };
+
+ testAction(action, examplePayload, exampleState);
+ });
+
+ describe('should work with synchronous actions', () => {
+ it('committing mutation', () => {
+ const action = ({ commit }) => {
+ commit('MUTATION');
+ };
+
+ assertion = { mutations: [{ type: 'MUTATION' }], actions: [] };
+
+ testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ });
+
+ it('dispatching action', () => {
+ const action = ({ dispatch }) => {
+ dispatch('ACTION');
+ };
+
+ assertion = { actions: [{ type: 'ACTION' }], mutations: [] };
+
+ testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
+ });
+
+ it('work with jasmine done once finished', done => {
+ assertion = { mutations: [], actions: [] };
+
+ testAction(noop, null, {}, assertion.mutations, assertion.actions, done);
+ });
+
+ it('provide promise interface', done => {
+ assertion = { mutations: [], actions: [] };
+
+ testAction(noop, null, {}, assertion.mutations, assertion.actions)
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('should work with promise based actions (fetch action)', () => {
+ let lastError;
+ const data = { FOO: 'BAR' };
+
+ const promiseAction = ({ commit, dispatch }) => {
+ dispatch('ACTION');
+
+ return axios
+ .get(TEST_HOST)
+ .catch(error => {
+ commit('ERROR');
+ lastError = error;
+ throw error;
+ })
+ .then(() => {
+ commit('SUCCESS');
+ return data;
+ });
+ };
+
+ beforeEach(() => {
+ lastError = null;
+ });
+
+ it('work with jasmine done once finished', done => {
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
+
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions, done);
+ });
+
+ it('return original data of successful promise while checking actions/mutations', done => {
+ mock.onGet(TEST_HOST).replyOnce(200, 42);
+
+ assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions)
+ .then(res => {
+ originalExpect(res).toEqual(data);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('return original error of rejected promise while checking actions/mutations', done => {
+ mock.onGet(TEST_HOST).replyOnce(500, '');
+
+ assertion = { mutations: [{ type: 'ERROR' }], actions: [{ type: 'ACTION' }] };
+
+ testAction(promiseAction, null, {}, assertion.mutations, assertion.actions)
+ .then(done.fail)
+ .catch(error => {
+ originalExpect(error).toBe(lastError);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/helpers/wait_for_promises.js b/spec/javascripts/helpers/wait_for_promises.js
new file mode 100644
index 00000000000..1d2b53fc770
--- /dev/null
+++ b/spec/javascripts/helpers/wait_for_promises.js
@@ -0,0 +1 @@
+export default () => new Promise(resolve => requestAnimationFrame(resolve));
diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js
index 770dca9cb0f..9895682f388 100644
--- a/spec/javascripts/ide/components/ide_status_bar_spec.js
+++ b/spec/javascripts/ide/components/ide_status_bar_spec.js
@@ -13,6 +13,7 @@ describe('ideStatusBar', () => {
store.state.currentProjectId = 'abcproject';
store.state.projects.abcproject = projectData;
+ store.state.currentBranchId = 'master';
vm = createComponentWithStore(Component, store).$mount();
});
@@ -60,4 +61,29 @@ describe('ideStatusBar', () => {
expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
});
});
+
+ describe('pipeline status', () => {
+ it('opens right sidebar on clicking icon', done => {
+ spyOn(vm, 'setRightPane');
+ Vue.set(vm.$store.state.pipelines, 'latestPipeline', {
+ details: {
+ status: {
+ text: 'success',
+ details_path: 'test',
+ icon: 'success',
+ },
+ },
+ });
+
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.ide-status-pipeline button').click();
+
+ expect(vm.setRightPane).toHaveBeenCalledWith('pipelines-list');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/merge_requests/info_spec.js b/spec/javascripts/ide/components/merge_requests/info_spec.js
new file mode 100644
index 00000000000..98a29e5128b
--- /dev/null
+++ b/spec/javascripts/ide/components/merge_requests/info_spec.js
@@ -0,0 +1,51 @@
+import Vue from 'vue';
+import '~/behaviors/markdown/render_gfm';
+import { createStore } from '~/ide/stores';
+import Info from '~/ide/components/merge_requests/info.vue';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+
+describe('IDE merge request details', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Info);
+ });
+
+ beforeEach(() => {
+ const store = createStore();
+ store.state.currentProjectId = 'gitlab-ce';
+ store.state.currentMergeRequestId = 1;
+ store.state.projects['gitlab-ce'] = {
+ mergeRequests: {
+ 1: {
+ iid: 1,
+ title: 'Testing',
+ title_html: '<span class="title-html">Testing</span>',
+ description: 'Description',
+ description_html: '<p class="description-html">Description HTML</p>',
+ },
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders merge request IID', () => {
+ expect(vm.$el.querySelector('.detail-page-header').textContent).toContain('!1');
+ });
+
+ it('renders title as HTML', () => {
+ expect(vm.$el.querySelector('.title-html')).not.toBe(null);
+ expect(vm.$el.querySelector('.title').textContent).toContain('Testing');
+ });
+
+ it('renders description as HTML', () => {
+ expect(vm.$el.querySelector('.description-html')).not.toBe(null);
+ expect(vm.$el.querySelector('.description').textContent).toContain('Description HTML');
+ });
+});
diff --git a/spec/javascripts/ide/components/new_dropdown/button_spec.js b/spec/javascripts/ide/components/new_dropdown/button_spec.js
new file mode 100644
index 00000000000..ef083d06ba7
--- /dev/null
+++ b/spec/javascripts/ide/components/new_dropdown/button_spec.js
@@ -0,0 +1,49 @@
+import Vue from 'vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import Button from '~/ide/components/new_dropdown/button.vue';
+
+describe('IDE new entry dropdown button component', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Button);
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ label: 'Testing',
+ icon: 'doc-new',
+ });
+
+ spyOn(vm, '$emit');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders button with label', () => {
+ expect(vm.$el.textContent).toContain('Testing');
+ });
+
+ it('renders icon', () => {
+ expect(vm.$el.querySelector('.ic-doc-new')).not.toBe(null);
+ });
+
+ it('emits click event', () => {
+ vm.$el.click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('click');
+ });
+
+ it('hides label if showLabel is false', done => {
+ vm.showLabel = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).not.toContain('Testing');
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js
index 7b637f37eba..4d704b80209 100644
--- a/spec/javascripts/ide/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js
@@ -13,6 +13,7 @@ describe('new dropdown component', () => {
vm = createComponentWithStore(component, store, {
branch: 'master',
path: '',
+ mouseOver: false,
});
vm.$store.state.currentProjectId = 'abcproject';
@@ -21,6 +22,8 @@ describe('new dropdown component', () => {
tree: [],
};
+ spyOn(vm, 'openNewEntryModal');
+
vm.$mount();
});
@@ -31,50 +34,23 @@ describe('new dropdown component', () => {
});
it('renders new file, upload and new directory links', () => {
- expect(vm.$el.querySelectorAll('a')[0].textContent.trim()).toBe('New file');
- expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('Upload file');
- expect(vm.$el.querySelectorAll('a')[2].textContent.trim()).toBe('New directory');
+ const buttons = vm.$el.querySelectorAll('.dropdown-menu button');
+ expect(buttons[0].textContent.trim()).toBe('New file');
+ expect(buttons[1].textContent.trim()).toBe('Upload file');
+ expect(buttons[2].textContent.trim()).toBe('New directory');
});
describe('createNewItem', () => {
it('sets modalType to blob when new file is clicked', () => {
- vm.$el.querySelectorAll('a')[0].click();
+ vm.$el.querySelectorAll('.dropdown-menu button')[0].click();
- expect(vm.modalType).toBe('blob');
+ expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'blob', path: '' });
});
it('sets modalType to tree when new directory is clicked', () => {
- vm.$el.querySelectorAll('a')[2].click();
-
- expect(vm.modalType).toBe('tree');
- });
-
- it('opens modal when link is clicked', done => {
- vm.$el.querySelectorAll('a')[0].click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
-
- done();
- });
- });
- });
-
- describe('hideModal', () => {
- beforeAll(done => {
- vm.openModal = true;
- Vue.nextTick(done);
- });
-
- it('closes modal after toggling', done => {
- vm.hideModal();
+ vm.$el.querySelectorAll('.dropdown-menu button')[2].click();
- Vue.nextTick()
- .then(() => {
- expect(vm.$el.querySelector('.modal')).toBeNull();
- })
- .then(done)
- .catch(done.fail);
+ expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'tree', path: '' });
});
});
diff --git a/spec/javascripts/ide/components/new_dropdown/modal_spec.js b/spec/javascripts/ide/components/new_dropdown/modal_spec.js
index f362ed4db65..6dcc5880677 100644
--- a/spec/javascripts/ide/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/modal_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
+import { createStore } from '~/ide/stores';
import modal from '~/ide/components/new_dropdown/modal.vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
describe('new file modal component', () => {
const Component = Vue.extend(modal);
@@ -13,13 +14,15 @@ describe('new file modal component', () => {
['tree', 'blob'].forEach(type => {
describe(type, () => {
beforeEach(() => {
- vm = createComponent(Component, {
+ const store = createStore();
+ store.state.newEntryModal = {
type,
- branchId: 'master',
path: '',
- });
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
- vm.entryName = 'testing';
+ vm.name = 'testing';
});
it(`sets modal title as ${type}`, () => {
@@ -40,12 +43,11 @@ describe('new file modal component', () => {
describe('createEntryInStore', () => {
it('$emits create', () => {
- spyOn(vm, '$emit');
+ spyOn(vm, 'createTempEntry');
vm.createEntryInStore();
- expect(vm.$emit).toHaveBeenCalledWith('create', {
- branchId: 'master',
+ expect(vm.createTempEntry).toHaveBeenCalledWith({
name: 'testing',
type,
});
@@ -53,22 +55,4 @@ describe('new file modal component', () => {
});
});
});
-
- it('focuses field on mount', () => {
- document.body.innerHTML += '<div class="js-test"></div>';
-
- vm = createComponent(
- Component,
- {
- type: 'tree',
- branchId: 'master',
- path: '',
- },
- '.js-test',
- );
-
- expect(document.activeElement).toBe(vm.$refs.fieldName);
-
- vm.$el.remove();
- });
});
diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
index 2bc5d701601..9c76500cfe5 100644
--- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/upload_spec.js
@@ -9,7 +9,6 @@ describe('new dropdown upload', () => {
const Component = Vue.extend(upload);
vm = createComponent(Component, {
- branchId: 'master',
path: '',
});
@@ -65,7 +64,6 @@ describe('new dropdown upload', () => {
expect(vm.$emit).toHaveBeenCalledWith('create', {
name: file.name,
- branchId: 'master',
type: 'blob',
content: target.result,
base64: false,
@@ -77,7 +75,6 @@ describe('new dropdown upload', () => {
expect(vm.$emit).toHaveBeenCalledWith('create', {
name: file.name,
- branchId: 'master',
type: 'blob',
content: binaryTarget.result.split('base64,')[1],
base64: true,
diff --git a/spec/javascripts/ide/components/panes/right_spec.js b/spec/javascripts/ide/components/panes/right_spec.js
new file mode 100644
index 00000000000..99879fb0930
--- /dev/null
+++ b/spec/javascripts/ide/components/panes/right_spec.js
@@ -0,0 +1,72 @@
+import Vue from 'vue';
+import '~/behaviors/markdown/render_gfm';
+import { createStore } from '~/ide/stores';
+import RightPane from '~/ide/components/panes/right.vue';
+import { rightSidebarViews } from '~/ide/constants';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+
+describe('IDE right pane', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(RightPane);
+ });
+
+ beforeEach(() => {
+ const store = createStore();
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('active', () => {
+ it('renders merge request button as active', done => {
+ vm.$store.state.rightPane = rightSidebarViews.mergeRequestInfo;
+ vm.$store.state.currentMergeRequestId = '123';
+ vm.$store.state.currentProjectId = 'gitlab-ce';
+ vm.$store.state.currentMergeRequestId = 1;
+ vm.$store.state.projects['gitlab-ce'] = {
+ mergeRequests: {
+ 1: {
+ iid: 1,
+ title: 'Testing',
+ title_html: '<span class="title-html">Testing</span>',
+ description: 'Description',
+ description_html: '<p class="description-html">Description HTML</p>',
+ },
+ },
+ };
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.ide-sidebar-link.active')).not.toBe(null);
+ expect(
+ vm.$el.querySelector('.ide-sidebar-link.active').getAttribute('data-original-title'),
+ ).toBe('Merge Request');
+
+ done();
+ });
+ });
+ });
+
+ describe('click', () => {
+ beforeEach(() => {
+ spyOn(vm, 'setRightPane');
+ });
+
+ it('sets view to merge request', done => {
+ vm.$store.state.currentMergeRequestId = '123';
+
+ vm.$nextTick(() => {
+ vm.$el.querySelector('.ide-sidebar-link').click();
+
+ expect(vm.setRightPane).toHaveBeenCalledWith(rightSidebarViews.mergeRequestInfo);
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js
index 80bf664d491..4bbda4c8e80 100644
--- a/spec/javascripts/ide/mock_data.js
+++ b/spec/javascripts/ide/mock_data.js
@@ -9,6 +9,9 @@ export const projectData = {
master: {
treeId: 'abcproject/master',
can_push: true,
+ commit: {
+ id: '123',
+ },
},
},
mergeRequests: {},
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index 58d3ffc6d94..f570c0b16bd 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -601,10 +601,7 @@ describe('IDE store file actions', () => {
actions.unstageChange,
'path',
store.state,
- [
- { type: types.UNSTAGE_CHANGE, payload: 'path' },
- { type: types.SET_LAST_COMMIT_MSG, payload: '' },
- ],
+ [{ type: types.UNSTAGE_CHANGE, payload: 'path' }],
[],
done,
);
diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js
index c99ccc70c6a..90c28c769f7 100644
--- a/spec/javascripts/ide/stores/actions/merge_request_spec.js
+++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js
@@ -39,7 +39,9 @@ describe('IDE store merge request actions', () => {
store
.dispatch('getMergeRequestData', { projectId: 'abcproject', mergeRequestId: 1 })
.then(() => {
- expect(service.getProjectMergeRequestData).toHaveBeenCalledWith('abcproject', 1);
+ expect(service.getProjectMergeRequestData).toHaveBeenCalledWith('abcproject', 1, {
+ render_html: true,
+ });
done();
})
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
index ca79edafb7e..6a85968e199 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -73,6 +73,7 @@ describe('IDE store project actions', () => {
branchId: store.state.currentBranchId,
},
store.state,
+ // mutations
[
{
type: 'SET_BRANCH_COMMIT',
@@ -82,17 +83,9 @@ describe('IDE store project actions', () => {
commit: { id: '123' },
},
},
- ], // mutations
- [
- {
- type: 'getLastCommitPipeline',
- payload: {
- projectId: 'abc/def',
- projectIdNumber: store.state.projects['abc/def'].id,
- branchId: 'master',
- },
- },
- ], // action
+ ],
+ // action
+ [],
done,
);
});
diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js
index 6860e6cdb91..9f098eded08 100644
--- a/spec/javascripts/ide/stores/actions/tree_spec.js
+++ b/spec/javascripts/ide/stores/actions/tree_spec.js
@@ -192,11 +192,8 @@ describe('Multi-file store tree actions', () => {
showTreeEntry,
'grandparent/parent/child.txt',
store.state,
- [
- { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' },
- { type: types.SET_TREE_OPEN, payload: 'grandparent' },
- ],
- [{ type: 'showTreeEntry' }],
+ [{ type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }],
+ [{ type: 'showTreeEntry', payload: 'grandparent/parent' }],
done,
);
});
diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
index d21f33eaf6d..d063f1ea860 100644
--- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
@@ -122,21 +122,6 @@ describe('IDE merge requests actions', () => {
});
});
- it('dispatches request', done => {
- testAction(
- fetchMergeRequests,
- { type: 'created' },
- mockedState,
- [],
- [
- { type: 'requestMergeRequests' },
- { type: 'resetMergeRequests' },
- { type: 'receiveMergeRequestsSuccess' },
- ],
- done,
- );
- });
-
it('dispatches success with received data', done => {
testAction(
fetchMergeRequests,
@@ -144,8 +129,8 @@ describe('IDE merge requests actions', () => {
mockedState,
[],
[
- { type: 'requestMergeRequests' },
- { type: 'resetMergeRequests' },
+ { type: 'requestMergeRequests', payload: 'created' },
+ { type: 'resetMergeRequests', payload: 'created' },
{
type: 'receiveMergeRequestsSuccess',
payload: { type: 'created', data: mergeRequests },
@@ -168,9 +153,9 @@ describe('IDE merge requests actions', () => {
mockedState,
[],
[
- { type: 'requestMergeRequests' },
- { type: 'resetMergeRequests' },
- { type: 'receiveMergeRequestsError' },
+ { type: 'requestMergeRequests', payload: 'created' },
+ { type: 'resetMergeRequests', payload: 'created' },
+ { type: 'receiveMergeRequestsError', payload: { type: 'created', search: '' } },
],
done,
);
diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
index 836ba72b5d8..91edb388791 100644
--- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
@@ -315,7 +315,7 @@ describe('IDE pipelines actions', () => {
'job',
mockedState,
[{ type: types.SET_DETAIL_JOB, payload: 'job' }],
- [{ type: 'setRightPane' }],
+ [{ type: 'setRightPane', payload: 'jobs-detail' }],
done,
);
});
@@ -325,7 +325,7 @@ describe('IDE pipelines actions', () => {
setDetailJob,
null,
mockedState,
- [{ type: types.SET_DETAIL_JOB }],
+ [{ type: types.SET_DETAIL_JOB, payload: null }],
[{ type: 'setRightPane', payload: rightSidebarViews.pipelines }],
done,
);
@@ -336,7 +336,7 @@ describe('IDE pipelines actions', () => {
setDetailJob,
'job',
mockedState,
- [{ type: types.SET_DETAIL_JOB }],
+ [{ type: types.SET_DETAIL_JOB, payload: 'job' }],
[{ type: 'setRightPane', payload: rightSidebarViews.jobsDetail }],
done,
);
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
deleted file mode 100644
index 5add150f874..00000000000
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/* eslint-disable no-unused-vars, func-call-spacing, no-spaced-func, semi, quotes, space-infix-ops, max-len */
-
-import $ from 'jquery';
-import Vue from 'vue';
-
-import timeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
-
-function initTimeTrackingComponent(opts) {
- setFixtures(`
- <div>
- <div id="mock-container"></div>
- </div>
- `);
-
- this.initialData = {
- time_estimate: opts.timeEstimate,
- time_spent: opts.timeSpent,
- human_time_estimate: opts.timeEstimateHumanReadable,
- human_time_spent: opts.timeSpentHumanReadable,
- rootPath: '/',
- };
-
- const TimeTrackingComponent = Vue.extend(timeTracker);
- this.timeTracker = new TimeTrackingComponent({
- el: '#mock-container',
- propsData: this.initialData,
- });
-}
-
-describe('Issuable Time Tracker', function() {
- describe('Initialization', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 100000, timeSpent: 5000, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '1h 23m' });
- });
-
- it('should return something defined', function() {
- expect(this.timeTracker).toBeDefined();
- });
-
- it ('should correctly set timeEstimate', function(done) {
- Vue.nextTick(() => {
- expect(this.timeTracker.timeEstimate).toBe(this.initialData.time_estimate);
- done();
- });
- });
- it ('should correctly set time_spent', function(done) {
- Vue.nextTick(() => {
- expect(this.timeTracker.timeSpent).toBe(this.initialData.time_spent);
- done();
- });
- });
- });
-
- describe('Content Display', function() {
- describe('Panes', function() {
- describe('Comparison pane', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 100000, timeSpent: 5000, timeEstimateHumanReadable: '', timeSpentHumanReadable: '' });
- });
-
- it('should show the "Comparison" pane when timeEstimate and time_spent are truthy', function(done) {
- Vue.nextTick(() => {
- const $comparisonPane = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane');
- expect(this.timeTracker.showComparisonState).toBe(true);
- done();
- });
- });
-
- describe('Remaining meter', function() {
- it('should display the remaining meter with the correct width', function(done) {
- Vue.nextTick(() => {
- const meterWidth = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane .meter-fill').style.width;
- const correctWidth = '5%';
-
- expect(meterWidth).toBe(correctWidth);
- done();
- })
- });
-
- it('should display the remaining meter with the correct background color when within estimate', function(done) {
- Vue.nextTick(() => {
- const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .within_estimate .meter-fill');
- expect(styledMeter.length).toBe(1);
- done()
- });
- });
-
- it('should display the remaining meter with the correct background color when over estimate', function(done) {
- this.timeTracker.time_estimate = 100000;
- this.timeTracker.time_spent = 20000000;
- Vue.nextTick(() => {
- const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .over_estimate .meter-fill');
- expect(styledMeter.length).toBe(1);
- done();
- });
- });
- });
- });
-
- describe("Estimate only pane", function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 100000, timeSpent: 0, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '' });
- });
-
- it('should display the human readable version of time estimated', function(done) {
- Vue.nextTick(() => {
- const estimateText = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane').innerText;
- const correctText = 'Estimated: 2h 46m';
-
- expect(estimateText).toBe(correctText);
- done();
- });
- });
- });
-
- describe('Spent only pane', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 0, timeSpent: 5000, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '1h 23m' });
- });
-
- it('should display the human readable version of time spent', function(done) {
- Vue.nextTick(() => {
- const spentText = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane').innerText;
- const correctText = 'Spent: 1h 23m';
-
- expect(spentText).toBe(correctText);
- done();
- });
- });
- });
-
- describe('No time tracking pane', function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 0, timeSpent: 0, timeEstimateHumanReadable: '', timeSpentHumanReadable: '' });
- });
-
- it('should only show the "No time tracking" pane when both timeEstimate and time_spent are falsey', function(done) {
- Vue.nextTick(() => {
- const $noTrackingPane = this.timeTracker.$el.querySelector('.time-tracking-no-tracking-pane');
- const noTrackingText =$noTrackingPane.innerText;
- const correctText = 'No estimate or time spent';
-
- expect(this.timeTracker.showNoTimeTrackingState).toBe(true);
- expect($noTrackingPane).toBeVisible();
- expect(noTrackingText).toBe(correctText);
- done();
- });
- });
- });
-
- describe("Help pane", function() {
- beforeEach(function() {
- initTimeTrackingComponent.call(this, { timeEstimate: 0, timeSpent: 0 });
- });
-
- it('should not show the "Help" pane by default', function(done) {
- Vue.nextTick(() => {
- const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
-
- expect(this.timeTracker.showHelpState).toBe(false);
- expect($helpPane).toBeNull();
- done();
- });
- });
-
- it('should show the "Help" pane when help button is clicked', function(done) {
- Vue.nextTick(() => {
- $(this.timeTracker.$el).find('.help-button').click();
-
- setTimeout(() => {
- const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
- expect(this.timeTracker.showHelpState).toBe(true);
- expect($helpPane).toBeVisible();
- done();
- }, 10);
- });
- });
-
- it('should not show the "Help" pane when help button is clicked and then closed', function(done) {
- Vue.nextTick(() => {
- $(this.timeTracker.$el).find('.help-button').click();
-
- setTimeout(() => {
-
- $(this.timeTracker.$el).find('.close-help-button').click();
-
- setTimeout(() => {
- const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
-
- expect(this.timeTracker.showHelpState).toBe(false);
- expect($helpPane).toBeNull();
-
- done();
- }, 1000);
- }, 1000);
- });
- });
- });
- });
- });
-});
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
index 79e375aa02e..2fcb5566ebc 100644
--- a/spec/javascripts/job_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -5,6 +5,7 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import '~/lib/utils/datetime_utility';
import Job from '~/job';
import '~/breakpoints';
+import waitForPromises from 'spec/helpers/wait_for_promises';
describe('Job', () => {
const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
@@ -12,10 +13,6 @@ describe('Job', () => {
let response;
let job;
- function waitForPromise() {
- return new Promise(resolve => requestAnimationFrame(resolve));
- }
-
preloadFixtures('builds/build-with-artifacts.html.raw');
beforeEach(() => {
@@ -49,7 +46,7 @@ describe('Job', () => {
beforeEach(function (done) {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(done)
.catch(done.fail);
});
@@ -93,7 +90,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
expect(job.state).toBe('newstate');
@@ -107,7 +104,7 @@ describe('Job', () => {
};
})
.then(() => jasmine.clock().tick(4001))
- .then(waitForPromise)
+ .then(waitForPromises)
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
expect(job.state).toBe('finalstate');
@@ -126,7 +123,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
@@ -137,7 +134,7 @@ describe('Job', () => {
};
})
.then(() => jasmine.clock().tick(4001))
- .then(waitForPromise)
+ .then(waitForPromises)
.then(() => {
expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/);
expect($('#build-trace .js-build-output').text()).toMatch(/Different/);
@@ -160,7 +157,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden');
})
@@ -181,7 +178,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -203,7 +200,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -219,7 +216,7 @@ describe('Job', () => {
};
})
.then(() => jasmine.clock().tick(4001))
- .then(waitForPromise)
+ .then(waitForPromises)
.then(() => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
@@ -258,7 +255,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(() => {
expect(document.querySelector('.js-truncated-info').classList).toContain('hidden');
})
@@ -280,7 +277,7 @@ describe('Job', () => {
job = new Job();
- waitForPromise()
+ waitForPromises()
.then(done)
.catch(done.fail);
});
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 1b040c924aa..be2a8ba67fe 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -165,6 +165,7 @@ export const note = {
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1',
path: '/gitlab-org/gitlab-ce/notes/546',
+ cached_markdown_version: 11,
};
export const discussionMock = {
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 71ef3aa9b03..b66e8e1ceb3 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -128,6 +128,19 @@ describe('Actions Notes Store', () => {
});
});
+ describe('collapseDiscussion', () => {
+ it('should commit collapse discussion', done => {
+ testAction(
+ actions.collapseDiscussion,
+ { discussionId: discussionMock.id },
+ { notes: [discussionMock] },
+ [{ type: 'COLLAPSE_DISCUSSION', payload: { discussionId: discussionMock.id } }],
+ [],
+ done,
+ );
+ });
+ });
+
describe('async methods', () => {
const interceptor = (request, next) => {
next(
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index ccc7328447b..a15ff1a5888 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -74,6 +74,20 @@ describe('Notes Store mutations', () => {
});
});
+ describe('COLLAPSE_DISCUSSION', () => {
+ it('should collpase an expanded discussion', () => {
+ const discussion = Object.assign({}, discussionMock, { expanded: true });
+
+ const state = {
+ discussions: [discussion],
+ };
+
+ mutations.COLLAPSE_DISCUSSION(state, { discussionId: discussion.id });
+
+ expect(state.discussions[0].expanded).toEqual(false);
+ });
+ });
+
describe('REMOVE_PLACEHOLDER_NOTES', () => {
it('should remove all placeholder notes in indivudal notes and discussion', () => {
const placeholderNote = Object.assign({}, individualNote, { isPlaceholderNote: true });
diff --git a/spec/javascripts/performance_bar/components/performance_bar_app_spec.js b/spec/javascripts/performance_bar/components/performance_bar_app_spec.js
index 9ab9ab1c9f4..7926db44429 100644
--- a/spec/javascripts/performance_bar/components/performance_bar_app_spec.js
+++ b/spec/javascripts/performance_bar/components/performance_bar_app_spec.js
@@ -1,39 +1,15 @@
import Vue from 'vue';
-import axios from '~/lib/utils/axios_utils';
import performanceBarApp from '~/performance_bar/components/performance_bar_app.vue';
-import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
import PerformanceBarStore from '~/performance_bar/stores/performance_bar_store';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import MockAdapter from 'axios-mock-adapter';
-describe('performance bar', () => {
- let mock;
+describe('performance bar app', () => {
let vm;
beforeEach(() => {
const store = new PerformanceBarStore();
- mock = new MockAdapter(axios);
-
- mock.onGet('/-/peek/results').reply(
- 200,
- {
- data: {
- gc: {
- invokes: 0,
- invoke_time: '0.00',
- use_size: 0,
- total_size: 0,
- total_object: 0,
- gc_time: '0.00',
- },
- host: { hostname: 'web-01' },
- },
- },
- {},
- );
-
vm = mountComponent(Vue.extend(performanceBarApp), {
store,
env: 'development',
@@ -45,44 +21,9 @@ describe('performance bar', () => {
afterEach(() => {
vm.$destroy();
- mock.restore();
});
it('sets the class to match the environment', () => {
expect(vm.$el.getAttribute('class')).toContain('development');
});
-
- describe('loadRequestDetails', () => {
- beforeEach(() => {
- spyOn(vm.store, 'addRequest').and.callThrough();
- });
-
- it('does nothing if the request cannot be tracked', () => {
- spyOn(vm.store, 'canTrackRequest').and.callFake(() => false);
-
- vm.loadRequestDetails('123', 'https://gitlab.com/');
-
- expect(vm.store.addRequest).not.toHaveBeenCalled();
- });
-
- it('adds the request immediately', () => {
- vm.loadRequestDetails('123', 'https://gitlab.com/');
-
- expect(vm.store.addRequest).toHaveBeenCalledWith(
- '123',
- 'https://gitlab.com/',
- );
- });
-
- it('makes an HTTP request for the request details', () => {
- spyOn(PerformanceBarService, 'fetchRequestDetails').and.callThrough();
-
- vm.loadRequestDetails('456', 'https://gitlab.com/');
-
- expect(PerformanceBarService.fetchRequestDetails).toHaveBeenCalledWith(
- '/-/peek/results',
- '456',
- );
- });
- });
});
diff --git a/spec/javascripts/performance_bar/index_spec.js b/spec/javascripts/performance_bar/index_spec.js
new file mode 100644
index 00000000000..1784bd64adb
--- /dev/null
+++ b/spec/javascripts/performance_bar/index_spec.js
@@ -0,0 +1,83 @@
+import axios from '~/lib/utils/axios_utils';
+import performanceBar from '~/performance_bar';
+import PerformanceBarService from '~/performance_bar/services/performance_bar_service';
+
+import MockAdapter from 'axios-mock-adapter';
+
+describe('performance bar wrapper', () => {
+ let mock;
+ let vm;
+
+ beforeEach(() => {
+ const peekWrapper = document.createElement('div');
+
+ peekWrapper.setAttribute('id', 'js-peek');
+ peekWrapper.setAttribute('data-env', 'development');
+ peekWrapper.setAttribute('data-request-id', '123');
+ peekWrapper.setAttribute('data-peek-url', '/-/peek/results');
+ peekWrapper.setAttribute('data-profile-url', '?lineprofiler=true');
+
+ document.body.appendChild(peekWrapper);
+
+ mock = new MockAdapter(axios);
+
+ mock.onGet('/-/peek/results').reply(
+ 200,
+ {
+ data: {
+ gc: {
+ invokes: 0,
+ invoke_time: '0.00',
+ use_size: 0,
+ total_size: 0,
+ total_object: 0,
+ gc_time: '0.00',
+ },
+ host: { hostname: 'web-01' },
+ },
+ },
+ {},
+ );
+
+ vm = performanceBar({ container: '#js-peek' });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ mock.restore();
+ });
+
+ describe('loadRequestDetails', () => {
+ beforeEach(() => {
+ spyOn(vm.store, 'addRequest').and.callThrough();
+ });
+
+ it('does nothing if the request cannot be tracked', () => {
+ spyOn(vm.store, 'canTrackRequest').and.callFake(() => false);
+
+ vm.loadRequestDetails('123', 'https://gitlab.com/');
+
+ expect(vm.store.addRequest).not.toHaveBeenCalled();
+ });
+
+ it('adds the request immediately', () => {
+ vm.loadRequestDetails('123', 'https://gitlab.com/');
+
+ expect(vm.store.addRequest).toHaveBeenCalledWith(
+ '123',
+ 'https://gitlab.com/',
+ );
+ });
+
+ it('makes an HTTP request for the request details', () => {
+ spyOn(PerformanceBarService, 'fetchRequestDetails').and.callThrough();
+
+ vm.loadRequestDetails('456', 'https://gitlab.com/');
+
+ expect(PerformanceBarService.fetchRequestDetails).toHaveBeenCalledWith(
+ '/-/peek/results',
+ '456',
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/profile/add_ssh_key_validation_spec.js b/spec/javascripts/profile/add_ssh_key_validation_spec.js
new file mode 100644
index 00000000000..c71a2885acc
--- /dev/null
+++ b/spec/javascripts/profile/add_ssh_key_validation_spec.js
@@ -0,0 +1,69 @@
+import AddSshKeyValidation from '../../../app/assets/javascripts/profile/add_ssh_key_validation';
+
+describe('AddSshKeyValidation', () => {
+ describe('submit', () => {
+ it('returns true if isValid is true', () => {
+ const addSshKeyValidation = new AddSshKeyValidation({});
+ spyOn(AddSshKeyValidation, 'isPublicKey').and.returnValue(true);
+
+ expect(addSshKeyValidation.submit()).toBeTruthy();
+ });
+
+ it('calls preventDefault and toggleWarning if isValid is false', () => {
+ const addSshKeyValidation = new AddSshKeyValidation({});
+ const event = jasmine.createSpyObj('event', ['preventDefault']);
+ spyOn(AddSshKeyValidation, 'isPublicKey').and.returnValue(false);
+ spyOn(addSshKeyValidation, 'toggleWarning');
+
+ addSshKeyValidation.submit(event);
+
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(addSshKeyValidation.toggleWarning).toHaveBeenCalledWith(true);
+ });
+ });
+
+ describe('toggleWarning', () => {
+ it('shows warningElement and hides originalSubmitElement if isVisible is true', () => {
+ const warningElement = document.createElement('div');
+ const originalSubmitElement = document.createElement('div');
+ warningElement.classList.add('hide');
+
+ const addSshKeyValidation = new AddSshKeyValidation(
+ {},
+ warningElement,
+ originalSubmitElement,
+ );
+ addSshKeyValidation.toggleWarning(true);
+
+ expect(warningElement.classList.contains('hide')).toBeFalsy();
+ expect(originalSubmitElement.classList.contains('hide')).toBeTruthy();
+ });
+
+ it('hides warningElement and shows originalSubmitElement if isVisible is false', () => {
+ const warningElement = document.createElement('div');
+ const originalSubmitElement = document.createElement('div');
+ originalSubmitElement.classList.add('hide');
+
+ const addSshKeyValidation = new AddSshKeyValidation(
+ {},
+ warningElement,
+ originalSubmitElement,
+ );
+ addSshKeyValidation.toggleWarning(false);
+
+ expect(warningElement.classList.contains('hide')).toBeTruthy();
+ expect(originalSubmitElement.classList.contains('hide')).toBeFalsy();
+ });
+ });
+
+ describe('isPublicKey', () => {
+ it('returns false if probably invalid public ssh key', () => {
+ expect(AddSshKeyValidation.isPublicKey('nope')).toBeFalsy();
+ });
+
+ it('returns true if probably valid public ssh key', () => {
+ expect(AddSshKeyValidation.isPublicKey('ssh-')).toBeTruthy();
+ expect(AddSshKeyValidation.isPublicKey('ecdsa-sha2-')).toBeTruthy();
+ });
+ });
+});
diff --git a/spec/javascripts/projects_dropdown/components/app_spec.js b/spec/javascripts/projects_dropdown/components/app_spec.js
deleted file mode 100644
index 38b31c3d727..00000000000
--- a/spec/javascripts/projects_dropdown/components/app_spec.js
+++ /dev/null
@@ -1,349 +0,0 @@
-import Vue from 'vue';
-
-import bp from '~/breakpoints';
-import appComponent from '~/projects_dropdown/components/app.vue';
-import eventHub from '~/projects_dropdown/event_hub';
-import ProjectsStore from '~/projects_dropdown/store/projects_store';
-import ProjectsService from '~/projects_dropdown/service/projects_service';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { currentSession, mockProject, mockRawProject } from '../mock_data';
-
-const createComponent = () => {
- gon.api_version = currentSession.apiVersion;
- const Component = Vue.extend(appComponent);
- const store = new ProjectsStore();
- const service = new ProjectsService(currentSession.username);
-
- return mountComponent(Component, {
- store,
- service,
- currentUserName: currentSession.username,
- currentProject: currentSession.project,
- });
-};
-
-const returnServicePromise = (data, failed) =>
- new Promise((resolve, reject) => {
- if (failed) {
- reject(data);
- } else {
- resolve({
- json() {
- return data;
- },
- });
- }
- });
-
-describe('AppComponent', () => {
- describe('computed', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('frequentProjects', () => {
- it('should return list of frequently accessed projects from store', () => {
- expect(vm.frequentProjects).toBeDefined();
- expect(vm.frequentProjects.length).toBe(0);
-
- vm.store.setFrequentProjects([mockProject]);
- expect(vm.frequentProjects).toBeDefined();
- expect(vm.frequentProjects.length).toBe(1);
- });
- });
-
- describe('searchProjects', () => {
- it('should return list of frequently accessed projects from store', () => {
- expect(vm.searchProjects).toBeDefined();
- expect(vm.searchProjects.length).toBe(0);
-
- vm.store.setSearchedProjects([mockRawProject]);
- expect(vm.searchProjects).toBeDefined();
- expect(vm.searchProjects.length).toBe(1);
- });
- });
- });
-
- describe('methods', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('toggleFrequentProjectsList', () => {
- it('should toggle props which control visibility of Frequent Projects list from state passed', () => {
- vm.toggleFrequentProjectsList(true);
- expect(vm.isLoadingProjects).toBeFalsy();
- expect(vm.isSearchListVisible).toBeFalsy();
- expect(vm.isFrequentsListVisible).toBeTruthy();
-
- vm.toggleFrequentProjectsList(false);
- expect(vm.isLoadingProjects).toBeTruthy();
- expect(vm.isSearchListVisible).toBeTruthy();
- expect(vm.isFrequentsListVisible).toBeFalsy();
- });
- });
-
- describe('toggleSearchProjectsList', () => {
- it('should toggle props which control visibility of Searched Projects list from state passed', () => {
- vm.toggleSearchProjectsList(true);
- expect(vm.isLoadingProjects).toBeFalsy();
- expect(vm.isFrequentsListVisible).toBeFalsy();
- expect(vm.isSearchListVisible).toBeTruthy();
-
- vm.toggleSearchProjectsList(false);
- expect(vm.isLoadingProjects).toBeTruthy();
- expect(vm.isFrequentsListVisible).toBeTruthy();
- expect(vm.isSearchListVisible).toBeFalsy();
- });
- });
-
- describe('toggleLoader', () => {
- it('should toggle props which control visibility of list loading animation from state passed', () => {
- vm.toggleLoader(true);
- expect(vm.isFrequentsListVisible).toBeFalsy();
- expect(vm.isSearchListVisible).toBeFalsy();
- expect(vm.isLoadingProjects).toBeTruthy();
-
- vm.toggleLoader(false);
- expect(vm.isFrequentsListVisible).toBeTruthy();
- expect(vm.isSearchListVisible).toBeTruthy();
- expect(vm.isLoadingProjects).toBeFalsy();
- });
- });
-
- describe('fetchFrequentProjects', () => {
- it('should set props for loading animation to `true` while frequent projects list is being loaded', () => {
- spyOn(vm, 'toggleLoader');
-
- vm.fetchFrequentProjects();
- expect(vm.isLocalStorageFailed).toBeFalsy();
- expect(vm.toggleLoader).toHaveBeenCalledWith(true);
- });
-
- it('should set props for loading animation to `false` and props for frequent projects list to `true` once data is loaded', () => {
- const mockData = [mockProject];
-
- spyOn(vm.service, 'getFrequentProjects').and.returnValue(mockData);
- spyOn(vm.store, 'setFrequentProjects');
- spyOn(vm, 'toggleFrequentProjectsList');
-
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).toHaveBeenCalled();
- expect(vm.store.setFrequentProjects).toHaveBeenCalledWith(mockData);
- expect(vm.toggleFrequentProjectsList).toHaveBeenCalledWith(true);
- });
-
- it('should set props for failure message to `true` when method fails to fetch frequent projects list', () => {
- spyOn(vm.service, 'getFrequentProjects').and.returnValue(null);
- spyOn(vm.store, 'setFrequentProjects');
- spyOn(vm, 'toggleFrequentProjectsList');
-
- expect(vm.isLocalStorageFailed).toBeFalsy();
-
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).toHaveBeenCalled();
- expect(vm.store.setFrequentProjects).toHaveBeenCalledWith([]);
- expect(vm.toggleFrequentProjectsList).toHaveBeenCalledWith(true);
- expect(vm.isLocalStorageFailed).toBeTruthy();
- });
-
- it('should set props for search results list to `true` if search query was already made previously', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('md');
- spyOn(vm.service, 'getFrequentProjects');
- spyOn(vm, 'toggleSearchProjectsList');
-
- vm.searchQuery = 'test';
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).not.toHaveBeenCalled();
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- });
-
- it('should set props for frequent projects list to `true` if search query was already made but screen size is less than 768px', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
- spyOn(vm, 'toggleSearchProjectsList');
- spyOn(vm.service, 'getFrequentProjects');
-
- vm.searchQuery = 'test';
- vm.fetchFrequentProjects();
- expect(vm.service.getFrequentProjects).toHaveBeenCalled();
- expect(vm.toggleSearchProjectsList).not.toHaveBeenCalled();
- });
- });
-
- describe('fetchSearchedProjects', () => {
- const searchQuery = 'test';
-
- it('should perform search with provided search query', done => {
- const mockData = [mockRawProject];
- spyOn(vm, 'toggleLoader');
- spyOn(vm, 'toggleSearchProjectsList');
- spyOn(vm.service, 'getSearchedProjects').and.returnValue(returnServicePromise(mockData));
- spyOn(vm.store, 'setSearchedProjects');
-
- vm.fetchSearchedProjects(searchQuery);
- setTimeout(() => {
- expect(vm.searchQuery).toBe(searchQuery);
- expect(vm.toggleLoader).toHaveBeenCalledWith(true);
- expect(vm.service.getSearchedProjects).toHaveBeenCalledWith(searchQuery);
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- expect(vm.store.setSearchedProjects).toHaveBeenCalledWith(mockData);
- done();
- }, 0);
- });
-
- it('should update props for showing search failure', done => {
- spyOn(vm, 'toggleSearchProjectsList');
- spyOn(vm.service, 'getSearchedProjects').and.returnValue(returnServicePromise({}, true));
-
- vm.fetchSearchedProjects(searchQuery);
- setTimeout(() => {
- expect(vm.searchQuery).toBe(searchQuery);
- expect(vm.service.getSearchedProjects).toHaveBeenCalledWith(searchQuery);
- expect(vm.isSearchFailed).toBeTruthy();
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- done();
- }, 0);
- });
- });
-
- describe('logCurrentProjectAccess', () => {
- it('should log current project access via service', done => {
- spyOn(vm.service, 'logProjectAccess');
-
- vm.currentProject = mockProject;
- vm.logCurrentProjectAccess();
-
- setTimeout(() => {
- expect(vm.service.logProjectAccess).toHaveBeenCalledWith(mockProject);
- done();
- }, 1);
- });
- });
-
- describe('handleSearchClear', () => {
- it('should show frequent projects list when search input is cleared', () => {
- spyOn(vm.store, 'clearSearchedProjects');
- spyOn(vm, 'toggleFrequentProjectsList');
-
- vm.handleSearchClear();
-
- expect(vm.toggleFrequentProjectsList).toHaveBeenCalledWith(true);
- expect(vm.store.clearSearchedProjects).toHaveBeenCalled();
- expect(vm.searchQuery).toBe('');
- });
- });
-
- describe('handleSearchFailure', () => {
- it('should show failure message within dropdown', () => {
- spyOn(vm, 'toggleSearchProjectsList');
-
- vm.handleSearchFailure();
- expect(vm.toggleSearchProjectsList).toHaveBeenCalledWith(true);
- expect(vm.isSearchFailed).toBeTruthy();
- });
- });
- });
-
- describe('created', () => {
- it('should bind event listeners on eventHub', done => {
- spyOn(eventHub, '$on');
-
- createComponent().$mount();
-
- Vue.nextTick(() => {
- expect(eventHub.$on).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- expect(eventHub.$on).toHaveBeenCalledWith('searchProjects', jasmine.any(Function));
- expect(eventHub.$on).toHaveBeenCalledWith('searchCleared', jasmine.any(Function));
- expect(eventHub.$on).toHaveBeenCalledWith('searchFailed', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', done => {
- const vm = createComponent();
- spyOn(eventHub, '$off');
-
- vm.$mount();
- vm.$destroy();
-
- Vue.nextTick(() => {
- expect(eventHub.$off).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- expect(eventHub.$off).toHaveBeenCalledWith('searchProjects', jasmine.any(Function));
- expect(eventHub.$off).toHaveBeenCalledWith('searchCleared', jasmine.any(Function));
- expect(eventHub.$off).toHaveBeenCalledWith('searchFailed', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('template', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should render search input', () => {
- expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
- });
-
- it('should render loading animation', done => {
- vm.toggleLoader(true);
- Vue.nextTick(() => {
- const loadingEl = vm.$el.querySelector('.loading-animation');
-
- expect(loadingEl).toBeDefined();
- expect(loadingEl.classList.contains('prepend-top-20')).toBeTruthy();
- expect(loadingEl.querySelector('i').getAttribute('aria-label')).toBe('Loading projects');
- done();
- });
- });
-
- it('should render frequent projects list header', done => {
- vm.toggleFrequentProjectsList(true);
- Vue.nextTick(() => {
- const sectionHeaderEl = vm.$el.querySelector('.section-header');
-
- expect(sectionHeaderEl).toBeDefined();
- expect(sectionHeaderEl.innerText.trim()).toBe('Frequently visited');
- done();
- });
- });
-
- it('should render frequent projects list', done => {
- vm.toggleFrequentProjectsList(true);
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.projects-list-frequent-container')).toBeDefined();
- done();
- });
- });
-
- it('should render searched projects list', done => {
- vm.toggleSearchProjectsList(true);
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.section-header')).toBe(null);
- expect(vm.$el.querySelector('.projects-list-search-container')).toBeDefined();
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
deleted file mode 100644
index 2bafb4e81ca..00000000000
--- a/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import Vue from 'vue';
-
-import projectsListFrequentComponent from '~/projects_dropdown/components/projects_list_frequent.vue';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { mockFrequents } from '../mock_data';
-
-const createComponent = () => {
- const Component = Vue.extend(projectsListFrequentComponent);
-
- return mountComponent(Component, {
- projects: mockFrequents,
- localStorageFailed: false,
- });
-};
-
-describe('ProjectsListFrequentComponent', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('isListEmpty', () => {
- it('should return `true` or `false` representing whether if `projects` is empty of not', () => {
- vm.projects = [];
- expect(vm.isListEmpty).toBeTruthy();
-
- vm.projects = mockFrequents;
- expect(vm.isListEmpty).toBeFalsy();
- });
- });
-
- describe('listEmptyMessage', () => {
- it('should return appropriate empty list message based on value of `localStorageFailed` prop', () => {
- vm.localStorageFailed = true;
- expect(vm.listEmptyMessage).toBe('This feature requires browser localStorage support');
-
- vm.localStorageFailed = false;
- expect(vm.listEmptyMessage).toBe('Projects you visit often will appear here');
- });
- });
- });
-
- describe('template', () => {
- it('should render component element with list of projects', (done) => {
- vm.projects = mockFrequents;
-
- Vue.nextTick(() => {
- expect(vm.$el.classList.contains('projects-list-frequent-container')).toBeTruthy();
- expect(vm.$el.querySelectorAll('ul.list-unstyled').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(5);
- done();
- });
- });
-
- it('should render component element with empty message', (done) => {
- vm.projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('li.section-empty').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(0);
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
deleted file mode 100644
index c4b86d77034..00000000000
--- a/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import Vue from 'vue';
-
-import projectsListSearchComponent from '~/projects_dropdown/components/projects_list_search.vue';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import { mockProject } from '../mock_data';
-
-const createComponent = () => {
- const Component = Vue.extend(projectsListSearchComponent);
-
- return mountComponent(Component, {
- projects: [mockProject],
- matcher: 'lab',
- searchFailed: false,
- });
-};
-
-describe('ProjectsListSearchComponent', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('isListEmpty', () => {
- it('should return `true` or `false` representing whether if `projects` is empty of not', () => {
- vm.projects = [];
- expect(vm.isListEmpty).toBeTruthy();
-
- vm.projects = [mockProject];
- expect(vm.isListEmpty).toBeFalsy();
- });
- });
-
- describe('listEmptyMessage', () => {
- it('should return appropriate empty list message based on value of `searchFailed` prop', () => {
- vm.searchFailed = true;
- expect(vm.listEmptyMessage).toBe('Something went wrong on our end.');
-
- vm.searchFailed = false;
- expect(vm.listEmptyMessage).toBe('Sorry, no projects matched your search');
- });
- });
- });
-
- describe('template', () => {
- it('should render component element with list of projects', (done) => {
- vm.projects = [mockProject];
-
- Vue.nextTick(() => {
- expect(vm.$el.classList.contains('projects-list-search-container')).toBeTruthy();
- expect(vm.$el.querySelectorAll('ul.list-unstyled').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(1);
- done();
- });
- });
-
- it('should render component element with empty message', (done) => {
- vm.projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('li.section-empty').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(0);
- done();
- });
- });
-
- it('should render component element with failure message', (done) => {
- vm.searchFailed = true;
- vm.projects = [];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('li.section-empty.section-failure').length).toBe(1);
- expect(vm.$el.querySelectorAll('li.projects-list-item-container').length).toBe(0);
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/components/search_spec.js b/spec/javascripts/projects_dropdown/components/search_spec.js
deleted file mode 100644
index 427f5024e3a..00000000000
--- a/spec/javascripts/projects_dropdown/components/search_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import Vue from 'vue';
-
-import searchComponent from '~/projects_dropdown/components/search.vue';
-import eventHub from '~/projects_dropdown/event_hub';
-
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-const createComponent = () => {
- const Component = Vue.extend(searchComponent);
-
- return mountComponent(Component);
-};
-
-describe('SearchComponent', () => {
- describe('methods', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('setFocus', () => {
- it('should set focus to search input', () => {
- spyOn(vm.$refs.search, 'focus');
-
- vm.setFocus();
- expect(vm.$refs.search.focus).toHaveBeenCalled();
- });
- });
-
- describe('emitSearchEvents', () => {
- it('should emit `searchProjects` event via eventHub when `searchQuery` present', () => {
- const searchQuery = 'test';
- spyOn(eventHub, '$emit');
- vm.searchQuery = searchQuery;
- vm.emitSearchEvents();
- expect(eventHub.$emit).toHaveBeenCalledWith('searchProjects', searchQuery);
- });
-
- it('should emit `searchCleared` event via eventHub when `searchQuery` is cleared', () => {
- spyOn(eventHub, '$emit');
- vm.searchQuery = '';
- vm.emitSearchEvents();
- expect(eventHub.$emit).toHaveBeenCalledWith('searchCleared');
- });
- });
- });
-
- describe('mounted', () => {
- it('should listen `dropdownOpen` event', (done) => {
- spyOn(eventHub, '$on');
- createComponent();
-
- Vue.nextTick(() => {
- expect(eventHub.$on).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', (done) => {
- const vm = createComponent();
- spyOn(eventHub, '$off');
-
- vm.$mount();
- vm.$destroy();
-
- Vue.nextTick(() => {
- expect(eventHub.$off).toHaveBeenCalledWith('dropdownOpen', jasmine.any(Function));
- done();
- });
- });
- });
-
- describe('template', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('should render component element', () => {
- const inputEl = vm.$el.querySelector('input.form-control');
-
- expect(vm.$el.classList.contains('search-input-container')).toBeTruthy();
- expect(inputEl).not.toBe(null);
- expect(inputEl.getAttribute('placeholder')).toBe('Search your projects');
- expect(vm.$el.querySelector('.search-icon')).toBeDefined();
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/mock_data.js b/spec/javascripts/projects_dropdown/mock_data.js
deleted file mode 100644
index d6a79fb8ac1..00000000000
--- a/spec/javascripts/projects_dropdown/mock_data.js
+++ /dev/null
@@ -1,96 +0,0 @@
-export const currentSession = {
- username: 'root',
- storageKey: 'root/frequent-projects',
- apiVersion: 'v4',
- project: {
- id: 1,
- name: 'dummy-project',
- namespace: 'SamepleGroup / Dummy-Project',
- webUrl: 'http://127.0.0.1/samplegroup/dummy-project',
- avatarUrl: null,
- lastAccessedOn: Date.now(),
- },
-};
-
-export const mockProject = {
- id: 1,
- name: 'GitLab Community Edition',
- namespace: 'gitlab-org / gitlab-ce',
- webUrl: 'http://127.0.0.1:3000/gitlab-org/gitlab-ce',
- avatarUrl: null,
-};
-
-export const mockRawProject = {
- id: 1,
- name: 'GitLab Community Edition',
- name_with_namespace: 'gitlab-org / gitlab-ce',
- web_url: 'http://127.0.0.1:3000/gitlab-org/gitlab-ce',
- avatar_url: null,
-};
-
-export const mockFrequents = [
- {
- id: 1,
- name: 'GitLab Community Edition',
- namespace: 'gitlab-org / gitlab-ce',
- webUrl: 'http://127.0.0.1:3000/gitlab-org/gitlab-ce',
- avatarUrl: null,
- },
- {
- id: 2,
- name: 'GitLab CI',
- namespace: 'gitlab-org / gitlab-ci',
- webUrl: 'http://127.0.0.1:3000/gitlab-org/gitlab-ci',
- avatarUrl: null,
- },
- {
- id: 3,
- name: 'Typeahead.Js',
- namespace: 'twitter / typeahead-js',
- webUrl: 'http://127.0.0.1:3000/twitter/typeahead-js',
- avatarUrl: '/uploads/-/system/project/avatar/7/TWBS.png',
- },
- {
- id: 4,
- name: 'Intel',
- namespace: 'platform / hardware / bsp / intel',
- webUrl: 'http://127.0.0.1:3000/platform/hardware/bsp/intel',
- avatarUrl: null,
- },
- {
- id: 5,
- name: 'v4.4',
- namespace: 'platform / hardware / bsp / kernel / common / v4.4',
- webUrl: 'http://localhost:3000/platform/hardware/bsp/kernel/common/v4.4',
- avatarUrl: null,
- },
-];
-
-export const unsortedFrequents = [
- { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
- { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
- { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
- { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
- { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
- { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
- { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
- { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
- { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
-];
-
-/**
- * This const has a specific order which tests authenticity
- * of `ProjectsService.getTopFrequentProjects` method so
- * DO NOT change order of items in this const.
- */
-export const sortedFrequents = [
- { id: 10, frequency: 46, lastAccessedOn: 1483251641543 },
- { id: 3, frequency: 44, lastAccessedOn: 1497675908472 },
- { id: 7, frequency: 42, lastAccessedOn: 1486815299875 },
- { id: 5, frequency: 34, lastAccessedOn: 1488089211943 },
- { id: 8, frequency: 33, lastAccessedOn: 1500762279114 },
- { id: 6, frequency: 14, lastAccessedOn: 1493517292488 },
- { id: 2, frequency: 14, lastAccessedOn: 1488240890738 },
- { id: 1, frequency: 12, lastAccessedOn: 1491400843391 },
- { id: 4, frequency: 8, lastAccessedOn: 1497979281815 },
-];
diff --git a/spec/javascripts/projects_dropdown/service/projects_service_spec.js b/spec/javascripts/projects_dropdown/service/projects_service_spec.js
deleted file mode 100644
index cfd1bb7d24f..00000000000
--- a/spec/javascripts/projects_dropdown/service/projects_service_spec.js
+++ /dev/null
@@ -1,179 +0,0 @@
-import Vue from 'vue';
-import VueResource from 'vue-resource';
-
-import bp from '~/breakpoints';
-import ProjectsService from '~/projects_dropdown/service/projects_service';
-import { FREQUENT_PROJECTS } from '~/projects_dropdown/constants';
-import { currentSession, unsortedFrequents, sortedFrequents } from '../mock_data';
-
-Vue.use(VueResource);
-
-FREQUENT_PROJECTS.MAX_COUNT = 3;
-
-describe('ProjectsService', () => {
- let service;
-
- beforeEach(() => {
- gon.api_version = currentSession.apiVersion;
- gon.current_user_id = 1;
- service = new ProjectsService(currentSession.username);
- });
-
- describe('contructor', () => {
- it('should initialize default properties of class', () => {
- expect(service.isLocalStorageAvailable).toBeTruthy();
- expect(service.currentUserName).toBe(currentSession.username);
- expect(service.storageKey).toBe(currentSession.storageKey);
- expect(service.projectsPath).toBeDefined();
- });
- });
-
- describe('getSearchedProjects', () => {
- it('should return promise from VueResource HTTP GET', () => {
- spyOn(service.projectsPath, 'get').and.stub();
-
- const searchQuery = 'lab';
- const queryParams = {
- simple: true,
- per_page: 20,
- membership: true,
- order_by: 'last_activity_at',
- search: searchQuery,
- };
-
- service.getSearchedProjects(searchQuery);
- expect(service.projectsPath.get).toHaveBeenCalledWith(queryParams);
- });
- });
-
- describe('logProjectAccess', () => {
- let storage;
-
- beforeEach(() => {
- storage = {};
-
- spyOn(window.localStorage, 'setItem').and.callFake((storageKey, value) => {
- storage[storageKey] = value;
- });
-
- spyOn(window.localStorage, 'getItem').and.callFake((storageKey) => {
- if (storage[storageKey]) {
- return storage[storageKey];
- }
-
- return null;
- });
- });
-
- it('should create a project store if it does not exist and adds a project', () => {
- service.logProjectAccess(currentSession.project);
-
- const projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects.length).toBe(1);
- expect(projects[0].frequency).toBe(1);
- expect(projects[0].lastAccessedOn).toBeDefined();
- });
-
- it('should prevent inserting same report multiple times into store', () => {
- service.logProjectAccess(currentSession.project);
- service.logProjectAccess(currentSession.project);
-
- const projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects.length).toBe(1);
- });
-
- it('should increase frequency of report if it was logged multiple times over the course of an hour', () => {
- let projects;
- spyOn(Math, 'abs').and.returnValue(3600001); // this will lead to `diff` > 1;
- service.logProjectAccess(currentSession.project);
-
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].frequency).toBe(1);
-
- service.logProjectAccess(currentSession.project);
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].frequency).toBe(2);
- expect(projects[0].lastAccessedOn).not.toBe(currentSession.project.lastAccessedOn);
- });
-
- it('should always update project metadata', () => {
- let projects;
- const oldProject = {
- ...currentSession.project,
- };
-
- const newProject = {
- ...currentSession.project,
- name: 'New Name',
- avatarUrl: 'new/avatar.png',
- namespace: 'New / Namespace',
- webUrl: 'http://localhost/new/web/url',
- };
-
- service.logProjectAccess(oldProject);
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].name).toBe(oldProject.name);
- expect(projects[0].avatarUrl).toBe(oldProject.avatarUrl);
- expect(projects[0].namespace).toBe(oldProject.namespace);
- expect(projects[0].webUrl).toBe(oldProject.webUrl);
-
- service.logProjectAccess(newProject);
- projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects[0].name).toBe(newProject.name);
- expect(projects[0].avatarUrl).toBe(newProject.avatarUrl);
- expect(projects[0].namespace).toBe(newProject.namespace);
- expect(projects[0].webUrl).toBe(newProject.webUrl);
- });
-
- it('should not add more than 20 projects in store', () => {
- for (let i = 1; i <= 5; i += 1) {
- const project = Object.assign(currentSession.project, { id: i });
- service.logProjectAccess(project);
- }
-
- const projects = JSON.parse(storage[currentSession.storageKey]);
- expect(projects.length).toBe(3);
- });
- });
-
- describe('getTopFrequentProjects', () => {
- let storage = {};
-
- beforeEach(() => {
- storage[currentSession.storageKey] = JSON.stringify(unsortedFrequents);
-
- spyOn(window.localStorage, 'getItem').and.callFake((storageKey) => {
- if (storage[storageKey]) {
- return storage[storageKey];
- }
-
- return null;
- });
- });
-
- it('should return top 5 frequently accessed projects for desktop screens', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('md');
- const frequentProjects = service.getTopFrequentProjects();
-
- expect(frequentProjects.length).toBe(5);
- frequentProjects.forEach((project, index) => {
- expect(project.id).toBe(sortedFrequents[index].id);
- });
- });
-
- it('should return top 3 frequently accessed projects for mobile screens', () => {
- spyOn(bp, 'getBreakpointSize').and.returnValue('sm');
- const frequentProjects = service.getTopFrequentProjects();
-
- expect(frequentProjects.length).toBe(3);
- frequentProjects.forEach((project, index) => {
- expect(project.id).toBe(sortedFrequents[index].id);
- });
- });
-
- it('should return empty array if there are no projects available in store', () => {
- storage = {};
- expect(service.getTopFrequentProjects().length).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/projects_dropdown/store/projects_store_spec.js b/spec/javascripts/projects_dropdown/store/projects_store_spec.js
deleted file mode 100644
index e57399d37cd..00000000000
--- a/spec/javascripts/projects_dropdown/store/projects_store_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import ProjectsStore from '~/projects_dropdown/store/projects_store';
-import { mockProject, mockRawProject } from '../mock_data';
-
-describe('ProjectsStore', () => {
- let store;
-
- beforeEach(() => {
- store = new ProjectsStore();
- });
-
- describe('setFrequentProjects', () => {
- it('should set frequent projects list to state', () => {
- store.setFrequentProjects([mockProject]);
-
- expect(store.getFrequentProjects().length).toBe(1);
- expect(store.getFrequentProjects()[0].id).toBe(mockProject.id);
- });
- });
-
- describe('setSearchedProjects', () => {
- it('should set searched projects list to state', () => {
- store.setSearchedProjects([mockRawProject]);
-
- const processedProjects = store.getSearchedProjects();
- expect(processedProjects.length).toBe(1);
- expect(processedProjects[0].id).toBe(mockRawProject.id);
- expect(processedProjects[0].namespace).toBe(mockRawProject.name_with_namespace);
- expect(processedProjects[0].webUrl).toBe(mockRawProject.web_url);
- expect(processedProjects[0].avatarUrl).toBe(mockRawProject.avatar_url);
- });
- });
-
- describe('clearSearchedProjects', () => {
- it('should clear searched projects list from state', () => {
- store.setSearchedProjects([mockRawProject]);
- expect(store.getSearchedProjects().length).toBe(1);
- store.clearSearchedProjects();
- expect(store.getSearchedProjects().length).toBe(0);
- });
- });
-});
diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
new file mode 100644
index 00000000000..b58de607ece
--- /dev/null
+++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js
@@ -0,0 +1,243 @@
+import $ from 'jquery';
+import Vue from 'vue';
+
+import TimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Issuable Time Tracker', () => {
+ let initialData;
+ let vm;
+
+ const initTimeTrackingComponent = opts => {
+ setFixtures(`
+ <div>
+ <div id="mock-container"></div>
+ </div>
+ `);
+
+ initialData = {
+ time_estimate: opts.timeEstimate,
+ time_spent: opts.timeSpent,
+ human_time_estimate: opts.timeEstimateHumanReadable,
+ human_time_spent: opts.timeSpentHumanReadable,
+ rootPath: '/',
+ };
+
+ const TimeTrackingComponent = Vue.extend({
+ ...TimeTracker,
+ components: {
+ ...TimeTracker.components,
+ transition: {
+ // disable animations
+ template: '<div><slot></slot></div>',
+ },
+ },
+ });
+ vm = mountComponent(TimeTrackingComponent, initialData, '#mock-container');
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('Initialization', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 100000,
+ timeSpent: 5000,
+ timeEstimateHumanReadable: '2h 46m',
+ timeSpentHumanReadable: '1h 23m',
+ });
+ });
+
+ it('should return something defined', () => {
+ expect(vm).toBeDefined();
+ });
+
+ it('should correctly set timeEstimate', done => {
+ Vue.nextTick(() => {
+ expect(vm.timeEstimate).toBe(initialData.time_estimate);
+ done();
+ });
+ });
+
+ it('should correctly set time_spent', done => {
+ Vue.nextTick(() => {
+ expect(vm.timeSpent).toBe(initialData.time_spent);
+ done();
+ });
+ });
+ });
+
+ describe('Content Display', () => {
+ describe('Panes', () => {
+ describe('Comparison pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 100000,
+ timeSpent: 5000,
+ timeEstimateHumanReadable: '',
+ timeSpentHumanReadable: '',
+ });
+ });
+
+ it('should show the "Comparison" pane when timeEstimate and time_spent are truthy', done => {
+ Vue.nextTick(() => {
+ expect(vm.showComparisonState).toBe(true);
+ const $comparisonPane = vm.$el.querySelector('.time-tracking-comparison-pane');
+ expect($comparisonPane).toBeVisible();
+ done();
+ });
+ });
+
+ 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);
+ 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);
+ done();
+ });
+ });
+
+ it('should display the remaining meter with the correct background color when over estimate', done => {
+ 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);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('Estimate only pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 100000,
+ timeSpent: 0,
+ timeEstimateHumanReadable: '2h 46m',
+ timeSpentHumanReadable: '',
+ });
+ });
+
+ it('should display the human readable version of time estimated', done => {
+ Vue.nextTick(() => {
+ const estimateText = vm.$el.querySelector('.time-tracking-estimate-only-pane')
+ .innerText;
+ const correctText = 'Estimated: 2h 46m';
+
+ expect(estimateText).toBe(correctText);
+ done();
+ });
+ });
+ });
+
+ describe('Spent only pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 0,
+ timeSpent: 5000,
+ timeEstimateHumanReadable: '2h 46m',
+ timeSpentHumanReadable: '1h 23m',
+ });
+ });
+
+ it('should display the human readable version of time spent', done => {
+ Vue.nextTick(() => {
+ const spentText = vm.$el.querySelector('.time-tracking-spend-only-pane').innerText;
+ const correctText = 'Spent: 1h 23m';
+
+ expect(spentText).toBe(correctText);
+ done();
+ });
+ });
+ });
+
+ describe('No time tracking pane', () => {
+ beforeEach(() => {
+ initTimeTrackingComponent({
+ timeEstimate: 0,
+ timeSpent: 0,
+ timeEstimateHumanReadable: '',
+ timeSpentHumanReadable: '',
+ });
+ });
+
+ it('should only show the "No time tracking" pane when both timeEstimate and time_spent are falsey', done => {
+ Vue.nextTick(() => {
+ const $noTrackingPane = vm.$el.querySelector('.time-tracking-no-tracking-pane');
+ const noTrackingText = $noTrackingPane.innerText;
+ const correctText = 'No estimate or time spent';
+
+ expect(vm.showNoTimeTrackingState).toBe(true);
+ expect($noTrackingPane).toBeVisible();
+ expect(noTrackingText).toBe(correctText);
+ done();
+ });
+ });
+ });
+
+ describe('Help pane', () => {
+ const helpButton = () => vm.$el.querySelector('.help-button');
+ const closeHelpButton = () => vm.$el.querySelector('.close-help-button');
+ const helpPane = () => vm.$el.querySelector('.time-tracking-help-state');
+
+ beforeEach(done => {
+ initTimeTrackingComponent({ timeEstimate: 0, timeSpent: 0 });
+
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should not show the "Help" pane by default', () => {
+ expect(vm.showHelpState).toBe(false);
+ expect(helpPane()).toBeNull();
+ });
+
+ it('should show the "Help" pane when help button is clicked', done => {
+ helpButton().click();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.showHelpState).toBe(true);
+ expect(helpPane()).toBeVisible();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('should not show the "Help" pane when help button is clicked and then closed', done => {
+ helpButton().click();
+
+ Vue.nextTick()
+ .then(() => closeHelpButton().click())
+ .then(() => Vue.nextTick())
+ .then(() => {
+ expect(vm.showHelpState).toBe(false);
+ expect(helpPane()).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/sidebar/todo_spec.js b/spec/javascripts/sidebar/todo_spec.js
deleted file mode 100644
index a929b804a29..00000000000
--- a/spec/javascripts/sidebar/todo_spec.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import Vue from 'vue';
-
-import SidebarTodos from '~/sidebar/components/todo_toggle/todo.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-const createComponent = ({
- issuableId = 1,
- issuableType = 'epic',
- isTodo,
- isActionActive,
- collapsed,
-}) => {
- const Component = Vue.extend(SidebarTodos);
-
- return mountComponent(Component, {
- issuableId,
- issuableType,
- isTodo,
- isActionActive,
- collapsed,
- });
-};
-
-describe('SidebarTodo', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent({});
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('computed', () => {
- describe('buttonClasses', () => {
- it('returns todo button classes for when `collapsed` prop is `false`', () => {
- expect(vm.buttonClasses).toBe('btn btn-default btn-todo issuable-header-btn float-right');
- });
-
- it('returns todo button classes for when `collapsed` prop is `true`', done => {
- vm.collapsed = true;
- Vue.nextTick()
- .then(() => {
- expect(vm.buttonClasses).toBe('btn-blank btn-todo sidebar-collapsed-icon dont-change-state');
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('buttonLabel', () => {
- it('returns todo button text for marking todo as done when `isTodo` prop is `true`', () => {
- expect(vm.buttonLabel).toBe('Mark todo as done');
- });
-
- it('returns todo button text for add todo when `isTodo` prop is `false`', done => {
- vm.isTodo = false;
- Vue.nextTick()
- .then(() => {
- expect(vm.buttonLabel).toBe('Add todo');
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('collapsedButtonIconClasses', () => {
- it('returns collapsed button icon class when `isTodo` prop is `true`', () => {
- expect(vm.collapsedButtonIconClasses).toBe('todo-undone');
- });
-
- it('returns empty string when `isTodo` prop is `false`', done => {
- vm.isTodo = false;
- Vue.nextTick()
- .then(() => {
- expect(vm.collapsedButtonIconClasses).toBe('');
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('collapsedButtonIcon', () => {
- it('returns button icon name when `isTodo` prop is `true`', () => {
- expect(vm.collapsedButtonIcon).toBe('todo-done');
- });
-
- it('returns button icon name when `isTodo` prop is `false`', done => {
- vm.isTodo = false;
- Vue.nextTick()
- .then(() => {
- expect(vm.collapsedButtonIcon).toBe('todo-add');
- })
- .then(done)
- .catch(done.fail);
- });
- });
- });
-
- describe('methods', () => {
- describe('handleButtonClick', () => {
- it('emits `toggleTodo` event on component', () => {
- spyOn(vm, '$emit');
- vm.handleButtonClick();
- expect(vm.$emit).toHaveBeenCalledWith('toggleTodo');
- });
- });
- });
-
- describe('template', () => {
- it('renders component container element', () => {
- const dataAttributes = {
- issuableId: '1',
- issuableType: 'epic',
- originalTitle: 'Mark todo as done',
- placement: 'left',
- container: 'body',
- boundary: 'viewport',
- };
- expect(vm.$el.nodeName).toBe('BUTTON');
-
- const elDataAttrs = vm.$el.dataset;
- Object.keys(elDataAttrs).forEach((attr) => {
- expect(elDataAttrs[attr]).toBe(dataAttributes[attr]);
- });
- });
-
- it('renders button label element when `collapsed` prop is `false`', () => {
- const buttonLabelEl = vm.$el.querySelector('span.issuable-todo-inner');
- expect(buttonLabelEl).not.toBeNull();
- expect(buttonLabelEl.innerText.trim()).toBe('Mark todo as done');
- });
-
- it('renders button icon when `collapsed` prop is `true`', done => {
- vm.collapsed = true;
- Vue.nextTick()
- .then(() => {
- const buttonIconEl = vm.$el.querySelector('svg');
- expect(buttonIconEl).not.toBeNull();
- expect(buttonIconEl.querySelector('use').getAttribute('xlink:href')).toContain('todo-done');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('renders loading icon when `isActionActive` prop is true', done => {
- vm.isActionActive = true;
- Vue.nextTick()
- .then(() => {
- const loadingEl = vm.$el.querySelector('span.loading-container');
- expect(loadingEl).not.toBeNull();
- })
- .then(done)
- .catch(done.fail);
- });
- });
-});
diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js
index 60153672214..d9b6dd1d487 100644
--- a/spec/javascripts/smart_interval_spec.js
+++ b/spec/javascripts/smart_interval_spec.js
@@ -1,12 +1,12 @@
import $ from 'jquery';
import _ from 'underscore';
import SmartInterval from '~/smart_interval';
+import waitForPromises from 'spec/helpers/wait_for_promises';
describe('SmartInterval', function () {
const DEFAULT_MAX_INTERVAL = 100;
const DEFAULT_STARTING_INTERVAL = 5;
const DEFAULT_SHORT_TIMEOUT = 75;
- const DEFAULT_LONG_TIMEOUT = 1000;
const DEFAULT_INCREMENT_FACTOR = 2;
function createDefaultSmartInterval(config) {
@@ -27,52 +27,65 @@ describe('SmartInterval', function () {
return new SmartInterval(defaultParams);
}
+ beforeEach(() => {
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
describe('Increment Interval', function () {
- beforeEach(function () {
- this.smartInterval = createDefaultSmartInterval();
- });
+ it('should increment the interval delay', (done) => {
+ const smartInterval = createDefaultSmartInterval();
- it('should increment the interval delay', function (done) {
- const interval = this.smartInterval;
- setTimeout(() => {
- const intervalConfig = this.smartInterval.cfg;
- const iterationCount = 4;
- const maxIntervalAfterIterations = intervalConfig.startingInterval *
- (intervalConfig.incrementByFactorOf ** (iterationCount - 1)); // 40
- const currentInterval = interval.getCurrentInterval();
-
- // Provide some flexibility for performance of testing environment
- expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval);
- expect(currentInterval <= maxIntervalAfterIterations).toBeTruthy();
-
- done();
- }, DEFAULT_SHORT_TIMEOUT); // 4 iterations, increment by 2x = (5 + 10 + 20 + 40)
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ const intervalConfig = smartInterval.cfg;
+ const iterationCount = 4;
+ const maxIntervalAfterIterations = intervalConfig.startingInterval *
+ (intervalConfig.incrementByFactorOf ** iterationCount);
+ const currentInterval = smartInterval.getCurrentInterval();
+
+ // Provide some flexibility for performance of testing environment
+ expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval);
+ expect(currentInterval).toBeLessThanOrEqual(maxIntervalAfterIterations);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('should not increment past maxInterval', function (done) {
- const interval = this.smartInterval;
+ it('should not increment past maxInterval', (done) => {
+ const smartInterval = createDefaultSmartInterval({ maxInterval: DEFAULT_STARTING_INTERVAL });
- setTimeout(() => {
- const currentInterval = interval.getCurrentInterval();
- expect(currentInterval).toBe(interval.cfg.maxInterval);
+ jasmine.clock().tick(DEFAULT_STARTING_INTERVAL);
+ jasmine.clock().tick(DEFAULT_STARTING_INTERVAL * DEFAULT_INCREMENT_FACTOR);
- done();
- }, DEFAULT_LONG_TIMEOUT);
+ waitForPromises()
+ .then(() => {
+ const currentInterval = smartInterval.getCurrentInterval();
+ expect(currentInterval).toBe(smartInterval.cfg.maxInterval);
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('does not increment while waiting for callback', function () {
- jasmine.clock().install();
-
+ it('does not increment while waiting for callback', done => {
const smartInterval = createDefaultSmartInterval({
callback: () => new Promise($.noop),
});
jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- const oneInterval = smartInterval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR;
- expect(smartInterval.getCurrentInterval()).toEqual(oneInterval);
-
- jasmine.clock().uninstall();
+ waitForPromises()
+ .then(() => {
+ const oneInterval = smartInterval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR;
+ expect(smartInterval.getCurrentInterval()).toEqual(oneInterval);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
@@ -84,34 +97,39 @@ describe('SmartInterval', function () {
it('should cancel an interval', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- interval.cancel();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- const { intervalId } = interval.state;
- const currentInterval = interval.getCurrentInterval();
- const intervalLowerLimit = interval.cfg.startingInterval;
+ interval.cancel();
- expect(intervalId).toBeUndefined();
- expect(currentInterval).toBe(intervalLowerLimit);
+ waitForPromises()
+ .then(() => {
+ const { intervalId } = interval.state;
+ const currentInterval = interval.getCurrentInterval();
+ const intervalLowerLimit = interval.cfg.startingInterval;
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(intervalId).toBeUndefined();
+ expect(currentInterval).toBe(intervalLowerLimit);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should resume an interval', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- interval.cancel();
-
- interval.resume();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- const { intervalId } = interval.state;
+ interval.cancel();
- expect(intervalId).toBeTruthy();
+ interval.resume();
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ waitForPromises()
+ .then(() => {
+ const { intervalId } = interval.state;
+ expect(intervalId).toBeTruthy();
+ })
+ .then(done)
+ .catch(done.fail);
});
});
@@ -126,64 +144,79 @@ describe('SmartInterval', function () {
it('should pause when page is not visible', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- expect(interval.state.intervalId).toBeTruthy();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ expect(interval.state.intervalId).toBeTruthy();
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
- expect(interval.state.intervalId).toBeUndefined();
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(interval.state.intervalId).toBeUndefined();
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('should change to the hidden interval when page is not visible', function (done) {
+ it('should change to the hidden interval when page is not visible', done => {
const HIDDEN_INTERVAL = 1500;
const interval = createDefaultSmartInterval({ hiddenInterval: HIDDEN_INTERVAL });
- setTimeout(() => {
- expect(interval.state.intervalId).toBeTruthy();
- expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL &&
- interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ expect(interval.state.intervalId).toBeTruthy();
+ expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL &&
+ interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy();
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
- expect(interval.state.intervalId).toBeTruthy();
- expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL);
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(interval.state.intervalId).toBeTruthy();
+ expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should resume when page is becomes visible at the previous interval', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- expect(interval.state.intervalId).toBeTruthy();
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
+ waitForPromises()
+ .then(() => {
+ expect(interval.state.intervalId).toBeTruthy();
- expect(interval.state.intervalId).toBeUndefined();
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } });
- // simulates triggering of visibilitychange event
- interval.handleVisibilityChange({ target: { visibilityState: 'visible' } });
+ expect(interval.state.intervalId).toBeUndefined();
- expect(interval.state.intervalId).toBeTruthy();
+ // simulates triggering of visibilitychange event
+ interval.handleVisibilityChange({ target: { visibilityState: 'visible' } });
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ expect(interval.state.intervalId).toBeTruthy();
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should cancel on page unload', function (done) {
const interval = this.smartInterval;
- setTimeout(() => {
- $(document).triggerHandler('beforeunload');
- expect(interval.state.intervalId).toBeUndefined();
- expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval);
- done();
- }, DEFAULT_SHORT_TIMEOUT);
+ jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT);
+
+ waitForPromises()
+ .then(() => {
+ $(document).triggerHandler('beforeunload');
+ expect(interval.state.intervalId).toBeUndefined();
+ expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should execute callback before first interval', function () {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 0eff98bcc9d..bc00fdfd73c 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -6,6 +6,7 @@ import '~/commons';
import Vue from 'vue';
import VueResource from 'vue-resource';
import Translate from '~/vue_shared/translate';
+import jasmineDiff from 'jasmine-diff';
import { getDefaultAdapter } from '~/lib/utils/axios_utils';
import { FIXTURES_PATH, TEST_HOST } from './test_constants';
@@ -35,7 +36,15 @@ Vue.use(Translate);
jasmine.getFixtures().fixturesPath = FIXTURES_PATH;
jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
-beforeAll(() => jasmine.addMatchers(customMatchers));
+beforeAll(() => {
+ jasmine.addMatchers(
+ jasmineDiff(jasmine, {
+ colors: true,
+ inline: true,
+ }),
+ );
+ jasmine.addMatchers(customMatchers);
+});
// globalize common libraries
window.$ = $;
@@ -166,13 +175,13 @@ if (process.env.BABEL_ENV === 'coverage') {
];
describe('Uncovered files', function() {
- const sourceFiles = require.context('~', true, /\.js$/);
+ const sourceFiles = require.context('~', true, /\.(js|vue)$/);
$.holdReady(true);
sourceFiles.keys().forEach(function(path) {
// ignore if there is a matching spec file
- if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) {
+ if (testsContext.keys().indexOf(`${path.replace(/\.(js|vue)$/, '')}_spec`) > -1) {
return;
}
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
index c82ba61a5b1..50c2b0e2bd0 100644
--- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -153,7 +153,7 @@ describe('Deployment component', () => {
it('renders external URL', () => {
expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url);
- expect(el.querySelector('.js-deploy-url').innerText).toContain(deploymentMockData.external_url_formatted);
+ expect(el.querySelector('.js-deploy-url').innerText).toContain('View app');
});
it('renders stop button', () => {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index 3d36e46d863..8ac2f26979b 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -119,6 +119,7 @@ describe('MRWidgetHeader', () => {
beforeEach(() => {
vm = mountComponent(Component, {
mr: {
+ iid: 1,
divergedCommitsCount: 12,
sourceBranch: 'mr-widget-refactor',
sourceBranchLink: '<a href="/foo/bar/mr-widget-refactor">mr-widget-refactor</a>',
@@ -130,6 +131,8 @@ describe('MRWidgetHeader', () => {
emailPatchesPath: '/mr/email-patches',
plainDiffPath: '/mr/plainDiffPath',
statusPath: 'abc',
+ sourceProjectFullPath: 'root/gitlab-ce',
+ targetProjectFullPath: 'gitlab-org/gitlab-ce',
},
});
});
@@ -145,17 +148,41 @@ describe('MRWidgetHeader', () => {
it('renders web ide button', () => {
const button = vm.$el.querySelector('.js-web-ide');
- expect(button.textContent.trim()).toEqual('Web IDE');
- expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
+ expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.getAttribute('href')).toEqual(
+ '/-/ide/project/root/gitlab-ce/merge_requests/1?target_project=gitlab-org%2Fgitlab-ce',
+ );
+ });
+
+ it('renders web ide button with blank query string if target & source project branch', done => {
+ vm.mr.targetProjectFullPath = 'root/gitlab-ce';
+
+ vm.$nextTick(() => {
+ const button = vm.$el.querySelector('.js-web-ide');
+
+ expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.getAttribute('href')).toEqual(
+ '/-/ide/project/root/gitlab-ce/merge_requests/1?target_project=',
+ );
+
+ done();
+ });
});
- it('renders web ide button with relative URL', () => {
+ it('renders web ide button with relative URL', done => {
gon.relative_url_root = '/gitlab';
+ vm.mr.iid = 2;
- const button = vm.$el.querySelector('.js-web-ide');
+ vm.$nextTick(() => {
+ const button = vm.$el.querySelector('.js-web-ide');
- expect(button.textContent.trim()).toEqual('Web IDE');
- expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
+ expect(button.textContent.trim()).toEqual('Open in Web IDE');
+ expect(button.getAttribute('href')).toEqual(
+ '/gitlab/-/ide/project/root/gitlab-ce/merge_requests/2?target_project=gitlab-org%2Fgitlab-ce',
+ );
+
+ done();
+ });
});
it('renders download dropdown with links', () => {
@@ -253,8 +280,8 @@ describe('MRWidgetHeader', () => {
});
it('renders diverged commits info', () => {
- expect(vm.$el.querySelector('.diverged-commits-count').textContent.trim()).toEqual(
- '(12 commits behind)',
+ expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch(
+ /(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/,
);
});
});
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 9d2a15ff009..c0b5a7d4455 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -29,8 +29,10 @@ export default {
source_branch: 'daaaa',
source_branch_link: 'daaaa',
source_project_id: 19,
+ source_project_full_path: '/group1/project1',
target_branch: 'master',
target_project_id: 19,
+ target_project_full_path: '/group2/project2',
metrics: {
merged_by: {
name: 'Administrator',
diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js
index 73a69df019e..65d8ed39ade 100644
--- a/spec/javascripts/vue_shared/components/memory_graph_spec.js
+++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js
@@ -113,7 +113,7 @@ describe('MemoryGraph', () => {
const circleEl = el.querySelector('circle');
expect(circleEl).toBeDefined();
expect(circleEl.getAttribute('r')).toBe('1.5');
- expect(circleEl.getAttribute('tranform')).toBe('translate(0 -1)');
+ expect(circleEl.getAttribute('transform')).toBe('translate(0 -1)');
expect(circleEl.getAttribute('cx')).toBe(`${dotX}`);
expect(circleEl.getAttribute('cy')).toBe(`${dotY}`);
done();
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index ab14d77d552..a515d07b072 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -3,17 +3,61 @@ require 'spec_helper'
describe Banzai::Filter::MarkdownFilter do
include FilterSpecHelper
- context 'code block' do
- it 'adds language to lang attribute when specified' do
- result = filter("```html\nsome code\n```")
+ describe 'markdown engine from context' do
+ it 'defaults to CommonMark' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
- expect(result).to start_with("<pre><code lang=\"html\">")
+ filter('test')
end
- it 'does not add language to lang attribute when not specified' do
- result = filter("```\nsome code\n```")
+ it 'uses Redcarpet' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::Redcarpet).to receive(:render).and_return('test')
- expect(result).to start_with("<pre><code>")
+ filter('test', { markdown_engine: :redcarpet })
+ end
+
+ it 'uses CommonMark' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
+
+ filter('test', { markdown_engine: :common_mark })
+ end
+ end
+
+ describe 'code block' do
+ context 'using CommonMark' do
+ before do
+ stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
+ end
+
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("<pre><code>")
+ end
+ end
+
+ context 'using Redcarpet' do
+ before do
+ stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :redcarpet)
+ end
+
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code>")
+ end
end
end
end
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 9a2e521fdcf..919825a6102 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -46,7 +46,7 @@ describe Banzai::Filter::RedactorFilter do
it 'allows permitted Project references' do
user = create(:user)
project = create(:project)
- project.add_master(user)
+ project.add_maintainer(user)
link = reference_link(project: project.id, reference_type: 'test')
doc = filter(link, current_user: user)
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index e13406d1972..8947e2ac4fb 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -203,4 +203,30 @@ describe ExtractsPath do
expect(extract_ref_without_atom('foo.atom')).to eq(nil)
end
end
+
+ describe '#lfs_blob_ids' do
+ shared_examples '#lfs_blob_ids' do
+ let(:tag) { @project.repository.add_tag(@project.owner, 'my-annotated-tag', 'master', 'test tag') }
+ let(:ref) { tag.target }
+ let(:params) { { ref: ref, path: 'README.md' } }
+
+ before do
+ @project = create(:project, :repository)
+ end
+
+ it 'handles annotated tags' do
+ assign_ref_vars
+
+ expect(lfs_blob_ids).to eq([])
+ end
+ end
+
+ context 'when gitaly is enabled' do
+ it_behaves_like '#lfs_blob_ids'
+ end
+
+ context 'when gitaly is disabled', :skip_gitaly_mock do
+ it_behaves_like '#lfs_blob_ids'
+ end
+ end
end
diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
index 1b3df7b20d4..64c994a268f 100644
--- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
@@ -1,31 +1,35 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180626125654 do
+describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do
describe '#perform' do
context 'when diff files can be deleted' do
let(:merge_request) { create(:merge_request, :merged) }
- let(:merge_request_diff) do
+ let!(:merge_request_diff) do
merge_request.create_merge_request_diff
merge_request.merge_request_diffs.first
end
+ let(:perform) do
+ described_class.new.perform(MergeRequestDiff.pluck(:id))
+ end
+
it 'deletes all merge request diff files' do
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to change { merge_request_diff.merge_request_diff_files.count }
.from(20).to(0)
end
it 'updates state to without_files' do
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to change { merge_request_diff.reload.state }
.from('collected').to('without_files')
end
it 'rollsback if something goes wrong' do
- expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all)
+ expect(described_class::MergeRequestDiffFile).to receive_message_chain(:where, :delete_all)
.and_raise
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to raise_error
merge_request_diff.reload
@@ -35,35 +39,35 @@ describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180
end
end
- it 'deletes no merge request diff files when MR is not merged' do
- merge_request = create(:merge_request, :opened)
- merge_request.create_merge_request_diff
- merge_request_diff = merge_request.merge_request_diffs.first
-
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
- end
-
- it 'deletes no merge request diff files when diff is marked as "without_files"' do
+ it 'reschedules itself when should_wait_deadtuple_vacuum' do
merge_request = create(:merge_request, :merged)
- merge_request.create_merge_request_diff
- merge_request_diff = merge_request.merge_request_diffs.first
+ first_diff = merge_request.merge_request_diff
+ second_diff = merge_request.create_merge_request_diff
- merge_request_diff.clean!
+ Sidekiq::Testing.fake! do
+ worker = described_class.new
+ allow(worker).to receive(:should_wait_deadtuple_vacuum?) { true }
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
+ worker.perform([first_diff.id, second_diff.id])
+
+ expect(described_class.name.demodulize).to be_scheduled_delayed_migration(5.minutes, [first_diff.id, second_diff.id])
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
end
+ end
- it 'deletes no merge request diff files when diff is the latest' do
- merge_request = create(:merge_request, :merged)
- merge_request_diff = merge_request.merge_request_diff
+ describe '#should_wait_deadtuple_vacuum?' do
+ it 'returns true when hitting merge_request_diff_files hits DEAD_TUPLES_THRESHOLD', :postgresql do
+ worker = described_class.new
+ threshold_query_result = [{ "n_dead_tup" => described_class::DEAD_TUPLES_THRESHOLD.to_s }]
+ normal_query_result = [{ "n_dead_tup" => '3' }]
+
+ allow(worker)
+ .to receive(:execute_statement)
+ .with(/SELECT n_dead_tup */)
+ .and_return(threshold_query_result, normal_query_result)
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
+ expect(worker.should_wait_deadtuple_vacuum?).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb b/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
index ee60e498b59..2e77e80ee46 100644
--- a/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::BackgroundMigration::MovePersonalSnippetFiles do
let(:snippet) do
snippet = create(:personal_snippet)
create_upload_for_snippet(snippet)
- snippet.update_attributes!(description: markdown_linking_file(snippet))
+ snippet.update!(description: markdown_linking_file(snippet))
snippet
end
diff --git a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
new file mode 100644
index 00000000000..fb5093b0bd1
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, schema: 20180619121030 do
+ describe '#perform' do
+ let(:merge_request_diffs) { table(:merge_request_diffs) }
+ let(:merge_requests) { table(:merge_requests) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+
+ before do
+ stub_const("#{described_class.name}::DIFF_BATCH_SIZE", 3)
+
+ namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab')
+ projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab')
+
+ merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged')
+
+ merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'empty')
+ merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'without_files')
+ merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 7, merge_request_id: 1, state: 'collected')
+
+ merge_requests.update(1, latest_merge_request_diff_id: 7)
+ end
+
+ it 'correctly schedules diff file deletion workers' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ described_class.new.perform
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, [1, 4, 5])
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, [6])
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 1cb8143a9e9..7c04aa27971 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'deletion' do
@@ -144,7 +144,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'if the user is allowed to delete protected branches' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'through the web interface' do
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index 6a52ae01b2f..e327399d82d 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -2,13 +2,21 @@ require 'spec_helper'
describe Gitlab::Ci::Build::Artifacts::Metadata do
def metadata(path = '', **opts)
- described_class.new(metadata_file_path, path, **opts)
+ described_class.new(metadata_file_stream, path, **opts)
end
let(:metadata_file_path) do
Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
end
+ let(:metadata_file_stream) do
+ File.open(metadata_file_path) if metadata_file_path
+ end
+
+ after do
+ metadata_file_stream&.close
+ end
+
context 'metadata file exists' do
describe '#find_entries! empty string' do
subject { metadata('').find_entries! }
@@ -86,11 +94,21 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
end
context 'metadata file does not exist' do
- let(:metadata_file_path) { '' }
+ let(:metadata_file_path) { nil }
+
+ describe '#find_entries!' do
+ it 'raises error' do
+ expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /Invalid stream/)
+ end
+ end
+ end
+
+ context 'metadata file is invalid' do
+ let(:metadata_file_path) { Rails.root + 'spec/fixtures/ci_build_artifacts.zip' }
describe '#find_entries!' do
it 'raises error' do
- expect { metadata.find_entries! }.to raise_error(Errno::ENOENT)
+ expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index a973ccda8de..8ba56d73838 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -99,9 +99,9 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { is_expected.to be_truthy }
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index e2bb378f663..02f8c4c114b 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -46,7 +46,7 @@ describe Gitlab::Ci::Status::Build::Play do
context 'when user can not push to the branch' do
before do
build.project.add_developer(user)
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: build.ref, project: project)
end
diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb
index 6ec35f8da7e..bb2d0a2c75c 100644
--- a/spec/lib/gitlab/ci/status/stage/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::Ci::Status::Stage::Common do
context 'when user has permission to read pipeline' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'has details' do
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 8d4862932b2..1f35d1e4880 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::ClosingIssueExtractor do
before do
project.add_developer(project.creator)
project.add_developer(project2.creator)
- project2.add_master(project.creator)
+ project2.add_maintainer(project.creator)
end
describe "#closed_by_message" do
@@ -298,7 +298,7 @@ describe Gitlab::ClosingIssueExtractor do
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
- jira_project.add_master(jira_project.creator)
+ jira_project.add_maintainer(jira_project.creator)
jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project)
closing_issue_extractor = described_class.new(jira_project, jira_project.creator)
message = "Resolve #{jira_issue.to_reference}"
@@ -379,6 +379,20 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue, other_issue, third_issue])
end
+ it 'allows non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference} #{reference2} #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
+ it 'allows mixed comma-separated and non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference}, #{reference2} and #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
index 6de4bd3dc7c..f670c7f6c75 100644
--- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
@@ -36,9 +36,9 @@ describe Gitlab::CycleAnalytics::Permissions do
end
end
- context 'user is master' do
+ context 'user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'has permissions to issue stage' do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index b411aaa19da..0a8c77b0ad9 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -281,7 +281,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
it "doesn't break when the namespace was renamed" do
subject.rename_namespace(namespace)
- namespace.update_attributes!(path: 'renamed-afterwards')
+ namespace.update!(path: 'renamed-afterwards')
expect { subject.revert_renames }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index b4896d69077..d4d7a83921c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -169,7 +169,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
it "doesn't break when the project was renamed" do
subject.rename_project(project)
- project.update_attributes!(path: 'renamed-afterwards')
+ project.update!(path: 'renamed-afterwards')
expect { subject.revert_renames }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 8bb246aa4bd..782e4e45a91 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -357,6 +357,35 @@ describe Gitlab::Database do
end
end
+ describe '.db_read_only?' do
+ context 'when using PostgreSQL' do
+ before do
+ allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
+ expect(described_class).to receive(:postgresql?).and_return(true)
+ end
+
+ it 'detects a read only database' do
+ allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "t" }])
+
+ expect(described_class.db_read_only?).to be_truthy
+ end
+
+ it 'detects a read write database' do
+ allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "f" }])
+
+ expect(described_class.db_read_only?).to be_falsey
+ end
+ end
+
+ context 'when using MySQL' do
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+ end
+
+ it { expect(described_class.db_read_only?).to be_falsey }
+ end
+ end
+
describe '#sanitize_timestamp' do
let(:max_timestamp) { Time.at((1 << 31) - 1) }
diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
index b5c1a360ba9..ae61ece8029 100644
--- a/spec/lib/additional_email_headers_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe AdditionalEmailHeadersInterceptor do
+describe Gitlab::Email::Hook::AdditionalHeadersInterceptor do
let(:mail) do
ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello')
end
diff --git a/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
new file mode 100644
index 00000000000..4497d4002da
--- /dev/null
+++ b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::Email::Hook::DeliveryMetricsObserver do
+ let(:email) do
+ ActionMailer::Base.mail(to: 'test@example.com',
+ from: 'info@example.com',
+ body: 'hello')
+ end
+
+ context 'when email has been delivered' do
+ it 'increments both email delivery metrics' do
+ expect(described_class.delivery_attempts_counter).to receive(:increment)
+ expect(described_class.delivered_emails_counter).to receive(:increment)
+
+ email.deliver_now
+ end
+ end
+
+ context 'when email has not been delivered due to an error' do
+ before do
+ allow(email.delivery_method).to receive(:deliver!)
+ .and_raise(StandardError, 'Some SMTP error')
+ end
+
+ it 'increments only delivery attempt metric' do
+ expect(described_class.delivery_attempts_counter)
+ .to receive(:increment)
+ expect(described_class.delivered_emails_counter)
+ .not_to receive(:increment)
+
+ expect { email.deliver_now }
+ .to raise_error(StandardError, 'Some SMTP error')
+ end
+ end
+end
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
index 3652d928c43..91aa3bc7c2e 100644
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe DisableEmailInterceptor do
+describe Gitlab::Email::Hook::DisableEmailInterceptor do
before do
Mail.register_interceptor(described_class)
end
diff --git a/spec/lib/gitlab/exclusive_lease_helpers_spec.rb b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
new file mode 100644
index 00000000000..2e3656b52fb
--- /dev/null
+++ b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state do
+ include ::ExclusiveLeaseHelpers
+
+ let(:class_instance) { (Class.new { include ::Gitlab::ExclusiveLeaseHelpers }).new }
+ let(:unique_key) { SecureRandom.hex(10) }
+
+ describe '#in_lock' do
+ subject { class_instance.in_lock(unique_key, **options) { } }
+
+ let(:options) { {} }
+
+ context 'when the lease is not obtained yet' do
+ before do
+ stub_exclusive_lease(unique_key, 'uuid')
+ end
+
+ it 'calls the given block' do
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ end
+
+ it 'calls the given block continuously' do
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ end
+
+ it 'cancels the exclusive lease after the block' do
+ expect_to_cancel_exclusive_lease(unique_key, 'uuid')
+
+ subject
+ end
+ end
+
+ context 'when the lease is obtained already' do
+ let!(:lease) { stub_exclusive_lease_taken(unique_key) }
+
+ it 'retries to obtain a lease and raises an error' do
+ expect(lease).to receive(:try_obtain).exactly(11).times
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+
+ context 'when ttl is specified' do
+ let(:options) { { ttl: 10.minutes } }
+
+ it 'receives the specified argument' do
+ expect(Gitlab::ExclusiveLease).to receive(:new).with(unique_key, { timeout: 10.minutes } )
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when retry count is specified' do
+ let(:options) { { retries: 3 } }
+
+ it 'retries for the specified times' do
+ expect(lease).to receive(:try_obtain).exactly(4).times
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when sleep second is specified' do
+ let(:options) { { retries: 0, sleep_sec: 0.05.seconds } }
+
+ it 'receives the specified argument' do
+ expect(class_instance).to receive(:sleep).with(0.05.seconds).once
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index b6061df349d..034b89a46fa 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -178,77 +178,67 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
describe '.batch' do
- shared_examples 'loading blobs in batch' do
- let(:blob_references) do
- [
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
- ]
- end
+ let(:blob_references) do
+ [
+ [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
+ [SeedRepo::Commit::ID, 'six']
+ ]
+ end
- subject { described_class.batch(repository, blob_references) }
+ subject { described_class.batch(repository, blob_references) }
- it { expect(subject.size).to eq(blob_references.size) }
+ it { expect(subject.size).to eq(blob_references.size) }
- context 'first blob' do
- let(:blob) { subject[0] }
+ context 'first blob' do
+ let(:blob) { subject[0] }
- it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
- it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
- it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
- it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
- it { expect(blob.size).to eq(669) }
- it { expect(blob.mode).to eq("100644") }
- end
+ it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
+ it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
+ it { expect(blob.path).to eq("files/ruby/popen.rb") }
+ it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
+ it { expect(blob.size).to eq(669) }
+ it { expect(blob.mode).to eq("100644") }
+ end
- context 'second blob' do
- let(:blob) { subject[1] }
+ context 'second blob' do
+ let(:blob) { subject[1] }
- it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
- it { expect(blob.data).to eq('') }
- it 'does not mark the blob as binary' do
- expect(blob).not_to be_binary
- end
+ it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
+ it { expect(blob.data).to eq('') }
+ it 'does not mark the blob as binary' do
+ expect(blob).not_to be_binary
end
+ end
- context 'limiting' do
- subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
+ context 'limiting' do
+ subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
- context 'positive' do
- let(:blob_size_limit) { 10 }
+ context 'positive' do
+ let(:blob_size_limit) { 10 }
- it { expect(subject.first.data.size).to eq(10) }
- end
+ it { expect(subject.first.data.size).to eq(10) }
+ end
- context 'zero' do
- let(:blob_size_limit) { 0 }
+ context 'zero' do
+ let(:blob_size_limit) { 0 }
- it 'only loads the metadata' do
- expect(subject.first.size).not_to be(0)
- expect(subject.first.data).to eq('')
- end
+ it 'only loads the metadata' do
+ expect(subject.first.size).not_to be(0)
+ expect(subject.first.data).to eq('')
end
+ end
- context 'negative' do
- let(:blob_size_limit) { -1 }
+ context 'negative' do
+ let(:blob_size_limit) { -1 }
- it 'ignores MAX_DATA_DISPLAY_SIZE' do
- stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
+ it 'ignores MAX_DATA_DISPLAY_SIZE' do
+ stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
- expect(subject.first.data.size).to eq(669)
- end
+ expect(subject.first.data.size).to eq(669)
end
end
end
-
- context 'when Gitaly list_blobs_by_sha_path feature is enabled' do
- it_behaves_like 'loading blobs in batch'
- end
-
- context 'when Gitaly list_blobs_by_sha_path feature is disabled', :disable_gitaly do
- it_behaves_like 'loading blobs in batch'
- end
end
describe '.batch_metadata' do
@@ -294,58 +284,48 @@ describe Gitlab::Git::Blob, seed_helper: true do
)
end
- shared_examples 'fetching batch of LFS pointers' do
- it 'returns a list of Gitlab::Git::Blob' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
-
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- expect(blobs).to be_an(Array)
- end
-
- it 'accepts blob IDs as a lazy enumerator' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
-
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- end
+ it 'returns a list of Gitlab::Git::Blob' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
- it 'handles empty list of IDs gracefully' do
- blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
- blobs_2 = described_class.batch_lfs_pointers(repository, [])
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ expect(blobs).to be_an(Array)
+ end
- expect(blobs_1).to eq([])
- expect(blobs_2).to eq([])
- end
+ it 'accepts blob IDs as a lazy enumerator' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
- expect(blobs).to eq([])
- end
+ it 'handles empty list of IDs gracefully' do
+ blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
+ blobs_2 = described_class.batch_lfs_pointers(repository, [])
- it 'silently ignores non lfs objects' do
- blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ expect(blobs_1).to eq([])
+ expect(blobs_2).to eq([])
+ end
- expect(blobs).to eq([])
- end
+ it 'silently ignores tree objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
- it 'avoids loading large blobs into memory' do
- # This line could call `lookup` on `repository`, so do here before mocking.
- non_lfs_blob_id = non_lfs_blob.id
+ expect(blobs).to eq([])
+ end
- expect(repository).not_to receive(:lookup)
+ it 'silently ignores non lfs objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
- described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
- end
+ expect(blobs).to eq([])
end
- context 'when Gitaly batch_lfs_pointers is enabled' do
- it_behaves_like 'fetching batch of LFS pointers'
- end
+ it 'avoids loading large blobs into memory' do
+ # This line could call `lookup` on `repository`, so do here before mocking.
+ non_lfs_blob_id = non_lfs_blob.id
+
+ expect(repository).not_to receive(:lookup)
- context 'when Gitaly batch_lfs_pointers is disabled', :disable_gitaly do
- it_behaves_like 'fetching batch of LFS pointers'
+ described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
end
end
@@ -532,8 +512,8 @@ describe Gitlab::Git::Blob, seed_helper: true do
subject { blob.load_all_data!(repository) }
it 'loads missing data' do
- expect(Gitlab::GitalyClient).to receive(:migrate)
- .with(:git_blob_load_all_data).and_return(full_data)
+ expect(repository.gitaly_blob_client).to receive(:get_blob)
+ .and_return(double(:response, data: full_data))
subject
@@ -544,8 +524,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
let(:blob) { Gitlab::Git::Blob.new(name: 'test', size: 4, data: full_data) }
it "doesn't perform any loading" do
- expect(Gitlab::GitalyClient).not_to receive(:migrate)
- .with(:git_blob_load_all_data)
+ expect(repository.gitaly_blob_client).not_to receive(:get_blob)
subject
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index ec1a684cfbc..a8c5627e678 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -2,6 +2,11 @@ require "spec_helper"
describe Gitlab::Git::Branch, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged
+ end
+ end
subject { repository.branches }
@@ -124,6 +129,7 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
def create_commit
- repository.create_commit(params.merge(committer: committer.merge(time: Time.now)))
+ params[:message].delete!("\r")
+ Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now)))
end
end
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 3bb0b5be15b..11ab376ab8f 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -27,6 +27,7 @@ EOT
too_large: false
}
+ # TODO use a Gitaly diff object instead
@rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
[".gitmodules"]).patches.first
@@ -266,8 +267,12 @@ EOT
describe '#submodule?' do
before do
- commit = repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- @diffs = commit.parents[0].diff(commit).patches
+ # TODO use a Gitaly diff object instead
+ rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.rev_parse('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ end
+
+ @diffs = rugged_commit.parents[0].diff(rugged_commit).patches
end
it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) }
diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb
index 16e6bd35449..e51b875be11 100644
--- a/spec/lib/gitlab/git/index_spec.rb
+++ b/spec/lib/gitlab/git/index_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Git::Index, seed_helper: true do
let(:index) { described_class.new(repository) }
before do
- index.read_tree(repository.lookup('master').tree)
+ index.read_tree(lookup('master').tree)
end
around do |example|
@@ -30,7 +30,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
end
@@ -54,7 +54,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
+ expect(lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
end
end
@@ -68,7 +68,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld")
+ expect(lookup(entry[:oid]).content).to eq("Hello,\nWorld")
end
end
end
@@ -135,7 +135,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -190,7 +190,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -232,4 +232,8 @@ describe Gitlab::Git::Index, seed_helper: true do
end
end
end
+
+ def lookup(revision)
+ repository.rugged.rev_parse(revision)
+ end
end
diff --git a/spec/lib/gitlab/git/popen_spec.rb b/spec/lib/gitlab/git/popen_spec.rb
index b033ede9062..074e66d2a5d 100644
--- a/spec/lib/gitlab/git/popen_spec.rb
+++ b/spec/lib/gitlab/git/popen_spec.rb
@@ -2,6 +2,9 @@ require 'spec_helper'
describe 'Gitlab::Git::Popen' do
let(:path) { Rails.root.join('tmp').to_s }
+ let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
+ # The pipe buffer is typically 64K. This string is about 440K.
+ let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
let(:klass) do
Class.new(Object) do
@@ -70,6 +73,15 @@ describe 'Gitlab::Git::Popen' do
end
end
end
+
+ context 'with a process that writes a lot of data to stderr' do
+ it 'returns zero' do
+ output, status = klass.new.popen(spew_command, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
+ end
end
context 'popen_with_timeout' do
@@ -85,6 +97,17 @@ describe 'Gitlab::Git::Popen' do
it { expect(output).to include('tests') }
end
+ context 'multi-line string' do
+ let(:test_string) { "this is 1 line\n2nd line\n3rd line\n" }
+ let(:result) { klass.new.popen_with_timeout(['echo', test_string], timeout, path) }
+ let(:output) { result.first }
+ let(:status) { result.last }
+
+ it { expect(status).to be_zero }
+ # echo adds its own line
+ it { expect(output).to eq(test_string + "\n") }
+ end
+
context 'non-zero status' do
let(:result) { klass.new.popen_with_timeout(%w(cat NOTHING), timeout, path) }
let(:output) { result.first }
@@ -110,6 +133,13 @@ describe 'Gitlab::Git::Popen' do
it "handles processes that do not shutdown correctly" do
expect { klass.new.popen_with_timeout(['bash', '-c', "trap -- '' SIGTERM; sleep 1000"], timeout, path) }.to raise_error(Timeout::Error)
end
+
+ it 'handles process that writes a lot of data to stderr' do
+ output, status = klass.new.popen_with_timeout(spew_command, timeout, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
end
context 'timeout period' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 5dd7af3a552..6480f6c407d 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -321,90 +321,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context '#submodules' do
- around do |example|
- # TODO #submodules will be removed, has been migrated to gitaly
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
-
- context 'where repo has submodules' do
- let(:submodules) { repository.send(:submodules, 'master') }
- let(:submodule) { submodules.first }
-
- it { expect(submodules).to be_kind_of Hash }
- it { expect(submodules.empty?).to be_falsey }
-
- it 'should have valid data' do
- expect(submodule).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should handle nested submodules correctly' do
- nested = submodules['nested/six']
- expect(nested['name']).to eq('nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should handle deeply nested submodules correctly' do
- nested = submodules['deeper/nested/six']
- expect(nested['name']).to eq('deeper/nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should not have an entry for an invalid submodule' do
- expect(submodules).not_to have_key('invalid/path')
- end
-
- it 'should not have an entry for an uncommited submodule dir' do
- submodules = repository.send(:submodules, 'fix-existing-submodule-dir')
- expect(submodules).not_to have_key('submodule-existing-dir')
- end
-
- it 'should handle tags correctly' do
- submodules = repository.send(:submodules, 'v1.2.1')
-
- expect(submodules.first).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should not break on invalid syntax' do
- allow(repository).to receive(:blob_content).and_return(<<-GITMODULES.strip_heredoc)
- [submodule "six"]
- path = six
- url = git://github.com/randx/six.git
-
- [submodule]
- foo = bar
- GITMODULES
-
- expect(submodules).to have_key('six')
- end
- end
-
- context 'where repo doesn\'t have submodules' do
- let(:submodules) { repository.send(:submodules, '6d39438') }
- it 'should return an empty hash' do
- expect(submodules).to be_empty
- end
- end
- end
-
describe '#commit_count' do
shared_examples 'simple commit counting' do
it { expect(repository.commit_count("master")).to eq(25) }
@@ -611,21 +527,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe "#remove_remote" do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.remove_remote("expendable")
- end
-
- it "should remove the remote" do
- expect(@repo.rugged.remotes).not_to include("expendable")
- end
-
- after(:all) do
- ensure_seeds
- end
- end
-
describe "#remote_update" do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -633,7 +534,9 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it "should add the remote" do
- expect(@repo.rugged.remotes["expendable"].url).to(
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access { @repo.rugged }
+
+ expect(rugged.remotes["expendable"].url).to(
eq(TEST_NORMAL_REPO_PATH)
)
end
@@ -1157,6 +1060,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = true
end
+ around do |example|
+ # OK because autocrlf is only used in gitaly-ruby
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
it 'return the value of the autocrlf option' do
expect(@repo.autocrlf).to be(true)
end
@@ -1172,6 +1082,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = false
end
+ around do |example|
+ # OK because autocrlf= is only used in gitaly-ruby
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
it 'should set the autocrlf option to the provided option' do
@repo.autocrlf = :input
@@ -1186,50 +1103,17 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#find_branch' do
- shared_examples 'finding a branch' do
- it 'should return a Branch for master' do
- branch = repository.find_branch('master')
+ it 'should return a Branch for master' do
+ branch = repository.find_branch('master')
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
-
- it 'should handle non-existent branch' do
- branch = repository.find_branch('this-is-garbage')
-
- expect(branch).to eq(nil)
- end
+ expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
+ expect(branch.name).to eq('master')
end
- context 'when Gitaly find_branch feature is enabled' do
- it_behaves_like 'finding a branch'
- end
-
- context 'when Gitaly find_branch feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'finding a branch'
-
- context 'force_reload is true' do
- it 'should reload Rugged::Repository' do
- expect(Rugged::Repository).to receive(:new).twice.and_call_original
-
- repository.find_branch('master')
- branch = repository.find_branch('master', force_reload: true)
+ it 'should handle non-existent branch' do
+ branch = repository.find_branch('this-is-garbage')
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
- end
-
- context 'force_reload is false' do
- it 'should not reload Rugged::Repository' do
- expect(Rugged::Repository).to receive(:new).once.and_call_original
-
- branch = repository.find_branch('master', force_reload: false)
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
- end
+ expect(branch).to eq(nil)
end
end
@@ -1716,59 +1600,51 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#fetch_source_branch!' do
- shared_examples '#fetch_source_branch!' do
- let(:local_ref) { 'refs/merge-requests/1/head' }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
-
- after do
- ensure_seeds
- end
+ let(:local_ref) { 'refs/merge-requests/1/head' }
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
- context 'when the branch exists' do
- context 'when the commit does not exist locally' do
- let(:source_branch) { 'new-branch-for-fetch-source-branch' }
- let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
- let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
+ after do
+ ensure_seeds
+ end
- before do
- source_rugged.branches.create(source_branch, new_oid)
- end
+ context 'when the branch exists' do
+ context 'when the commit does not exist locally' do
+ let(:source_branch) { 'new-branch-for-fetch-source-branch' }
+ let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
+ let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
- it 'writes the ref' do
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(new_oid)
- end
+ before do
+ source_rugged.branches.create(source_branch, new_oid)
end
- context 'when the commit exists locally' do
- let(:source_branch) { 'master' }
- let(:expected_oid) { SeedRepo::LastCommit::ID }
-
- it 'writes the ref' do
- # Sanity check: the commit should already exist
- expect(repository.commit(expected_oid)).not_to be_nil
-
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(expected_oid)
- end
+ it 'writes the ref' do
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
+ expect(repository.commit(local_ref).sha).to eq(new_oid)
end
end
- context 'when the branch does not exist' do
- let(:source_branch) { 'definitely-not-master' }
+ context 'when the commit exists locally' do
+ let(:source_branch) { 'master' }
+ let(:expected_oid) { SeedRepo::LastCommit::ID }
+
+ it 'writes the ref' do
+ # Sanity check: the commit should already exist
+ expect(repository.commit(expected_oid)).not_to be_nil
- it 'does not write the ref' do
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(false)
- expect(repository.commit(local_ref)).to be_nil
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
+ expect(repository.commit(local_ref).sha).to eq(expected_oid)
end
end
end
- it_behaves_like '#fetch_source_branch!'
+ context 'when the branch does not exist' do
+ let(:source_branch) { 'definitely-not-master' }
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#fetch_source_branch!'
+ it 'does not write the ref' do
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(false)
+ expect(repository.commit(local_ref)).to be_nil
+ end
end
end
@@ -1856,6 +1732,54 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#set_config' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:rugged) { repository_rugged }
+ let(:entries) do
+ {
+ 'test.foo1' => 'bla bla',
+ 'test.foo2' => 1234,
+ 'test.foo3' => true
+ }
+ end
+
+ it 'can set config settings' do
+ expect(repository.set_config(entries)).to be_nil
+
+ expect(rugged.config['test.foo1']).to eq('bla bla')
+ expect(rugged.config['test.foo2']).to eq('1234')
+ expect(rugged.config['test.foo3']).to eq('true')
+ end
+
+ after do
+ entries.keys.each { |k| rugged.config.delete(k) }
+ end
+ end
+
+ describe '#delete_config' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:rugged) { repository_rugged }
+ let(:entries) do
+ {
+ 'test.foo1' => 'bla bla',
+ 'test.foo2' => 1234,
+ 'test.foo3' => true
+ }
+ end
+
+ it 'can delete config settings' do
+ entries.each do |key, value|
+ rugged.config[key] = value
+ end
+
+ expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil
+
+ config_keys = rugged.config.each_key.to_a
+ expect(config_keys).not_to include('test.foo1')
+ expect(config_keys).not_to include('test.foo2')
+ end
+ end
+
describe '#merge' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -2002,54 +1926,61 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
end
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
+ end
let(:remote_name) { 'my-remote' }
+ let(:url) { 'http://my-repo.git' }
after do
ensure_seeds
end
describe '#add_remote' do
- let(:url) { 'http://my-repo.git' }
let(:mirror_refmap) { '+refs/*:refs/*' }
- it 'creates a new remote via Gitaly' do
- expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
- .to receive(:add_remote).with(remote_name, url, mirror_refmap)
+ shared_examples 'add_remote' do
+ it 'added the remote' do
+ begin
+ rugged.remotes.delete(remote_name)
+ rescue Rugged::ConfigError
+ end
+
+ repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
+ expect(rugged.remotes[remote_name]).not_to be_nil
+ expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true')
+ expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true')
+ expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap)
+ end
end
- context 'with Gitaly disabled', :skip_gitaly_mock do
- it 'creates a new remote via Rugged' do
- expect_any_instance_of(Rugged::RemoteCollection).to receive(:create)
- .with(remote_name, url)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.mirror", true)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.prune", true)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.fetch", mirror_refmap)
+ context 'using Gitaly' do
+ it_behaves_like 'add_remote'
+ end
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- end
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'add_remote'
end
end
describe '#remove_remote' do
- it 'removes the remote via Gitaly' do
- expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
- .to receive(:remove_remote).with(remote_name)
+ shared_examples 'remove_remote' do
+ it 'removes the remote' do
+ rugged.remotes.create(remote_name, url)
+
+ repository.remove_remote(remote_name)
- repository.remove_remote(remote_name)
+ expect(rugged.remotes[remote_name]).to be_nil
+ end
end
- context 'with Gitaly disabled', :skip_gitaly_mock do
- it 'removes the remote via Rugged' do
- expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete)
- .with(remote_name)
+ context 'using Gitaly' do
+ it_behaves_like 'remove_remote'
+ end
- repository.remove_remote(remote_name)
- end
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'remove_remote'
end
end
end
@@ -2241,20 +2172,25 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') }
it 'cleans up the files' do
- repository.with_worktree(worktree_path, 'master', env: ENV) do
- FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
- # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
- # but the HEAD must be 40 characters long or git will ignore it.
- File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
+ create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master]
+ raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
- # git 2.16 fails with "fatal: bad object HEAD"
- expect { repository.rev_list(including: :all) }.to raise_error(Gitlab::Git::Repository::GitError)
+ FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
+ # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
+ # but the HEAD must be 40 characters long or git will ignore it.
+ File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
- repository.clean_stale_repository_files
+ # git 2.16 fails with "fatal: bad object HEAD"
+ expect(rev_list_all).to be false
- expect { repository.rev_list(including: :all) }.not_to raise_error
- expect(File.exist?(worktree_path)).to be_falsey
- end
+ repository.clean_stale_repository_files
+
+ expect(rev_list_all).to be true
+ expect(File.exist?(worktree_path)).to be_falsey
+ end
+
+ def rev_list_all
+ system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
end
it 'increments a counter upon an error' do
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
index b752c3e8341..d9d405e1ccc 100644
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ b/spec/lib/gitlab/git/rev_list_spec.rb
@@ -32,65 +32,4 @@ describe Gitlab::Git::RevList do
expect(rev_list.new_refs).to eq(%w[sha1 sha2])
end
end
-
- context '#new_objects' do
- it 'fetches list of newly pushed objects using rev-list' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(&b) }.to yield_with_args(%w[sha1 sha2])
- end
-
- it 'can skip pathless objects' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file")
-
- expect { |b| rev_list.new_objects(require_path: true, &b) }.to yield_with_args(%w[sha2])
- end
-
- it 'can handle non utf-8 paths' do
- non_utf_char = [0x89].pack("c*").force_encoding("UTF-8")
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha2 πå†h/†ø/ƒîlé#{non_utf_char}\nsha1")
-
- rev_list.new_objects(require_path: true) do |object_ids|
- expect(object_ids.force).to eq(%w[sha2])
- end
- end
-
- it 'can yield a lazy enumerator' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- rev_list.new_objects do |object_ids|
- expect(object_ids).to be_a Enumerator::Lazy
- end
- end
-
- it 'returns the result of the block when given' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- objects = rev_list.new_objects do |object_ids|
- object_ids.first
- end
-
- expect(objects).to eq 'sha1'
- end
-
- it 'can accept list of references to exclude' do
- stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(not_in: ['master'], &b) }.to yield_with_args(%w[sha1 sha2])
- end
-
- it 'handles empty list of references to exclude as listing all known objects' do
- stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(not_in: [], &b) }.to yield_with_args(%w[sha1 sha2])
- end
- end
-
- context '#all_objects' do
- it 'fetches list of all pushed objects using rev-list' do
- stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2])
- end
- end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index ff32025253a..dbd64c4bec0 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -13,14 +13,6 @@ describe Gitlab::GitAccess do
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
let(:auth_result_type) { nil }
-
- let(:access) do
- described_class.new(actor, project,
- protocol, authentication_abilities: authentication_abilities,
- namespace_path: namespace_path, project_path: project_path,
- redirected_path: redirected_path, auth_result_type: auth_result_type)
- end
-
let(:changes) { '_any' }
let(:push_access_check) { access.check('git-receive-pack', changes) }
let(:pull_access_check) { access.check('git-upload-pack', changes) }
@@ -48,7 +40,7 @@ describe Gitlab::GitAccess do
before do
disable_protocol('http')
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'blocks http push and pull' do
@@ -113,7 +105,7 @@ describe Gitlab::GitAccess do
context 'when actor is a User' do
context 'when the User can read the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'allows push and pull access' do
@@ -254,7 +246,7 @@ describe Gitlab::GitAccess do
shared_examples '#check with a key that is not valid' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'key is too small' do
@@ -307,7 +299,7 @@ describe Gitlab::GitAccess do
describe '#add_project_moved_message!', :clean_gitlab_redis_shared_state do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when a redirect was not followed to find the project' do
@@ -335,7 +327,7 @@ describe Gitlab::GitAccess do
describe '#check_authentication_abilities!' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when download' do
@@ -381,7 +373,7 @@ describe Gitlab::GitAccess do
describe '#check_command_disabled!' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'over http' do
@@ -529,8 +521,8 @@ describe Gitlab::GitAccess do
end
describe '#check_download_access!' do
- it 'allows masters to pull' do
- project.add_master(user)
+ it 'allows maintainers to pull' do
+ project.add_maintainer(user)
expect { pull_access_check }.not_to raise_error
end
@@ -542,7 +534,7 @@ describe Gitlab::GitAccess do
end
it 'disallows blocked users to pull' do
- project.add_master(user)
+ project.add_maintainer(user)
user.block
expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.')
@@ -724,10 +716,11 @@ describe Gitlab::GitAccess do
end
describe '#check_push_access!' do
+ let(:unprotected_branch) { 'unprotected_branch' }
+
before do
merge_into_protected_branch
end
- let(:unprotected_branch) { 'unprotected_branch' }
let(:changes) do
{ push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
@@ -741,26 +734,18 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" }
end
- def stub_git_hooks
- # Running the `pre-receive` hook is expensive, and not necessary for this test.
- allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) do |service, &block|
- block.call(service)
- end
- end
-
def merge_into_protected_branch
@protected_branch_merge_commit ||= begin
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- stub_git_hooks
project.repository.add_branch(user, unprotected_branch, 'feature')
- target_branch = project.repository.lookup('feature')
+ rugged = project.repository.rugged
+ target_branch = rugged.rev_parse('feature')
source_branch = project.repository.create_file(
user,
'filename',
'This is the file content',
message: 'This is a good commit message',
branch_name: unprotected_branch)
- rugged = project.repository.rugged
author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
merge_index = rugged.merge_commits(target_branch, source_branch)
@@ -785,7 +770,7 @@ describe Gitlab::GitAccess do
aggregate_failures do
matrix.each do |action, allowed|
- check = -> { access.send(:check_push_access!, changes[action]) }
+ check = -> { push_changes(changes[action]) }
if allowed
expect(&check).not_to raise_error,
@@ -812,7 +797,7 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: true
},
- master: {
+ maintainer: {
push_new_branch: true,
push_master: true,
push_protected_branch: true,
@@ -917,7 +902,7 @@ describe Gitlab::GitAccess do
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
- master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
+ maintainer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }))
end
end
@@ -989,7 +974,7 @@ describe Gitlab::GitAccess do
let(:project) { create(:project, :repository, :read_only) }
it 'denies push access' do
- project.add_master(user)
+ project.add_maintainer(user)
expect { push_access_check }.to raise_unauthorized('The repository is temporarily read-only. Please try again later.')
end
@@ -1119,9 +1104,9 @@ describe Gitlab::GitAccess do
it_behaves_like 'access after accepting terms'
end
- describe 'as a master of the project' do
+ describe 'as a maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'access after accepting terms'
@@ -1152,6 +1137,17 @@ describe Gitlab::GitAccess do
private
+ def access
+ described_class.new(actor, project, protocol,
+ authentication_abilities: authentication_abilities,
+ namespace_path: namespace_path, project_path: project_path,
+ redirected_path: redirected_path, auth_result_type: auth_result_type)
+ end
+
+ def push_changes(changes)
+ access.check('git-receive-pack', changes)
+ end
+
def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 9709f1f5646..031d1e87dc1 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -53,6 +53,47 @@ describe Gitlab::GitalyClient::OperationService do
end
end
+ describe '#user_update_branch' do
+ let(:branch_name) { 'my-branch' }
+ let(:newrev) { '01e' }
+ let(:oldrev) { '01d' }
+ let(:request) do
+ Gitaly::UserUpdateBranchRequest.new(
+ repository: repository.gitaly_repository,
+ branch_name: branch_name,
+ newrev: newrev,
+ oldrev: oldrev,
+ user: gitaly_user
+ )
+ end
+ let(:response) { Gitaly::UserUpdateBranchResponse.new }
+
+ subject { client.user_update_branch(branch_name, user, newrev, oldrev) }
+
+ it 'sends a user_update_branch message' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_update_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ subject
+ end
+
+ context "when pre_receive_error is present" do
+ let(:response) do
+ Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "something failed")
+ end
+
+ it "throws a PreReceive exception" do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_update_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ expect { subject }.to raise_error(
+ Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+ end
+
describe '#user_delete_branch' do
let(:branch_name) { 'my-branch' }
let(:request) do
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
index 51fad6c6838..b2e544e6fed 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
@@ -27,9 +27,9 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
milestone: double(:milestone, number: 4),
user: double(:user, id: 4, login: 'alice'),
assignee: double(:user, id: 4, login: 'alice'),
- created_at: Time.zone.now,
- updated_at: Time.zone.now,
- merged_at: Time.zone.now
+ created_at: 1.second.ago,
+ updated_at: 1.second.ago,
+ merged_at: 1.second.ago
)
end
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 017facd0f5e..031f57dbc65 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject { described_class.new(project) }
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
project.create_import_data(data: import_data)
end
diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
index 6fbffc38444..1a2c6ef25c4 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -141,7 +141,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
expect(invalid_gpg_signature.reload.verification_status).to eq 'unverified_key'
# InvalidGpgSignatureUpdater is called by the after_update hook
- user.update_attributes!(email: GpgHelpers::User1.emails.first)
+ user.update!(email: GpgHelpers::User1.emails.first)
expect(invalid_gpg_signature.reload).to have_attributes(
project: project,
@@ -166,7 +166,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
)
# InvalidGpgSignatureUpdater is called by the after_update hook
- user.update_attributes!(email: 'still.unrelated@example.com')
+ user.update!(email: 'still.unrelated@example.com')
expect(invalid_gpg_signature.reload).to have_attributes(
project: project,
diff --git a/spec/lib/gitlab/ci/trace/http_io_spec.rb b/spec/lib/gitlab/http_io_spec.rb
index 5474e2f518c..788bddb8f59 100644
--- a/spec/lib/gitlab/ci/trace/http_io_spec.rb
+++ b/spec/lib/gitlab/http_io_spec.rb
@@ -1,11 +1,14 @@
require 'spec_helper'
-describe Gitlab::Ci::Trace::HttpIO do
+describe Gitlab::HttpIO do
include HttpIOHelpers
let(:http_io) { described_class.new(url, size) }
- let(:url) { remote_trace_url }
- let(:size) { remote_trace_size }
+
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
+ let(:file_body) { File.read(file_path).force_encoding(Encoding::BINARY) }
+ let(:size) { File.size(file_path) }
describe '#close' do
subject { http_io.close }
@@ -86,10 +89,10 @@ describe Gitlab::Ci::Trace::HttpIO do
describe '#each_line' do
subject { http_io.each_line }
- let(:string_io) { StringIO.new(remote_trace_body) }
+ let(:string_io) { StringIO.new(file_body) }
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
end
it 'yields lines' do
@@ -99,7 +102,7 @@ describe Gitlab::Ci::Trace::HttpIO do
context 'when buckets on GCS' do
context 'when BUFFER_SIZE is larger than file size' do
before do
- stub_remote_trace_200
+ stub_remote_url_200(url, file_path)
set_larger_buffer_size_than(size)
end
@@ -117,7 +120,7 @@ describe Gitlab::Ci::Trace::HttpIO do
context 'when there are no network issue' do
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
end
context 'when read whole size' do
@@ -129,7 +132,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
@@ -139,7 +142,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
end
@@ -153,7 +156,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body[0, length])
+ is_expected.to eq(file_body[0, length])
end
end
@@ -163,7 +166,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body[0, length])
+ is_expected.to eq(file_body[0, length])
end
end
end
@@ -177,7 +180,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
@@ -187,7 +190,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
end
@@ -221,11 +224,11 @@ describe Gitlab::Ci::Trace::HttpIO do
let(:length) { nil }
before do
- stub_remote_trace_500
+ stub_remote_url_500(url)
end
it 'reads a trace' do
- expect { subject }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
+ expect { subject }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
end
end
end
@@ -233,15 +236,15 @@ describe Gitlab::Ci::Trace::HttpIO do
describe '#readline' do
subject { http_io.readline }
- let(:string_io) { StringIO.new(remote_trace_body) }
+ let(:string_io) { StringIO.new(file_body) }
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
end
shared_examples 'all line matching' do
it 'reads a line' do
- (0...remote_trace_body.lines.count).each do
+ (0...file_body.lines.count).each do
expect(http_io.readline).to eq(string_io.readline)
end
end
@@ -251,11 +254,11 @@ describe Gitlab::Ci::Trace::HttpIO do
let(:length) { nil }
before do
- stub_remote_trace_500
+ stub_remote_url_500(url)
end
it 'reads a trace' do
- expect { subject }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
+ expect { subject }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
end
end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
new file mode 100644
index 00000000000..5059d68e54b
--- /dev/null
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
+ let!(:service) { described_class.new }
+ let!(:project) { create(:project, :with_object_export) }
+ let(:shared) { project.import_export_shared }
+ let!(:user) { create(:user) }
+
+ describe '#execute' do
+ before do
+ allow(service).to receive(:strategy_execute)
+ stub_feature_flags(import_export_object_storage: true)
+ end
+
+ it 'returns if project exported file is not found' do
+ allow(project).to receive(:export_project_object_exists?).and_return(false)
+
+ expect(service).not_to receive(:strategy_execute)
+
+ service.execute(user, project)
+ end
+
+ it 'creates a lock file in the export dir' do
+ allow(service).to receive(:delete_after_export_lock)
+
+ service.execute(user, project)
+
+ expect(lock_path_exist?).to be_truthy
+ end
+
+ context 'when the method succeeds' do
+ it 'removes the lock file' do
+ service.execute(user, project)
+
+ expect(lock_path_exist?).to be_falsey
+ end
+ end
+
+ context 'when the method fails' do
+ before do
+ allow(service).to receive(:strategy_execute).and_call_original
+ end
+
+ context 'when validation fails' do
+ before do
+ allow(service).to receive(:invalid?).and_return(true)
+ end
+
+ it 'does not create the lock file' do
+ expect(service).not_to receive(:create_or_update_after_export_lock)
+
+ service.execute(user, project)
+ end
+
+ it 'does not execute main logic' do
+ expect(service).not_to receive(:strategy_execute)
+
+ service.execute(user, project)
+ end
+
+ it 'logs validation errors in shared context' do
+ expect(service).to receive(:log_validation_errors)
+
+ service.execute(user, project)
+ end
+ end
+
+ context 'when an exception is raised' do
+ it 'removes the lock' do
+ expect { service.execute(user, project) }.to raise_error(NotImplementedError)
+
+ expect(lock_path_exist?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe '#log_validation_errors' do
+ it 'add the message to the shared context' do
+ errors = %w(test_message test_message2)
+
+ allow(service).to receive(:invalid?).and_return(true)
+ allow(service.errors).to receive(:full_messages).and_return(errors)
+
+ expect(shared).to receive(:add_error_message).twice.and_call_original
+
+ service.execute(user, project)
+
+ expect(shared.errors).to eq errors
+ end
+ end
+
+ describe '#to_json' do
+ it 'adds the current strategy class to the serialized attributes' do
+ params = { param1: 1 }
+ result = params.merge(klass: described_class.to_s).to_json
+
+ expect(described_class.new(params).to_json).to eq result
+ end
+ end
+
+ def lock_path_exist?
+ File.exist?(described_class.lock_file_path(project))
+ end
+end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
index ed54d87de4a..566b7f46c87 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
describe '#execute' do
before do
allow(service).to receive(:strategy_execute)
+ stub_feature_flags(import_export_object_storage: false)
end
it 'returns if project exported file is not found' do
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index 5fe57d9987b..7f2e0a4ee2c 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -24,13 +24,34 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
end
describe '#execute' do
- it 'removes the exported project file after the upload' do
- allow(strategy).to receive(:send_file)
- allow(strategy).to receive(:handle_response_error)
+ context 'without object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
+
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
+
+ expect(project).to receive(:remove_exported_project_file)
+
+ strategy.execute(user, project)
+ end
+ end
+
+ context 'with object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ end
- expect(project).to receive(:remove_exported_project_file)
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
- strategy.execute(user, project)
+ expect(project).to receive(:remove_exported_project_file)
+
+ strategy.execute(user, project)
+ end
end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 2ea66479c1b..084ce3066d6 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -293,6 +293,7 @@ project:
- deploy_tokens
- settings
- ci_cd_settings
+- import_export_upload
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 2223f163177..90e6d653d34 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AvatarSaver do
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index 246f009ad27..67e4c289906 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -111,7 +111,7 @@ describe Gitlab::ImportExport::MembersMapper do
end
it 'maps the project member if it already exists' do
- project.add_master(user2)
+ project.add_maintainer(user2)
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 2b8a11ce8f9..fec8a2af9ab 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
let!(:project) { setup_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow_any_instance_of(MergeRequest).to receive(:source_branch_sha).and_return('ABCD')
allow_any_instance_of(MergeRequest).to receive(:target_branch_sha).and_return('DCBA')
@@ -217,8 +217,8 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(member_emails).not_to include('group@member.com')
end
- it 'does not export group members as master' do
- Group.first.add_master(user)
+ it 'does not export group members as maintainer' do
+ Group.first.add_maintainer(user)
expect(member_emails).not_to include('group@member.com')
end
diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index 187ec8fcfa2..5a646b4aac8 100644
--- a/spec/lib/gitlab/import_export/repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RepoSaver do
let(:bundler) { described_class.new(project: project, shared: shared) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
new file mode 100644
index 00000000000..02f1a4b81aa
--- /dev/null
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require 'fileutils'
+
+describe Gitlab::ImportExport::Saver do
+ let!(:project) { create(:project, :public, name: 'project') }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:shared) { project.import_export_shared }
+ subject { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ FileUtils.mkdir_p(shared.export_path)
+ FileUtils.touch("#{shared.export_path}/tmp.bundle")
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ end
+
+ context 'local archive' do
+ it 'saves the repo to disk' do
+ stub_feature_flags(import_export_object_storage: false)
+
+ subject.save
+
+ expect(shared.errors).to be_empty
+ expect(Dir.empty?(shared.archive_path)).to be false
+ end
+ end
+
+ context 'object storage' do
+ it 'saves the repo using object storage' do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(ImportExportUploader)
+
+ subject.save
+
+ expect(ImportExportUpload.find_by(project: project).export_file.url)
+ .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
new file mode 100644
index 00000000000..9c3870a0af8
--- /dev/null
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::UploadsManager do
+ let(:shared) { project.import_export_shared }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:project) { create(:project) }
+ let(:exported_file_path) { "#{shared.export_path}/uploads/#{upload.secret}/#{File.basename(upload.path)}" }
+
+ subject(:manager) { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ FileUtils.mkdir_p(shared.export_path)
+ end
+
+ after do
+ FileUtils.rm_rf(shared.export_path)
+ end
+
+ describe '#save' do
+ context 'when the project has uploads locally stored' do
+ let(:upload) { create(:upload, :issuable_upload, :with_file, model: project) }
+
+ before do
+ project.uploads << upload
+ end
+
+ it 'does not cause errors' do
+ manager.save
+
+ expect(shared.errors).to be_empty
+ end
+
+ it 'copies the file in the correct location when there is an upload' do
+ manager.save
+
+ expect(File).to exist(exported_file_path)
+ end
+ end
+
+ context 'using object storage' do
+ let!(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+ end
+
+ it 'saves the file' do
+ fake_uri = double
+
+ expect(fake_uri).to receive(:open).and_return(StringIO.new('File content'))
+ expect(URI).to receive(:parse).and_return(fake_uri)
+
+ manager.save
+
+ expect(File.read(exported_file_path)).to eq('File content')
+ end
+ end
+
+ describe '#restore' do
+ context 'using object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038'))
+ FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt"))
+ end
+
+ it 'restores the file' do
+ manager.restore
+
+ expect(project.uploads.size).to eq(1)
+ expect(project.uploads.first.build_uploader.filename).to eq('dummy.txt')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 095687fa89d..c716edd9397 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::UploadsSaver do
let(:shared) { project.import_export_shared }
before do
+ stub_feature_flags(import_export_object_storage: false)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
@@ -30,7 +31,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
@@ -52,7 +53,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
index 24bc231d5a0..441aa1defe6 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do
let!(:project_wiki) { ProjectWiki.new(project, user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
project_wiki.wiki
project_wiki.create_page("index", "test content")
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 10341486512..25827423914 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -12,7 +12,8 @@ describe Gitlab::ImportSources do
'FogBugz' => 'fogbugz',
'Repo by URL' => 'git',
'GitLab export' => 'gitlab_project',
- 'Gitea' => 'gitea'
+ 'Gitea' => 'gitea',
+ 'Manifest file' => 'manifest'
}
expect(described_class.options).to eq(expected)
@@ -31,6 +32,7 @@ describe Gitlab::ImportSources do
git
gitlab_project
gitea
+ manifest
)
expect(described_class.values).to eq(expected)
@@ -63,7 +65,8 @@ describe Gitlab::ImportSources do
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
- 'gitea' => Gitlab::LegacyGithubImport::Importer
+ 'gitea' => Gitlab::LegacyGithubImport::Importer,
+ 'manifest' => nil
}
import_sources.each do |name, klass|
@@ -82,7 +85,8 @@ describe Gitlab::ImportSources do
'fogbugz' => 'FogBugz',
'git' => 'Repo by URL',
'gitlab_project' => 'GitLab export',
- 'gitea' => 'Gitea'
+ 'gitea' => 'Gitea',
+ 'manifest' => 'Manifest file'
}
import_sources.each do |name, title|
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index 34b33772578..5c03a2ce7d3 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -70,4 +70,19 @@ describe Gitlab::Kubernetes do
it { is_expected.to eq(YAML.load_file(path)) }
end
end
+
+ describe '#add_terminal_auth' do
+ it 'adds authentication parameters to a hash' do
+ terminal = { original: 'value' }
+
+ add_terminal_auth(terminal, token: 'foo', max_session_time: 0, ca_pem: 'bar')
+
+ expect(terminal).to eq(
+ original: 'value',
+ headers: { 'Authorization' => ['Bearer foo'] },
+ max_session_time: 0,
+ ca_pem: 'bar'
+ )
+ end
+ end
end
diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb
new file mode 100644
index 00000000000..ab305fb2316
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::Manifest, :postgresql do
+ let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
+ let(:manifest) { described_class.new(file) }
+
+ describe '#valid?' do
+ context 'valid file' do
+ it { expect(manifest.valid?).to be true }
+ end
+
+ context 'missing or invalid attributes' do
+ let(:file) { Tempfile.new('foo') }
+
+ before do
+ content = <<~EOS
+ <manifest>
+ <remote review="invalid-url" />
+ <project name="platform/build"/>
+ </manifest>
+ EOS
+
+ file.write(content)
+ file.rewind
+ end
+
+ it { expect(manifest.valid?).to be false }
+
+ describe 'errors' do
+ before do
+ manifest.valid?
+ end
+
+ it { expect(manifest.errors).to include('Make sure a <remote> tag is present and is valid.') }
+ it { expect(manifest.errors).to include('Make sure every <project> tag has name and path attributes.') }
+ end
+ end
+ end
+
+ describe '#projects' do
+ it { expect(manifest.projects.size).to eq(660) }
+ it { expect(manifest.projects[0][:name]).to eq('platform/build') }
+ it { expect(manifest.projects[0][:path]).to eq('build/make') }
+ it { expect(manifest.projects[0][:url]).to eq('https://android-review.googlesource.com/platform/build') }
+ end
+end
diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
new file mode 100644
index 00000000000..1d01d437535
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::ProjectCreator, :postgresql do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+ let(:repository) do
+ {
+ path: 'device/common',
+ url: 'https://android-review.googlesource.com/device/common'
+ }
+ end
+
+ before do
+ group.add_owner(user)
+ end
+
+ subject { described_class.new(repository, group, user) }
+
+ describe '#execute' do
+ it { expect(subject.execute).to be_a(Project) }
+ it { expect { subject.execute }.to change { Project.count }.by(1) }
+ it { expect { subject.execute }.to change { Group.count }.by(1) }
+
+ it 'creates project with valid full path and import url' do
+ subject.execute
+
+ project = Project.last
+
+ expect(project.full_path).to eq(File.join(group.path, 'device/common'))
+ expect(project.import_url).to eq('https://android-review.googlesource.com/device/common')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index b24c9882c0c..7a3a9ab875b 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -79,7 +79,7 @@ describe Gitlab::Middleware::Go do
let(:current_user) { project.creator }
before do
- project.team.add_master(current_user)
+ project.team.add_maintainer(current_user)
end
shared_examples 'authenticated' do
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
index b4837a1689a..f788f8ee276 100644
--- a/spec/lib/gitlab/middleware/multipart_spec.rb
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -75,6 +75,33 @@ describe Gitlab::Middleware::Multipart do
it_behaves_like 'multipart upload files'
end
+ it 'allows symlinks for uploads dir' do
+ Tempfile.open('two-levels') do |tempfile|
+ symlinked_dir = '/some/dir/uploads'
+ symlinked_path = File.join(symlinked_dir, File.basename(tempfile.path))
+ env = post_env({ 'file' => symlinked_path }, { 'file.name' => original_filename, 'file.path' => symlinked_path }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ allow(FileUploader).to receive(:root).and_return(symlinked_dir)
+ allow(UploadedFile).to receive(:allowed_paths).and_return([symlinked_dir, Gitlab.config.uploads.storage_path])
+ allow(File).to receive(:realpath).and_call_original
+ allow(File).to receive(:realpath).with(symlinked_dir).and_return(Dir.tmpdir)
+ allow(File).to receive(:realpath).with(symlinked_path).and_return(tempfile.path)
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(symlinked_dir).and_return(true)
+
+ # override Dir.tmpdir because this dir is in the list of allowed paths
+ # and it would match FileUploader.root path (which in this test is linked
+ # to /tmp too)
+ allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir'))
+
+ expect(app).to receive(:call) do |env|
+ expect(Rack::Request.new(env).params['file']).to be_a(::UploadedFile)
+ end
+
+ middleware.call(env)
+ end
+ end
+
def post_env(rewritten_fields, params, secret, issuer)
token = JWT.encode({ 'iss' => issuer, 'rewritten_fields' => rewritten_fields }, secret, 'HS256')
Rack::MockRequest.env_for(
diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb
index 5c398bc2063..8fbeaa065fa 100644
--- a/spec/lib/gitlab/middleware/read_only_spec.rb
+++ b/spec/lib/gitlab/middleware/read_only_spec.rb
@@ -4,28 +4,6 @@ describe Gitlab::Middleware::ReadOnly do
include Rack::Test::Methods
using RSpec::Parameterized::TableSyntax
- RSpec::Matchers.define :be_a_redirect do
- match do |response|
- response.status == 301
- end
- end
-
- RSpec::Matchers.define :disallow_request do
- match do |middleware|
- alert = middleware.env['rack.session'].to_hash
- .dig('flash', 'flashes', 'alert')
-
- alert&.include?('You cannot perform write operations')
- end
- end
-
- RSpec::Matchers.define :disallow_request_in_json do
- match do |response|
- json_response = JSON.parse(response.body)
- response.body.include?('You cannot perform write operations') && json_response.key?('message')
- end
- end
-
let(:rack_stack) do
rack = Rack::Builder.new do
use ActionDispatch::Session::CacheStore
@@ -66,38 +44,38 @@ describe Gitlab::Middleware::ReadOnly do
it 'expects PATCH requests to be disallowed' do
response = request.patch('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects PUT requests to be disallowed' do
response = request.put('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects POST requests to be disallowed' do
response = request.post('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects a internal POST request to be allowed after a disallowed request' do
response = request.post('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
response = request.post("/api/#{API::API.version}/internal")
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
end
it 'expects DELETE requests to be disallowed' do
response = request.delete('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
@@ -105,7 +83,7 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).to receive(:recognize_path).and_call_original
response = request.post('/root/gitlab-ce/new/master/app/info/lfs/objects/batch')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
@@ -120,19 +98,19 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).not_to receive(:recognize_path)
response = request.post("/api/#{API::API.version}/internal")
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
it 'expects requests to sidekiq admin to be allowed' do
response = request.post('/admin/sidekiq')
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
response = request.get('/admin/sidekiq')
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
@@ -150,7 +128,7 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).to receive(:recognize_path).and_call_original
response = request.post(path)
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index 1dbead16d5b..c1b84e9f077 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -55,6 +55,19 @@ describe Gitlab::Popen do
end
end
+ context 'with a process that writes a lot of data to stderr' do
+ let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
+ # The pipe buffer is typically 64K. This string is about 440K.
+ let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
+
+ it 'returns zero' do
+ output, status = @klass.new.popen(spew_command, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
+ end
+
context 'without a directory argument' do
before do
@output, @status = @klass.new.popen(%w(ls))
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index f3cd6961e94..00c62c7bf96 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -41,7 +41,7 @@ describe Gitlab::ProjectAuthorizations do
it 'includes the correct access levels' do
mapping = map_access_levels(authorizations)
- expect(mapping[owned_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(mapping[owned_project.id]).to eq(Gitlab::Access::MAINTAINER)
expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER)
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
@@ -62,11 +62,11 @@ describe Gitlab::ProjectAuthorizations do
end
it 'uses the greatest access level when a user is a member of a nested group' do
- nested_group.add_master(user)
+ nested_group.add_maintainer(user)
mapping = map_access_levels(authorizations)
- expect(mapping[nested_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 50224bde722..767a3092c73 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -385,7 +385,7 @@ describe Gitlab::ProjectSearchResults do
let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) }
let(:team_master) do
user = create(:user, username: 'private-project-master')
- private_project.add_master(user)
+ private_project.add_maintainer(user)
user
end
let(:team_reporter) do
diff --git a/spec/lib/gitlab/sanitizers/svg_spec.rb b/spec/lib/gitlab/sanitizers/svg_spec.rb
index 030c2063ab2..df46a874528 100644
--- a/spec/lib/gitlab/sanitizers/svg_spec.rb
+++ b/spec/lib/gitlab/sanitizers/svg_spec.rb
@@ -7,9 +7,9 @@ describe Gitlab::Sanitizers::SVG do
describe '.clean' do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
- let(:data) { open(input_svg_path).read }
+ let(:data) { File.read(input_svg_path) }
let(:sanitized_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
- let(:sanitized) { open(sanitized_svg_path).read }
+ let(:sanitized) { File.read(sanitized_svg_path) }
it 'delegates sanitization to scrubber' do
expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index c435f988cdd..f8bf896950e 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -403,46 +403,36 @@ describe Gitlab::Shell do
end
describe '#create_repository' do
- shared_examples '#create_repository' do
- let(:repository_storage) { 'default' }
- let(:repository_storage_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
- end
- end
- let(:repo_name) { 'project/path' }
- let(:created_path) { File.join(repository_storage_path, repo_name + '.git') }
-
- after do
- FileUtils.rm_rf(created_path)
+ let(:repository_storage) { 'default' }
+ let(:repository_storage_path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
end
+ end
+ let(:repo_name) { 'project/path' }
+ let(:created_path) { File.join(repository_storage_path, repo_name + '.git') }
- it 'creates a repository' do
- expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
-
- expect(File.stat(created_path).mode & 0o777).to eq(0o770)
+ after do
+ FileUtils.rm_rf(created_path)
+ end
- hooks_path = File.join(created_path, 'hooks')
- expect(File.lstat(hooks_path)).to be_symlink
- expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path)
- end
+ it 'creates a repository' do
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
- it 'returns false when the command fails' do
- FileUtils.mkdir_p(File.dirname(created_path))
- # This file will block the creation of the repo's .git directory. That
- # should cause #create_repository to fail.
- FileUtils.touch(created_path)
+ expect(File.stat(created_path).mode & 0o777).to eq(0o770)
- expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
- end
+ hooks_path = File.join(created_path, 'hooks')
+ expect(File.lstat(hooks_path)).to be_symlink
+ expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path)
end
- context 'with gitaly' do
- it_behaves_like '#create_repository'
- end
+ it 'returns false when the command fails' do
+ FileUtils.mkdir_p(File.dirname(created_path))
+ # This file will block the creation of the repo's .git directory. That
+ # should cause #create_repository to fail.
+ FileUtils.touch(created_path)
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#create_repository'
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
end
end
@@ -513,22 +503,12 @@ describe Gitlab::Shell do
end
end
- shared_examples 'fetch_remote' do |gitaly_on|
+ describe '#fetch_remote' do
def fetch_remote(ssh_auth = nil, prune = true)
gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth, prune: prune)
end
- def expect_gitlab_projects(fail = false, options = {})
- expect(gitlab_projects).to receive(:fetch_remote).with(
- 'remote-name',
- timeout,
- options
- ).and_return(!fail)
-
- allow(gitlab_projects).to receive(:output).and_return('error') if fail
- end
-
- def expect_gitaly_call(fail, options = {})
+ def expect_call(fail, options = {})
receive_fetch_remote =
if fail
receive(:fetch_remote).and_raise(GRPC::NotFound)
@@ -539,16 +519,6 @@ describe Gitlab::Shell do
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive_fetch_remote
end
- if gitaly_on
- def expect_call(fail, options = {})
- expect_gitaly_call(fail, options)
- end
- else
- def expect_call(fail, options = {})
- expect_gitlab_projects(fail, options)
- end
- end
-
def build_ssh_auth(opts = {})
defaults = {
ssh_import?: true,
@@ -634,14 +604,6 @@ describe Gitlab::Shell do
expect(fetch_remote(ssh_auth)).to be_truthy
end
end
- end
-
- describe '#fetch_remote local', :skip_gitaly_mock do
- it_should_behave_like 'fetch_remote', false
- end
-
- describe '#fetch_remote gitaly' do
- it_should_behave_like 'fetch_remote', true
context 'gitaly call' do
let(:remote_name) { 'remote-name' }
@@ -683,25 +645,6 @@ describe Gitlab::Shell do
end.to raise_error(Gitlab::Shell::Error, "error")
end
end
-
- context 'without gitaly', :disable_gitaly do
- it 'returns true when the command succeeds' do
- expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
-
- result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
-
- expect(result).to be_truthy
- end
-
- it 'raises an exception when the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:import_project) { false }
-
- expect do
- gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
- end.to raise_error(Gitlab::Shell::Error, "error")
- end
- end
end
end
diff --git a/spec/lib/gitlab/slash_commands/issue_move_spec.rb b/spec/lib/gitlab/slash_commands/issue_move_spec.rb
index d41441c9472..9a990e1fad7 100644
--- a/spec/lib/gitlab/slash_commands/issue_move_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_move_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::SlashCommands::IssueMove, service: true do
set(:other_project) { create(:project, namespace: project.namespace) }
before do
- [project, other_project].each { |prj| prj.add_master(user) }
+ [project, other_project].each { |prj| prj.add_maintainer(user) }
end
subject { described_class.new(project, chat_name) }
diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 8e7df946529..724c76ade6e 100644
--- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::SlashCommands::IssueNew do
let(:regex_match) { described_class.match("issue create bird is the word") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
subject do
diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 189e9592f1b..47787307990 100644
--- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::IssueSearch do
context 'the user has access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'returns all results' do
diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
index b1db1638237..5c4ba2736ba 100644
--- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::SlashCommands::IssueShow do
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
subject do
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
index fc8991fd31f..758a9bc5a2b 100644
--- a/spec/lib/gitlab/url_sanitizer_spec.rb
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -92,6 +92,7 @@ describe Gitlab::UrlSanitizer do
context 'credentials in URL' do
where(:url, :credentials) do
'http://foo:bar@example.com' | { user: 'foo', password: 'bar' }
+ 'http://foo:bar:baz@example.com' | { user: 'foo', password: 'bar:baz' }
'http://:bar@example.com' | { user: nil, password: 'bar' }
'http://foo:@example.com' | { user: 'foo', password: nil }
'http://foo@example.com' | { user: 'foo', password: nil }
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index 0469d984a40..9da06bb40f4 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -9,8 +9,8 @@ describe Gitlab::UserAccess do
describe '#can_push_to_branch?' do
describe 'push to none protected branch' do
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
@@ -38,8 +38,8 @@ describe Gitlab::UserAccess do
expect(access.can_push_to_branch?('master')).to be_truthy
end
- it 'returns true if user is master' do
- empty_project.add_master(user)
+ it 'returns true if user is maintainer' do
+ empty_project.add_maintainer(user)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
@@ -83,8 +83,8 @@ describe Gitlab::UserAccess do
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
@@ -113,8 +113,8 @@ describe Gitlab::UserAccess do
@branch = create :protected_branch, :developers_can_push, project: project
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
@@ -170,8 +170,8 @@ describe Gitlab::UserAccess do
@branch = create :protected_branch, :developers_can_merge, project: project
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
@@ -192,8 +192,8 @@ describe Gitlab::UserAccess do
describe '#can_create_tag?' do
describe 'push to none protected tag' do
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?('random_tag')).to be_truthy
end
@@ -215,8 +215,8 @@ describe Gitlab::UserAccess do
let(:tag) { create(:protected_tag, project: project, name: "test") }
let(:not_existing_tag) { create :protected_tag, project: project }
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?(tag.name)).to be_truthy
end
@@ -239,8 +239,8 @@ describe Gitlab::UserAccess do
@tag = create(:protected_tag, :developers_can_create, project: project)
end
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?(@tag.name)).to be_truthy
end
@@ -261,8 +261,8 @@ describe Gitlab::UserAccess do
describe '#can_delete_branch?' do
describe 'delete unprotected branch' do
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_delete_branch?('random_branch')).to be_truthy
end
@@ -283,8 +283,8 @@ describe Gitlab::UserAccess do
describe 'delete protected branch' do
let(:branch) { create(:protected_branch, project: project, name: "test") }
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_delete_branch?(branch.name)).to be_truthy
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 660671cefaf..23869f3d2da 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -36,22 +36,20 @@ describe Gitlab::Workhorse do
allow(described_class).to receive(:git_archive_cache_disabled?).and_return(cache_disabled)
end
- context 'when Gitaly workhorse_archive feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-archive')
- expect(params).to include(gitaly_params)
- end
+ expect(key).to eq('Gitlab-Workhorse-Send-Data')
+ expect(command).to eq('git-archive')
+ expect(params).to include(gitaly_params)
+ end
- context 'when archive caching is disabled' do
- let(:cache_disabled) { true }
+ context 'when archive caching is disabled' do
+ let(:cache_disabled) { true }
- it 'tells workhorse not to use the cache' do
- _, _, params = decode_workhorse_header(subject)
- expect(params).to include({ 'DisableCache' => true })
- end
+ it 'tells workhorse not to use the cache' do
+ _, _, params = decode_workhorse_header(subject)
+ expect(params).to include({ 'DisableCache' => true })
end
end
@@ -70,34 +68,22 @@ describe Gitlab::Workhorse do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_patch(repository, diff_refs) }
- context 'when Gitaly workhorse_send_git_patch feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-format-patch")
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
- repository: repository.gitaly_repository,
- left_commit_id: 'base',
- right_commit_id: 'head'
- ).to_json
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_send_git_patch feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-format-patch")
- expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
- end
+ expect(key).to eq("Gitlab-Workhorse-Send-Data")
+ expect(command).to eq("git-format-patch")
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
+ repository: repository.gitaly_repository,
+ left_commit_id: 'base',
+ right_commit_id: 'head'
+ ).to_json
+ }.deep_stringify_keys)
end
end
@@ -143,34 +129,22 @@ describe Gitlab::Workhorse do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_diff(repository, diff_refs) }
- context 'when Gitaly workhorse_send_git_diff feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-diff")
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
- repository: repository.gitaly_repository,
- left_commit_id: 'base',
- right_commit_id: 'head'
- ).to_json
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_send_git_diff feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-diff")
- expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
- end
+ expect(key).to eq("Gitlab-Workhorse-Send-Data")
+ expect(command).to eq("git-diff")
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
+ repository: repository.gitaly_repository,
+ left_commit_id: 'base',
+ right_commit_id: 'head'
+ ).to_json
+ }.deep_stringify_keys)
end
end
@@ -189,7 +163,7 @@ describe Gitlab::Workhorse do
end
it 'accepts a trailing newline' do
- open(described_class.secret_path, 'a') { |f| f.write "\n" }
+ File.open(described_class.secret_path, 'a') { |f| f.write "\n" }
expect(subject.length).to eq(32)
end
@@ -425,34 +399,22 @@ describe Gitlab::Workhorse do
subject { described_class.send_git_blob(repository, blob) }
- context 'when Gitaly workhorse_raw_show feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-blob')
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'GetBlobRequest' => {
- repository: repository.gitaly_repository.to_h,
- oid: blob.id,
- limit: -1
- }
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_raw_show feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-blob')
- expect(params).to eq('RepoPath' => repository.path_to_repo, 'BlobId' => blob.id)
- end
+ expect(key).to eq('Gitlab-Workhorse-Send-Data')
+ expect(command).to eq('git-blob')
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'GetBlobRequest' => {
+ repository: repository.gitaly_repository.to_h,
+ oid: blob.id,
+ limit: -1
+ }
+ }.deep_stringify_keys)
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index a9a45367b4a..ff1a5aa2536 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -314,6 +314,17 @@ describe Notify do
end
end
+ describe 'that are new with a description' do
+ subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
+
+ it_behaves_like 'it should show Gmail Actions View Merge request link'
+ it_behaves_like "an unsubscribeable thread"
+
+ it 'contains the description' do
+ is_expected.to have_body_text(merge_request.description)
+ end
+ end
+
describe 'that have been relabeled' do
subject { described_class.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) }
@@ -541,7 +552,7 @@ describe Notify do
describe 'project access requested' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
- project.add_master(project.owner)
+ project.add_maintainer(project.owner)
end
end
@@ -616,8 +627,8 @@ describe Notify do
end
describe 'project invitation' do
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
- let(:project_member) { invite_to_project(project, inviter: master) }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let(:project_member) { invite_to_project(project, inviter: maintainer) }
subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
@@ -636,9 +647,9 @@ describe Notify do
describe 'project invitation accepted' do
let(:invited_user) { create(:user, name: 'invited user') }
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:project_member) do
- invitee = invite_to_project(project, inviter: master)
+ invitee = invite_to_project(project, inviter: maintainer)
invitee.accept_invite!(invited_user)
invitee
end
@@ -659,14 +670,14 @@ describe Notify do
end
describe 'project invitation declined' do
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:project_member) do
- invitee = invite_to_project(project, inviter: master)
+ invitee = invite_to_project(project, inviter: maintainer)
invitee.decline_invite!
invitee
end
- subject { described_class.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) }
+ subject { described_class.member_invite_declined_email('project', project.id, project_member.invite_email, maintainer.id) }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
@@ -1355,7 +1366,8 @@ describe Notify do
it 'only sends the text template' do
stub_application_setting(html_emails_enabled: false)
- EmailTemplateInterceptor.delivering_email(multipart_mail)
+ Gitlab::Email::Hook::EmailTemplateInterceptor
+ .delivering_email(multipart_mail)
expect(multipart_mail).to have_part_with('text/plain')
expect(multipart_mail).not_to have_part_with('text/html')
@@ -1366,7 +1378,8 @@ describe Notify do
it 'sends a multipart message' do
stub_application_setting(html_emails_enabled: true)
- EmailTemplateInterceptor.delivering_email(multipart_mail)
+ Gitlab::Email::Hook::EmailTemplateInterceptor
+ .delivering_email(multipart_mail)
expect(multipart_mail).to have_part_with('text/plain')
expect(multipart_mail).to have_part_with('text/html')
diff --git a/spec/migrations/enqueue_delete_diff_files_workers_spec.rb b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb
new file mode 100644
index 00000000000..6bae870920c
--- /dev/null
+++ b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180619121030_enqueue_delete_diff_files_workers.rb')
+
+describe EnqueueDeleteDiffFilesWorkers, :migration, :sidekiq do
+ it 'correctly schedules diff files deletion schedulers' do
+ Sidekiq::Testing.fake! do
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_async)
+ .with(described_class::SCHEDULER)
+ .and_call_original
+
+ migrate!
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
+ end
+end
diff --git a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
index dd2b08099f2..495e86ee888 100644
--- a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
+++ b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
@@ -14,7 +14,7 @@ describe IssuesMovedToIdForeignKey, :migration, schema: 20171114150259 do
it 'removes the orphaned moved_to_id' do
subject.down
- issue_third.update_attributes(moved_to_id: 100000)
+ issue_third.update(moved_to_id: 100000)
subject.up
diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
index df009cec25c..ba4c66057d4 100644
--- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
+++ b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
@@ -12,7 +12,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
class KubernetesService < ActiveRecord::Base
self.table_name = 'services'
- serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :properties, JSON
default_value_for :active, true
default_value_for :type, 'KubernetesService'
@@ -175,7 +175,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
end
end
- def tr(s)
- s.delete("'")
+ def tr(str)
+ str.delete("'")
end
end
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index 7e75d5a5411..6dba132184c 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -30,7 +30,7 @@ describe Ci::BuildMetadata do
context 'when runner is assigned to the job' do
before do
- build.update_attributes(runner: runner)
+ build.update(runner: runner)
end
context 'when runner timeout is lower than project timeout' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 090ca159e08..ee923374480 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -186,18 +186,18 @@ describe Ci::Build do
let(:runner) { create(:ci_runner, :project, projects: [build.project]) }
before do
- runner.update_attributes(contacted_at: 1.second.ago)
+ runner.update(contacted_at: 1.second.ago)
end
it { is_expected.to be_truthy }
it 'that is inactive' do
- runner.update_attributes(active: false)
+ runner.update(active: false)
is_expected.to be_falsey
end
it 'that is not online' do
- runner.update_attributes(contacted_at: nil)
+ runner.update(contacted_at: nil)
is_expected.to be_falsey
end
@@ -261,7 +261,7 @@ describe Ci::Build do
context 'artifacts metadata does not exist' do
before do
- build.update_attributes(legacy_artifacts_metadata: nil)
+ build.update(legacy_artifacts_metadata: nil)
end
it { is_expected.to be_falsy }
@@ -1535,7 +1535,7 @@ describe Ci::Build do
expect(ProjectStatistics)
.not_to receive(:increment_statistic)
- build.project.update_attributes(pending_delete: true)
+ build.project.update(pending_delete: true)
build.project.destroy!
end
end
@@ -1613,6 +1613,7 @@ describe Ci::Build do
{ key: 'CI_JOB_NAME', value: 'test', public: true },
{ key: 'CI_JOB_STAGE', value: 'test', public: true },
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true },
+ { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true },
{ key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true },
{ key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true },
{ key: 'CI_BUILD_REF', value: build.sha, public: true },
@@ -1662,7 +1663,7 @@ describe Ci::Build do
end
before do
- build.update_attributes(user: user)
+ build.update(user: user)
end
it { user_variables.each { |v| is_expected.to include(v) } }
@@ -1740,7 +1741,7 @@ describe Ci::Build do
context 'when build started manually' do
before do
- build.update_attributes(when: :manual)
+ build.update(when: :manual)
end
let(:manual_variable) do
@@ -1756,7 +1757,7 @@ describe Ci::Build do
end
before do
- build.update_attributes(tag: true)
+ build.update(tag: true)
end
it { is_expected.to include(tag_variable) }
@@ -2687,4 +2688,58 @@ describe Ci::Build do
end
end
end
+
+ describe '#artifacts_metadata_entry' do
+ set(:build) { create(:ci_build, project: project) }
+ let(:path) { 'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif' }
+
+ before do
+ stub_artifacts_object_storage
+ end
+
+ subject { build.artifacts_metadata_entry(path) }
+
+ context 'when using local storage' do
+ let!(:metadata) { create(:ci_job_artifact, :metadata, job: build) }
+
+ context 'for existing file' do
+ it 'does exist' do
+ is_expected.to be_exists
+ end
+ end
+
+ context 'for non-existing file' do
+ let(:path) { 'invalid-file' }
+
+ it 'does not exist' do
+ is_expected.not_to be_exists
+ end
+ end
+ end
+
+ context 'when using remote storage' do
+ include HttpIOHelpers
+
+ let!(:metadata) { create(:ci_job_artifact, :remote_store, :metadata, job: build) }
+ let(:file_path) { expand_fixture_path('ci_build_artifacts_metadata.gz') }
+
+ before do
+ stub_remote_url_206(metadata.file.url, file_path)
+ end
+
+ context 'for existing file' do
+ it 'does exist' do
+ is_expected.to be_exists
+ end
+ end
+
+ context 'for non-existing file' do
+ let(:path) { 'invalid-file' }
+
+ it 'does not exist' do
+ is_expected.not_to be_exists
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 464897de306..774a638b430 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -14,6 +14,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
before do
stub_feature_flags(ci_enable_live_trace: true)
+ stub_artifacts_object_storage
end
context 'FastDestroyAll' do
@@ -37,6 +38,22 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
+ describe '.all_stores' do
+ subject { described_class.all_stores }
+
+ it 'returns a correctly ordered array' do
+ is_expected.to eq(%w[redis database fog])
+ end
+
+ it 'returns redis store as the the lowest precedence' do
+ expect(subject.first).to eq('redis')
+ end
+
+ it 'returns fog store as the the highest precedence' do
+ expect(subject.last).to eq('fog')
+ end
+ end
+
describe '#data' do
subject { build_trace_chunk.data }
@@ -44,181 +61,269 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :redis }
before do
- build_trace_chunk.send(:redis_set_data, 'Sample data in redis')
+ build_trace_chunk.send(:unsafe_set_data!, 'Sample data in redis')
end
it { is_expected.to eq('Sample data in redis') }
end
context 'when data_store is database' do
- let(:data_store) { :db }
- let(:raw_data) { 'Sample data in db' }
+ let(:data_store) { :database }
+ let(:raw_data) { 'Sample data in database' }
- it { is_expected.to eq('Sample data in db') }
+ it { is_expected.to eq('Sample data in database') }
end
- end
-
- describe '#set_data' do
- subject { build_trace_chunk.send(:set_data, value) }
- let(:value) { 'Sample data' }
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
- context 'when value bytesize is bigger than CHUNK_SIZE' do
- let(:value) { 'a' * (described_class::CHUNK_SIZE + 1) }
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, 'Sample data in fog')
+ end
- it { expect { subject }.to raise_error('too much data') }
+ it { is_expected.to eq('Sample data in fog') }
end
+ end
- context 'when data_store is redis' do
- let(:data_store) { :redis }
+ describe '#append' do
+ subject { build_trace_chunk.append(new_data, offset) }
- it do
- expect(build_trace_chunk.send(:redis_data)).to be_nil
+ let(:new_data) { 'Sample new data' }
+ let(:offset) { 0 }
+ let(:merged_data) { data + new_data.to_s }
- subject
+ shared_examples_for 'Appending correctly' do
+ context 'when offset is negative' do
+ let(:offset) { -1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
+ end
- expect(build_trace_chunk.send(:redis_data)).to eq(value)
+ context 'when offset is bigger than data size' do
+ let(:offset) { data.bytesize + 1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
end
- context 'when fullfilled chunk size' do
- let(:value) { 'a' * described_class::CHUNK_SIZE }
+ context 'when new data overflows chunk size' do
+ let(:new_data) { 'a' * (described_class::CHUNK_SIZE + 1) }
- it 'schedules stashing data' do
- expect(Ci::BuildTraceChunkFlushWorker).to receive(:perform_async).once
+ it { expect { subject }.to raise_error('Chunk size overflow') }
+ end
+
+ context 'when offset is EOF' do
+ let(:offset) { data.bytesize }
+ it 'appends' do
subject
+
+ expect(build_trace_chunk.data).to eq(merged_data)
end
- end
- end
- context 'when data_store is database' do
- let(:data_store) { :db }
+ context 'when the other process is appending' do
+ let(:lease_key) { "trace_write:#{build_trace_chunk.build.id}:chunks:#{build_trace_chunk.chunk_index}" }
- it 'sets data' do
- expect(build_trace_chunk.raw_data).to be_nil
+ before do
+ stub_exclusive_lease_taken(lease_key)
+ end
- subject
+ it 'raise an error' do
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
- expect(build_trace_chunk.raw_data).to eq(value)
- expect(build_trace_chunk.persisted?).to be_truthy
- end
+ context 'when new_data is nil' do
+ let(:new_data) { nil }
- context 'when raw_data is not changed' do
- it 'does not execute UPDATE' do
- expect(build_trace_chunk.raw_data).to be_nil
- build_trace_chunk.save!
+ it 'raises an error' do
+ expect { subject }.to raise_error('New data is missing')
+ end
+ end
- # First set
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to be > 0
- expect(build_trace_chunk.raw_data).to eq(value)
- expect(build_trace_chunk.persisted?).to be_truthy
+ context 'when new_data is empty' do
+ let(:new_data) { '' }
- # Second set
- build_trace_chunk.reload
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to be(0)
+ it 'does not append' do
+ subject
+
+ expect(build_trace_chunk.data).to eq(data)
+ end
+
+ it 'does not execute UPDATE' do
+ ActiveRecord::QueryRecorder.new { subject }.log.map do |query|
+ expect(query).not_to include('UPDATE')
+ end
+ end
end
end
- context 'when fullfilled chunk size' do
- it 'does not schedule stashing data' do
- expect(Ci::BuildTraceChunkFlushWorker).not_to receive(:perform_async)
+ context 'when offset is middle of datasize' do
+ let(:offset) { data.bytesize / 2 }
+ it 'appends' do
subject
+
+ expect(build_trace_chunk.data).to eq(data.byteslice(0, offset) + new_data)
end
end
end
- end
- describe '#truncate' do
- subject { build_trace_chunk.truncate(offset) }
+ shared_examples_for 'Scheduling sidekiq worker to flush data to persist store' do
+ context 'when new data fullfilled chunk size' do
+ let(:new_data) { 'a' * described_class::CHUNK_SIZE }
- shared_examples_for 'truncates' do
- context 'when offset is negative' do
- let(:offset) { -1 }
+ it 'schedules trace chunk flush worker' do
+ expect(Ci::BuildTraceChunkFlushWorker).to receive(:perform_async).once
- it { expect { subject }.to raise_error('Offset is out of range') }
- end
+ subject
+ end
- context 'when offset is bigger than data size' do
- let(:offset) { data.bytesize + 1 }
+ it 'migrates data to object storage' do
+ Sidekiq::Testing.inline! do
+ subject
- it { expect { subject }.to raise_error('Offset is out of range') }
+ build_trace_chunk.reload
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(build_trace_chunk.data).to eq(new_data)
+ end
+ end
end
+ end
- context 'when offset is 10' do
- let(:offset) { 10 }
+ shared_examples_for 'Scheduling no sidekiq worker' do
+ context 'when new data fullfilled chunk size' do
+ let(:new_data) { 'a' * described_class::CHUNK_SIZE }
+
+ it 'does not schedule trace chunk flush worker' do
+ expect(Ci::BuildTraceChunkFlushWorker).not_to receive(:perform_async)
- it 'truncates' do
subject
+ end
- expect(build_trace_chunk.data).to eq(data.byteslice(0, offset))
+ it 'does not migrate data to object storage' do
+ Sidekiq::Testing.inline! do
+ data_store = build_trace_chunk.data_store
+
+ subject
+
+ build_trace_chunk.reload
+ expect(build_trace_chunk.data_store).to eq(data_store)
+ end
end
end
end
context 'when data_store is redis' do
let(:data_store) { :redis }
- let(:data) { 'Sample data in redis' }
- before do
- build_trace_chunk.send(:redis_set_data, data)
+ context 'when there are no data' do
+ let(:data) { '' }
+
+ it 'has no data' do
+ expect(build_trace_chunk.data).to be_empty
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling sidekiq worker to flush data to persist store'
end
- it_behaves_like 'truncates'
- end
+ context 'when there are some data' do
+ let(:data) { 'Sample data in redis' }
- context 'when data_store is database' do
- let(:data_store) { :db }
- let(:raw_data) { 'Sample data in db' }
- let(:data) { raw_data }
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
- it_behaves_like 'truncates'
+ it 'has data' do
+ expect(build_trace_chunk.data).to eq(data)
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling sidekiq worker to flush data to persist store'
+ end
end
- end
- describe '#append' do
- subject { build_trace_chunk.append(new_data, offset) }
+ context 'when data_store is database' do
+ let(:data_store) { :database }
- let(:new_data) { 'Sample new data' }
- let(:offset) { 0 }
- let(:total_data) { data + new_data }
+ context 'when there are no data' do
+ let(:data) { '' }
- shared_examples_for 'appends' do
- context 'when offset is negative' do
- let(:offset) { -1 }
+ it 'has no data' do
+ expect(build_trace_chunk.data).to be_empty
+ end
- it { expect { subject }.to raise_error('Offset is out of range') }
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
end
- context 'when offset is bigger than data size' do
- let(:offset) { data.bytesize + 1 }
+ context 'when there are some data' do
+ let(:raw_data) { 'Sample data in database' }
+ let(:data) { raw_data }
- it { expect { subject }.to raise_error('Offset is out of range') }
+ it 'has data' do
+ expect(build_trace_chunk.data).to eq(data)
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
end
+ end
- context 'when offset is bigger than data size' do
- let(:new_data) { 'a' * (described_class::CHUNK_SIZE + 1) }
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
- it { expect { subject }.to raise_error('Chunk size overflow') }
+ context 'when there are no data' do
+ let(:data) { '' }
+
+ it 'has no data' do
+ expect(build_trace_chunk.data).to be_empty
+ end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
end
- context 'when offset is EOF' do
- let(:offset) { data.bytesize }
+ context 'when there are some data' do
+ let(:data) { 'Sample data in fog' }
- it 'appends' do
- subject
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
- expect(build_trace_chunk.data).to eq(total_data)
+ it 'has data' do
+ expect(build_trace_chunk.data).to eq(data)
end
+
+ it_behaves_like 'Appending correctly'
+ it_behaves_like 'Scheduling no sidekiq worker'
+ end
+ end
+ end
+
+ describe '#truncate' do
+ subject { build_trace_chunk.truncate(offset) }
+
+ shared_examples_for 'truncates' do
+ context 'when offset is negative' do
+ let(:offset) { -1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
+ end
+
+ context 'when offset is bigger than data size' do
+ let(:offset) { data.bytesize + 1 }
+
+ it { expect { subject }.to raise_error('Offset is out of range') }
end
context 'when offset is 10' do
let(:offset) { 10 }
- it 'appends' do
+ it 'truncates' do
subject
- expect(build_trace_chunk.data).to eq(data.byteslice(0, offset) + new_data)
+ expect(build_trace_chunk.data).to eq(data.byteslice(0, offset))
end
end
end
@@ -228,18 +333,29 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { 'Sample data in redis' }
before do
- build_trace_chunk.send(:redis_set_data, data)
+ build_trace_chunk.send(:unsafe_set_data!, data)
end
- it_behaves_like 'appends'
+ it_behaves_like 'truncates'
end
context 'when data_store is database' do
- let(:data_store) { :db }
- let(:raw_data) { 'Sample data in db' }
+ let(:data_store) { :database }
+ let(:raw_data) { 'Sample data in database' }
let(:data) { raw_data }
- it_behaves_like 'appends'
+ it_behaves_like 'truncates'
+ end
+
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
+ let(:data) { 'Sample data in fog' }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it_behaves_like 'truncates'
end
end
@@ -253,7 +369,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { 'Sample data in redis' }
before do
- build_trace_chunk.send(:redis_set_data, data)
+ build_trace_chunk.send(:unsafe_set_data!, data)
end
it { is_expected.to eq(data.bytesize) }
@@ -265,10 +381,10 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data_store is database' do
- let(:data_store) { :db }
+ let(:data_store) { :database }
context 'when data exists' do
- let(:raw_data) { 'Sample data in db' }
+ let(:raw_data) { 'Sample data in database' }
let(:data) { raw_data }
it { is_expected.to eq(data.bytesize) }
@@ -278,10 +394,43 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it { is_expected.to eq(0) }
end
end
+
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
+
+ context 'when data exists' do
+ let(:data) { 'Sample data in fog' }
+ let(:key) { "tmp/builds/#{build.id}/chunks/#{chunk_index}.log" }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it { is_expected.to eq(data.bytesize) }
+ end
+
+ context 'when data does not exist' do
+ it { is_expected.to eq(0) }
+ end
+ end
end
- describe '#use_database!' do
- subject { build_trace_chunk.use_database! }
+ describe '#persist_data!' do
+ subject { build_trace_chunk.persist_data! }
+
+ shared_examples_for 'Atomic operation' do
+ context 'when the other process is persisting' do
+ let(:lease_key) { "trace_write:#{build_trace_chunk.build.id}:chunks:#{build_trace_chunk.chunk_index}" }
+
+ before do
+ stub_exclusive_lease_taken(lease_key)
+ end
+
+ it 'raise an error' do
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+ end
context 'when data_store is redis' do
let(:data_store) { :redis }
@@ -290,46 +439,93 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data) { 'Sample data in redis' }
before do
- build_trace_chunk.send(:redis_set_data, data)
+ build_trace_chunk.send(:unsafe_set_data!, data)
end
- it 'stashes the data' do
- expect(build_trace_chunk.data_store).to eq('redis')
- expect(build_trace_chunk.send(:redis_data)).to eq(data)
- expect(build_trace_chunk.raw_data).to be_nil
+ it 'persists the data' do
+ expect(build_trace_chunk.redis?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to eq(data)
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound)
subject
- expect(build_trace_chunk.data_store).to eq('db')
- expect(build_trace_chunk.send(:redis_data)).to be_nil
- expect(build_trace_chunk.raw_data).to eq(data)
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
end
+
+ it_behaves_like 'Atomic operation'
end
context 'when data does not exist' do
- it 'does not call UPDATE' do
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to eq(0)
+ it 'does not persist' do
+ expect { subject }.to raise_error('Can not persist empty data')
end
end
end
context 'when data_store is database' do
- let(:data_store) { :db }
+ let(:data_store) { :database }
- it 'does not call UPDATE' do
- expect(ActiveRecord::QueryRecorder.new { subject }.count).to eq(0)
+ context 'when data exists' do
+ let(:data) { 'Sample data in database' }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it 'persists the data' do
+ expect(build_trace_chunk.database?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to eq(data)
+ expect { Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk) }.to raise_error(Excon::Error::NotFound)
+
+ subject
+
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
+ end
+
+ it_behaves_like 'Atomic operation'
end
- end
- end
- describe 'ExclusiveLock' do
- before do
- stub_exclusive_lease_taken
- stub_const('Ci::BuildTraceChunk::WRITE_LOCK_RETRY', 1)
+ context 'when data does not exist' do
+ it 'does not persist' do
+ expect { subject }.to raise_error('Can not persist empty data')
+ end
+ end
end
- it 'raise an error' do
- expect { build_trace_chunk.append('ABC', 0) }.to raise_error('Failed to obtain write lock')
+ context 'when data_store is fog' do
+ let(:data_store) { :fog }
+
+ context 'when data exists' do
+ let(:data) { 'Sample data in fog' }
+
+ before do
+ build_trace_chunk.send(:unsafe_set_data!, data)
+ end
+
+ it 'does not change data store' do
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
+
+ subject
+
+ expect(build_trace_chunk.fog?).to be_truthy
+ expect(Ci::BuildTraceChunks::Redis.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Database.new.data(build_trace_chunk)).to be_nil
+ expect(Ci::BuildTraceChunks::Fog.new.data(build_trace_chunk)).to eq(data)
+ end
+
+ it_behaves_like 'Atomic operation'
+ end
end
end
diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb
new file mode 100644
index 00000000000..d8fc9d57e95
--- /dev/null
+++ b/spec/models/ci/build_trace_chunks/database_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Ci::BuildTraceChunks::Database do
+ let(:data_store) { described_class.new }
+
+ describe '#available?' do
+ subject { data_store.available? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ describe '#data' do
+ subject { data_store.data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'sample data in database') }
+
+ it 'returns the data' do
+ is_expected.to eq('sample data in database')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :database_without_data) }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#set_data' do
+ subject { data_store.set_data(model, data) }
+
+ let(:data) { 'abc123' }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'sample data in database') }
+
+ it 'overwrites data' do
+ expect(data_store.data(model)).to eq('sample data in database')
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :database_without_data) }
+
+ it 'sets new data' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+ end
+
+ describe '#delete_data' do
+ subject { data_store.delete_data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :database_with_data, initial_data: 'sample data in database') }
+
+ it 'deletes data' do
+ expect(data_store.data(model)).to eq('sample data in database')
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :database_without_data) }
+
+ it 'does nothing' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+ end
+
+ describe '#keys' do
+ subject { data_store.keys(relation) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+
+ before do
+ create(:ci_build_trace_chunk, :database_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :database_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'returns empty array' do
+ is_expected.to eq([])
+ end
+ end
+end
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
new file mode 100644
index 00000000000..8f49190af13
--- /dev/null
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+describe Ci::BuildTraceChunks::Fog do
+ let(:data_store) { described_class.new }
+
+ before do
+ stub_artifacts_object_storage
+ end
+
+ describe '#available?' do
+ subject { data_store.available? }
+
+ context 'when object storage is enabled' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when object storage is disabled' do
+ before do
+ stub_artifacts_object_storage(enabled: false)
+ end
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#data' do
+ subject { data_store.data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
+
+ it 'returns the data' do
+ is_expected.to eq('sample data in fog')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) }
+
+ it 'returns nil' do
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+ end
+
+ describe '#set_data' do
+ subject { data_store.set_data(model, data) }
+
+ let(:data) { 'abc123' }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
+
+ it 'overwrites data' do
+ expect(data_store.data(model)).to eq('sample data in fog')
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) }
+
+ it 'sets new data' do
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+ end
+
+ describe '#delete_data' do
+ subject { data_store.delete_data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') }
+
+ it 'deletes data' do
+ expect(data_store.data(model)).to eq('sample data in fog')
+
+ subject
+
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) }
+
+ it 'does nothing' do
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+
+ subject
+
+ expect { data_store.data(model) }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+ end
+
+ describe '#keys' do
+ subject { data_store.keys(relation) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+
+ before do
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'returns keys' do
+ is_expected.to eq([[build.id, 0], [build.id, 1]])
+ end
+ end
+
+ describe '#delete_keys' do
+ subject { data_store.delete_keys(keys) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+ let(:keys) { data_store.keys(relation) }
+
+ before do
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'deletes multiple data' do
+ ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection|
+ expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body]).to be_present
+ expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body]).to be_present
+ end
+
+ subject
+
+ ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection|
+ expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body] }.to raise_error(Excon::Error::NotFound)
+ expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body] }.to raise_error(Excon::Error::NotFound)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb
new file mode 100644
index 00000000000..9da1e6a95ee
--- /dev/null
+++ b/spec/models/ci/build_trace_chunks/redis_spec.rb
@@ -0,0 +1,132 @@
+require 'spec_helper'
+
+describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do
+ let(:data_store) { described_class.new }
+
+ describe '#available?' do
+ subject { data_store.available? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ describe '#data' do
+ subject { data_store.data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'sample data in redis') }
+
+ it 'returns the data' do
+ is_expected.to eq('sample data in redis')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_without_data) }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#set_data' do
+ subject { data_store.set_data(model, data) }
+
+ let(:data) { 'abc123' }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'sample data in redis') }
+
+ it 'overwrites data' do
+ expect(data_store.data(model)).to eq('sample data in redis')
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_without_data) }
+
+ it 'sets new data' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to eq('abc123')
+ end
+ end
+ end
+
+ describe '#delete_data' do
+ subject { data_store.delete_data(model) }
+
+ context 'when data exists' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_with_data, initial_data: 'sample data in redis') }
+
+ it 'deletes data' do
+ expect(data_store.data(model)).to eq('sample data in redis')
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+
+ context 'when data does not exist' do
+ let(:model) { create(:ci_build_trace_chunk, :redis_without_data) }
+
+ it 'does nothing' do
+ expect(data_store.data(model)).to be_nil
+
+ subject
+
+ expect(data_store.data(model)).to be_nil
+ end
+ end
+ end
+
+ describe '#keys' do
+ subject { data_store.keys(relation) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+
+ before do
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'returns keys' do
+ is_expected.to eq([[build.id, 0], [build.id, 1]])
+ end
+ end
+
+ describe '#delete_keys' do
+ subject { data_store.delete_keys(keys) }
+
+ let(:build) { create(:ci_build) }
+ let(:relation) { build.trace_chunks }
+ let(:keys) { data_store.keys(relation) }
+
+ before do
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 0, build: build)
+ create(:ci_build_trace_chunk, :redis_with_data, chunk_index: 1, build: build)
+ end
+
+ it 'deletes multiple data' do
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:0")).to be_truthy
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:1")).to be_truthy
+ end
+
+ subject
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:0")).to be_falsy
+ expect(redis.exists("gitlab:ci:trace:#{build.id}:chunks:1")).to be_falsy
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index efddd4e7662..0fd7612c011 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -25,7 +25,7 @@ describe Ci::JobArtifact do
end
it 'does not schedule the migration' do
- expect(ObjectStorageUploadWorker).not_to receive(:perform_async)
+ expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
subject
end
@@ -163,7 +163,7 @@ describe Ci::JobArtifact do
expect(ProjectStatistics)
.not_to receive(:increment_statistic)
- project.update_attributes(pending_delete: true)
+ project.update(pending_delete: true)
project.destroy!
end
end
diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
index c16b245bea8..e5392fe0462 100644
--- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
+++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
@@ -4,8 +4,8 @@ describe BatchDestroyDependentAssociations do
class TestProject < ActiveRecord::Base
self.table_name = 'projects'
- has_many :builds, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
- has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+ has_many :builds, dependent: :destroy
+ has_many :notification_settings, as: :source, dependent: :delete_all
has_many :pages_domains
has_many :todos
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 2d75422ee68..da26d802688 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -370,4 +370,20 @@ describe CacheMarkdownField do
end
end
end
+
+ describe CacheMarkdownField::MarkdownEngine do
+ subject { lambda { |version| CacheMarkdownField::MarkdownEngine.from_version(version) } }
+
+ it 'returns :common_mark as a default' do
+ expect(subject.call(nil)).to eq :common_mark
+ end
+
+ it 'returns :common_mark' do
+ expect(subject.call(CacheMarkdownField::CACHE_COMMONMARK_VERSION)).to eq :common_mark
+ end
+
+ it 'returns :redcarpet' do
+ expect(subject.call(CacheMarkdownField::CACHE_REDCARPET_VERSION)).to eq :redcarpet
+ end
+ end
end
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index c6331c5ec15..f8c2e29fadd 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -52,7 +52,7 @@ describe CacheableAttributes do
describe '.cache_key' do
it 'excludes cache attributes' do
- expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Gitlab.migrations_hash}:#{Rails.version}")
+ expect(minimal_test_class.cache_key).to eq("TestClass:#{Gitlab::VERSION}:#{Rails.version}")
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 1cfd526834c..ec6374f3963 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -549,7 +549,7 @@ describe Issuable do
let(:project) { create(:project, namespace: group) }
let(:other_project) { create(:project) }
let(:owner) { create(:owner) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
@@ -558,7 +558,7 @@ describe Issuable do
before do
group.add_owner(owner)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
project.add_guest(contributor)
@@ -570,8 +570,8 @@ describe Issuable do
let(:merged_mr_other_project) { create(:merge_request, :merged, author: first_time_contributor, target_project: other_project, source_project: other_project) }
context "for merge requests" do
- it "is false for MASTER" do
- mr = create(:merge_request, author: master, target_project: project, source_project: project)
+ it "is false for MAINTAINER" do
+ mr = create(:merge_request, author: maintainer, target_project: project, source_project: project)
expect(mr).not_to be_first_contribution
end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index c73ea6aa94c..a9b237fa9ea 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -136,7 +136,7 @@ describe Issue, "Mentionable" do
expect(SystemNoteService).not_to receive(:cross_reference)
- issue.update_attributes(description: 'New description')
+ issue.update(description: 'New description')
issue.create_new_cross_references!
end
@@ -145,7 +145,7 @@ describe Issue, "Mentionable" do
expect(SystemNoteService).to receive(:cross_reference).with(issues[1], any_args)
- issue.update_attributes(description: issues[1].to_reference)
+ issue.update(description: issues[1].to_reference)
issue.create_new_cross_references!
end
@@ -155,7 +155,7 @@ describe Issue, "Mentionable" do
expect(SystemNoteService).to receive(:cross_reference).with(issues[1], any_args)
- note.update_attributes(note: issues[1].to_reference)
+ note.update(note: issues[1].to_reference)
note.create_new_cross_references!
end
end
diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb
index a62ca391e25..ce602337647 100644
--- a/spec/models/concerns/protected_ref_access_spec.rb
+++ b/spec/models/concerns/protected_ref_access_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe ProtectedRefAccess do
subject(:protected_ref_access) do
- create(:protected_branch, :masters_can_push).push_access_levels.first
+ create(:protected_branch, :maintainers_can_push).push_access_levels.first
end
let(:project) { protected_ref_access.project }
@@ -14,11 +14,11 @@ describe ProtectedRefAccess do
expect(protected_ref_access.check_access(admin)).to be_truthy
end
- it 'is true for masters' do
- master = create(:user)
- project.add_master(master)
+ it 'is true for maintainers' do
+ maintainer = create(:user)
+ project.add_maintainer(maintainer)
- expect(protected_ref_access.check_access(master)).to be_truthy
+ expect(protected_ref_access.check_access(maintainer)).to be_truthy
end
it 'is for developers of the project' do
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index 2f9f63ce7e0..97b046b0f21 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -190,7 +190,7 @@ describe Discussion, ResolvableDiscussion do
context "when the signed in user can push to the project" do
before do
- subject.project.add_master(current_user)
+ subject.project.add_maintainer(current_user)
end
it "returns true" do
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index 8cb50d7465c..ed3e28fbeca 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -29,7 +29,7 @@ describe Group, 'Routable' do
end
it 'updates route record on path change' do
- group.update_attributes(path: 'wow', name: 'much')
+ group.update(path: 'wow', name: 'much')
expect(group.route.path).to eq('wow')
expect(group.route.name).to eq('much')
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index e01906f4b6c..b335e0fbeb3 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -157,22 +157,4 @@ describe Deployment do
end
end
end
-
- describe '#stop_action?' do
- subject { deployment.stop_action? }
-
- context 'when no other actions' do
- let(:deployment) { build(:deployment) }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when matching action is defined' do
- let(:build) { create(:ci_build) }
- let(:deployment) { FactoryBot.build(:deployment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
-
- it { is_expected.to be_truthy }
- end
- end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 25d6597084c..c65e0b81451 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -170,8 +170,8 @@ describe Environment do
end
end
- describe '#stop_action?' do
- subject { environment.stop_action? }
+ describe '#stop_action_available?' do
+ subject { environment.stop_action_available? }
context 'when no other actions' do
it { is_expected.to be_falsey }
@@ -179,8 +179,17 @@ describe Environment do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
- let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
+
+ let!(:deployment) do
+ create(:deployment, environment: environment,
+ deployable: build,
+ on_stop: 'close_app')
+ end
+
+ let!(:close_action) do
+ create(:ci_build, :manual, pipeline: build.pipeline,
+ name: 'close_app')
+ end
context 'when environment is available' do
before do
@@ -562,7 +571,7 @@ describe Environment do
it "is not regenerated if name changes" do
original_slug = environment.slug
- environment.update_attributes!(name: environment.name.reverse)
+ environment.update!(name: environment.name.reverse)
expect(environment.slug).to eq(original_slug)
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 9fe1186a8c9..0729eb99e78 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -177,7 +177,7 @@ describe Group do
describe 'when the user has access to a group' do
before do
- group.add_user(user, Gitlab::Access::MASTER)
+ group.add_user(user, Gitlab::Access::MAINTAINER)
end
it { is_expected.to eq([group]) }
@@ -229,10 +229,10 @@ describe Group do
let(:user) { create(:user) }
before do
- group.add_user(user, GroupMember::MASTER)
+ group.add_user(user, GroupMember::MAINTAINER)
end
- it { expect(group.group_members.masters.map(&:user)).to include(user) }
+ it { expect(group.group_members.maintainers.map(&:user)).to include(user) }
end
describe '#add_users' do
@@ -254,7 +254,7 @@ describe Group do
let(:user) { create(:user) }
before do
- group.add_user(user, GroupMember::MASTER)
+ group.add_user(user, GroupMember::MAINTAINER)
end
it "is true if avatar is image" do
@@ -274,7 +274,7 @@ describe Group do
context 'when avatar file is uploaded' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'shows correct avatar url' do
@@ -317,7 +317,7 @@ describe Group do
end
it { expect(group.has_owner?(@members[:owner])).to be_truthy }
- it { expect(group.has_owner?(@members[:master])).to be_falsey }
+ it { expect(group.has_owner?(@members[:maintainer])).to be_falsey }
it { expect(group.has_owner?(@members[:developer])).to be_falsey }
it { expect(group.has_owner?(@members[:reporter])).to be_falsey }
it { expect(group.has_owner?(@members[:guest])).to be_falsey }
@@ -325,19 +325,19 @@ describe Group do
it { expect(group.has_owner?(nil)).to be_falsey }
end
- describe '#has_master?' do
+ describe '#has_maintainer?' do
before do
@members = setup_group_members(group)
- create(:group_member, :invited, :master, group: group)
+ create(:group_member, :invited, :maintainer, group: group)
end
- it { expect(group.has_master?(@members[:owner])).to be_falsey }
- it { expect(group.has_master?(@members[:master])).to be_truthy }
- it { expect(group.has_master?(@members[:developer])).to be_falsey }
- it { expect(group.has_master?(@members[:reporter])).to be_falsey }
- it { expect(group.has_master?(@members[:guest])).to be_falsey }
- it { expect(group.has_master?(@members[:requester])).to be_falsey }
- it { expect(group.has_master?(nil)).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:owner])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:maintainer])).to be_truthy }
+ it { expect(group.has_maintainer?(@members[:developer])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:reporter])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:guest])).to be_falsey }
+ it { expect(group.has_maintainer?(@members[:requester])).to be_falsey }
+ it { expect(group.has_maintainer?(nil)).to be_falsey }
end
describe '#lfs_enabled?' do
@@ -401,7 +401,7 @@ describe Group do
def setup_group_members(group)
members = {
owner: create(:user),
- master: create(:user),
+ maintainer: create(:user),
developer: create(:user),
reporter: create(:user),
guest: create(:user),
@@ -409,7 +409,7 @@ describe Group do
}
group.add_user(members[:owner], GroupMember::OWNER)
- group.add_user(members[:master], GroupMember::MASTER)
+ group.add_user(members[:maintainer], GroupMember::MAINTAINER)
group.add_user(members[:developer], GroupMember::DEVELOPER)
group.add_user(members[:reporter], GroupMember::REPORTER)
group.add_user(members[:guest], GroupMember::GUEST)
@@ -439,25 +439,25 @@ describe Group do
describe '#members_with_parents', :nested_groups do
let!(:group) { create(:group, :nested) }
- let!(:master) { group.parent.add_user(create(:user), GroupMember::MASTER) }
+ let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
it 'returns parents members' do
expect(group.members_with_parents).to include(developer)
- expect(group.members_with_parents).to include(master)
+ expect(group.members_with_parents).to include(maintainer)
end
end
describe '#direct_and_indirect_members', :nested_groups do
let!(:group) { create(:group, :nested) }
let!(:sub_group) { create(:group, parent: group) }
- let!(:master) { group.parent.add_user(create(:user), GroupMember::MASTER) }
+ let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
let!(:other_developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
it 'returns parents members' do
expect(group.direct_and_indirect_members).to include(developer)
- expect(group.direct_and_indirect_members).to include(master)
+ expect(group.direct_and_indirect_members).to include(maintainer)
end
it 'returns descendant members' do
@@ -539,14 +539,14 @@ describe Group do
describe '#user_ids_for_project_authorizations' do
it 'returns the user IDs for which to refresh authorizations' do
- master = create(:user)
+ maintainer = create(:user)
developer = create(:user)
- group.add_user(master, GroupMember::MASTER)
+ group.add_user(maintainer, GroupMember::MAINTAINER)
group.add_user(developer, GroupMember::DEVELOPER)
expect(group.user_ids_for_project_authorizations)
- .to include(master.id, developer.id)
+ .to include(maintainer.id, developer.id)
end
end
@@ -617,7 +617,7 @@ describe Group do
expect(group).to receive(:system_hook_service).and_return(system_hook_service)
expect(system_hook_service).to receive(:execute_hooks_for).with(group, :rename)
- group.update_attributes!(path: new_path)
+ group.update!(path: new_path)
end
end
@@ -625,7 +625,7 @@ describe Group do
it 'does not trigger system hook' do
expect(group).not_to receive(:system_hook_service)
- group.update_attributes!(name: 'new name')
+ group.update!(name: 'new name')
end
end
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 8bc45715dcd..01129df1107 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -63,7 +63,7 @@ describe SystemHook do
end
it "project_create hook" do
- project.add_master(user)
+ project.add_maintainer(user)
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
@@ -72,7 +72,7 @@ describe SystemHook do
end
it "project_destroy hook" do
- project.add_master(user)
+ project.add_maintainer(user)
project.project_members.destroy_all
expect(WebMock).to have_requested(:post, system_hook.url).with(
@@ -100,7 +100,7 @@ describe SystemHook do
end
it 'group member create hook' do
- group.add_master(user)
+ group.add_maintainer(user)
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_group/,
@@ -109,7 +109,7 @@ describe SystemHook do
end
it 'group member destroy hook' do
- group.add_master(user)
+ group.add_maintainer(user)
group.group_members.destroy_all
expect(WebMock).to have_requested(:post, system_hook.url).with(
diff --git a/spec/models/import_export_upload_spec.rb b/spec/models/import_export_upload_spec.rb
new file mode 100644
index 00000000000..58af84b8a08
--- /dev/null
+++ b/spec/models/import_export_upload_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe ImportExportUpload do
+ subject { described_class.new(project: create(:project)) }
+
+ shared_examples 'stores the Import/Export file' do |method|
+ it 'stores the import file' do
+ subject.public_send("#{method}=", fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+
+ subject.save!
+
+ url = "/uploads/-/system/import_export_upload/#{method}/#{subject.id}/project_export.tar.gz"
+
+ expect(subject.public_send(method).url).to eq(url)
+ end
+ end
+
+ context 'import' do
+ it_behaves_like 'stores the Import/Export file', :import_file
+ end
+
+ context 'export' do
+ it_behaves_like 'stores the Import/Export file', :export_file
+ end
+end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index e818fbeb9cf..84edfc3ff00 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -669,7 +669,7 @@ describe Issue do
context 'when the user is the project owner' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'returns true for a regular issue' do
diff --git a/spec/models/lfs_file_lock_spec.rb b/spec/models/lfs_file_lock_spec.rb
index ce87b01b49c..e74f342d3eb 100644
--- a/spec/models/lfs_file_lock_spec.rb
+++ b/spec/models/lfs_file_lock_spec.rb
@@ -13,13 +13,13 @@ describe LfsFileLock do
describe '#can_be_unlocked_by?' do
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
before do
project = lfs_file_lock.project
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context "when it's forced" do
@@ -29,8 +29,8 @@ describe LfsFileLock do
expect(lfs_file_lock.can_be_unlocked_by?(user, true)).to eq(true)
end
- it 'can be unlocked by a master' do
- expect(lfs_file_lock.can_be_unlocked_by?(master, true)).to eq(true)
+ it 'can be unlocked by a maintainer' do
+ expect(lfs_file_lock.can_be_unlocked_by?(maintainer, true)).to eq(true)
end
it "can't be unlocked by other user" do
@@ -45,8 +45,8 @@ describe LfsFileLock do
expect(lfs_file_lock.can_be_unlocked_by?(user)).to eq(true)
end
- it "can't be unlocked by a master" do
- expect(lfs_file_lock.can_be_unlocked_by?(master)).to eq(false)
+ it "can't be unlocked by a maintainer" do
+ expect(lfs_file_lock.can_be_unlocked_by?(maintainer)).to eq(false)
end
it "can't be unlocked by other user" do
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index c64cdf8f812..fca1b1f90d9 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -62,16 +62,16 @@ describe Member do
@owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id)
- @master_user = create(:user).tap { |u| project.add_master(u) }
- @master = project.members.find_by(user_id: @master_user.id)
+ @maintainer_user = create(:user).tap { |u| project.add_maintainer(u) }
+ @maintainer = project.members.find_by(user_id: @maintainer_user.id)
@blocked_user = create(:user).tap do |u|
- project.add_master(u)
+ project.add_maintainer(u)
project.add_developer(u)
u.block!
end
- @blocked_master = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::MASTER)
+ @blocked_maintainer = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::MAINTAINER)
@blocked_developer = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::DEVELOPER)
@invited_member = create(:project_member, :developer,
@@ -95,10 +95,10 @@ describe Member do
describe '.access_for_user_ids' do
it 'returns the right access levels' do
- users = [@owner_user.id, @master_user.id, @blocked_user.id]
+ users = [@owner_user.id, @maintainer_user.id, @blocked_user.id]
expected = {
@owner_user.id => Gitlab::Access::OWNER,
- @master_user.id => Gitlab::Access::MASTER
+ @maintainer_user.id => Gitlab::Access::MAINTAINER
}
expect(described_class.access_for_user_ids(users)).to eq(expected)
@@ -106,7 +106,7 @@ describe Member do
end
describe '.invite' do
- it { expect(described_class.invite).not_to include @master }
+ it { expect(described_class.invite).not_to include @maintainer }
it { expect(described_class.invite).to include @invited_member }
it { expect(described_class.invite).not_to include @accepted_invite_member }
it { expect(described_class.invite).not_to include @requested_member }
@@ -114,7 +114,7 @@ describe Member do
end
describe '.non_invite' do
- it { expect(described_class.non_invite).to include @master }
+ it { expect(described_class.non_invite).to include @maintainer }
it { expect(described_class.non_invite).not_to include @invited_member }
it { expect(described_class.non_invite).to include @accepted_invite_member }
it { expect(described_class.non_invite).to include @requested_member }
@@ -122,7 +122,7 @@ describe Member do
end
describe '.request' do
- it { expect(described_class.request).not_to include @master }
+ it { expect(described_class.request).not_to include @maintainer }
it { expect(described_class.request).not_to include @invited_member }
it { expect(described_class.request).not_to include @accepted_invite_member }
it { expect(described_class.request).to include @requested_member }
@@ -130,7 +130,7 @@ describe Member do
end
describe '.non_request' do
- it { expect(described_class.non_request).to include @master }
+ it { expect(described_class.non_request).to include @maintainer }
it { expect(described_class.non_request).to include @invited_member }
it { expect(described_class.non_request).to include @accepted_invite_member }
it { expect(described_class.non_request).not_to include @requested_member }
@@ -141,35 +141,35 @@ describe Member do
subject { described_class.developers.to_a }
it { is_expected.not_to include @owner }
- it { is_expected.not_to include @master }
+ it { is_expected.not_to include @maintainer }
it { is_expected.to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
- it { is_expected.not_to include @blocked_master }
+ it { is_expected.not_to include @blocked_maintainer }
it { is_expected.not_to include @blocked_developer }
end
- describe '.owners_and_masters' do
- it { expect(described_class.owners_and_masters).to include @owner }
- it { expect(described_class.owners_and_masters).to include @master }
- it { expect(described_class.owners_and_masters).not_to include @invited_member }
- it { expect(described_class.owners_and_masters).not_to include @accepted_invite_member }
- it { expect(described_class.owners_and_masters).not_to include @requested_member }
- it { expect(described_class.owners_and_masters).not_to include @accepted_request_member }
- it { expect(described_class.owners_and_masters).not_to include @blocked_master }
+ describe '.owners_and_maintainers' do
+ it { expect(described_class.owners_and_maintainers).to include @owner }
+ it { expect(described_class.owners_and_maintainers).to include @maintainer }
+ it { expect(described_class.owners_and_maintainers).not_to include @invited_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @accepted_invite_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @requested_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @accepted_request_member }
+ it { expect(described_class.owners_and_maintainers).not_to include @blocked_maintainer }
end
describe '.has_access' do
subject { described_class.has_access.to_a }
it { is_expected.to include @owner }
- it { is_expected.to include @master }
+ it { is_expected.to include @maintainer }
it { is_expected.to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
- it { is_expected.not_to include @blocked_master }
+ it { is_expected.not_to include @blocked_maintainer }
it { is_expected.not_to include @blocked_developer }
end
end
@@ -187,20 +187,20 @@ describe Member do
let!(:admin) { create(:admin) }
it 'returns a <Source>Member object' do
- member = described_class.add_user(source, user, :master)
+ member = described_class.add_user(source, user, :maintainer)
expect(member).to be_a "#{source_type.classify}Member".constantize
expect(member).to be_persisted
end
it 'sets members.created_by to the given current_user' do
- member = described_class.add_user(source, user, :master, current_user: admin)
+ member = described_class.add_user(source, user, :maintainer, current_user: admin)
expect(member.created_by).to eq(admin)
end
it 'sets members.expires_at to the given expires_at' do
- member = described_class.add_user(source, user, :master, expires_at: Date.new(2016, 9, 22))
+ member = described_class.add_user(source, user, :maintainer, expires_at: Date.new(2016, 9, 22))
expect(member.expires_at).to eq(Date.new(2016, 9, 22))
end
@@ -230,7 +230,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user.id, :master)
+ described_class.add_user(source, user.id, :maintainer)
expect(source.users.reload).to include(user)
end
@@ -240,7 +240,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, 42, :master)
+ described_class.add_user(source, 42, :maintainer)
expect(source.users.reload).not_to include(user)
end
@@ -250,7 +250,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user, :master)
+ described_class.add_user(source, user, :maintainer)
expect(source.users.reload).to include(user)
end
@@ -265,7 +265,7 @@ describe Member do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- expect { described_class.add_user(source, user, :master) }
+ expect { described_class.add_user(source, user, :maintainer) }
.to raise_error(Gitlab::Access::AccessDeniedError)
expect(source.users.reload).not_to include(user)
@@ -277,7 +277,7 @@ describe Member do
it 'adds the user as a member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user.email, :master)
+ described_class.add_user(source, user.email, :maintainer)
expect(source.users.reload).to include(user)
end
@@ -287,7 +287,7 @@ describe Member do
it 'creates an invited member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, 'user@example.com', :master)
+ described_class.add_user(source, 'user@example.com', :maintainer)
expect(source.members.invite.pluck(:invite_email)).to include('user@example.com')
end
@@ -298,7 +298,7 @@ describe Member do
it 'creates the member' do
expect(source.users).not_to include(user)
- described_class.add_user(source, user, :master, current_user: admin)
+ described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.users.reload).to include(user)
end
@@ -312,7 +312,7 @@ describe Member do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- described_class.add_user(source, user, :master, current_user: admin)
+ described_class.add_user(source, user, :maintainer, current_user: admin)
expect(source.users.reload).to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
@@ -324,7 +324,7 @@ describe Member do
it 'does not create the member' do
expect(source.users).not_to include(user)
- member = described_class.add_user(source, user, :master, current_user: user)
+ member = described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.users.reload).not_to include(user)
expect(member).not_to be_persisted
@@ -339,7 +339,7 @@ describe Member do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- described_class.add_user(source, user, :master, current_user: user)
+ described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.users.reload).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
@@ -356,9 +356,9 @@ describe Member do
it 'updates the member' do
expect(source.users).to include(user)
- described_class.add_user(source, user, :master)
+ described_class.add_user(source, user, :maintainer)
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MASTER)
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -366,9 +366,9 @@ describe Member do
it 'updates the member' do
expect(source.users).to include(user)
- described_class.add_user(source, user, :master, current_user: admin)
+ described_class.add_user(source, user, :maintainer, current_user: admin)
- expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MASTER)
+ expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -376,7 +376,7 @@ describe Member do
it 'does not update the member' do
expect(source.users).to include(user)
- described_class.add_user(source, user, :master, current_user: user)
+ described_class.add_user(source, user, :maintainer, current_user: user)
expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER)
end
@@ -395,7 +395,7 @@ describe Member do
let(:user2) { create(:user) }
it 'returns a <Source>Member objects' do
- members = described_class.add_users(source, [user1, user2], :master)
+ members = described_class.add_users(source, [user1, user2], :maintainer)
expect(members).to be_a Array
expect(members.size).to eq(2)
@@ -404,7 +404,7 @@ describe Member do
end
it 'returns an empty array' do
- members = described_class.add_users(source, [], :master)
+ members = described_class.add_users(source, [], :maintainer)
expect(members).to be_a Array
expect(members).to be_empty
@@ -413,7 +413,7 @@ describe Member do
it 'supports differents formats' do
list = ['joe@local.test', admin, user1.id, user2.id.to_s]
- members = described_class.add_users(source, list, :master)
+ members = described_class.add_users(source, list, :maintainer)
expect(members.size).to eq(4)
expect(members.first).to be_invite
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index ffc78015f94..97959ed4304 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -21,7 +21,7 @@ describe GroupMember do
described_class.add_users(
group,
[users.first.id, users.second],
- described_class::MASTER
+ described_class::MAINTAINER
)
expect(group.users).to include(users.first, users.second)
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 574eb468e4c..334d4f95f53 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -28,7 +28,7 @@ describe ProjectMember do
expect(project.users).not_to include(user)
- described_class.add_user(project, user, :master, current_user: project.owner)
+ described_class.add_user(project, user, :maintainer, current_user: project.owner)
expect(project.users.reload).to include(user)
end
@@ -41,9 +41,9 @@ describe ProjectMember do
end
describe "#destroy" do
- let(:owner) { create(:project_member, access_level: ProjectMember::MASTER) }
+ let(:owner) { create(:project_member, access_level: ProjectMember::MAINTAINER) }
let(:project) { owner.project }
- let(:master) { create(:project_member, project: project) }
+ let(:maintainer) { create(:project_member, project: project) }
it "creates an expired event when left due to expiry" do
expired = create(:project_member, project: project, expires_at: Time.now - 6.days)
@@ -52,7 +52,7 @@ describe ProjectMember do
end
it "creates a left event when left due to leave" do
- master.destroy
+ maintainer.destroy
expect(Event.recent.first.action).to eq(Event::LEFT)
end
end
@@ -95,7 +95,7 @@ describe ProjectMember do
described_class.add_users_to_projects(
[projects.first.id, projects.second.id],
[users.first.id, users.second],
- described_class::MASTER)
+ described_class::MAINTAINER)
expect(projects.first.users).to include(users.first)
expect(projects.first.users).to include(users.second)
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 8c6b411ec9a..b0d9d03bf6c 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -724,7 +724,7 @@ describe MergeRequest do
subject { merge_request }
before do
- subject.source_project.add_master(user)
+ subject.source_project.add_maintainer(user)
end
it "can't be removed when its a protected branch" do
@@ -1199,7 +1199,7 @@ describe MergeRequest do
end
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
ProcessCommitWorker.new.perform(project.id,
current_user.id,
@@ -1569,8 +1569,8 @@ describe MergeRequest do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
- merge_request.source_project.add_master(user)
- merge_request.target_project.add_master(user)
+ merge_request.source_project.add_maintainer(user)
+ merge_request.target_project.add_maintainer(user)
end
context 'with multiple environments' do
@@ -1891,7 +1891,7 @@ describe MergeRequest do
end
it 'returns false if the merge request is merged' do
- merge_request.update_attributes(state: 'merged')
+ merge_request.update(state: 'merged')
expect(merge_request.reload.reopenable?).to be_falsey
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 70f1a1c8b38..c1b385aaf76 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -200,7 +200,7 @@ describe Namespace do
end
it "moves dir if path changed" do
- namespace.update_attributes(path: namespace.full_path + '_new')
+ namespace.update(path: namespace.full_path + '_new')
expect(gitlab_shell.exists?(project.repository_storage, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
@@ -279,7 +279,7 @@ describe Namespace do
it "repository directory remains unchanged if path changed" do
before_disk_path = project.disk_path
- namespace.update_attributes(path: namespace.full_path + '_new')
+ namespace.update(path: namespace.full_path + '_new')
expect(before_disk_path).to eq(project.disk_path)
expect(gitlab_shell.exists?(project.repository_storage, "#{project.disk_path}.git")).to be_truthy
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index a2cb716cb93..947be44c903 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -144,8 +144,8 @@ describe Note do
describe 'admin' do
before do
@p1.project_members.create(user: @u1, access_level: ProjectMember::REPORTER)
- @p1.project_members.create(user: @u2, access_level: ProjectMember::MASTER)
- @p2.project_members.create(user: @u3, access_level: ProjectMember::MASTER)
+ @p1.project_members.create(user: @u2, access_level: ProjectMember::MAINTAINER)
+ @p2.project_members.create(user: @u3, access_level: ProjectMember::MAINTAINER)
end
it { expect(Ability.allowed?(@u1, :admin_note, @p1)).to be_falsey }
@@ -225,7 +225,7 @@ describe Note do
describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) }
- let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_master(private_user) } }
+ let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_maintainer(private_user) } }
let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) }
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 12681a147b4..77c475b9f52 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe NotificationSetting do
1.upto(4) do |i|
setting = create(:notification_setting, user: user)
- setting.project.update_attributes(pending_delete: true) if i.even?
+ setting.project.update(pending_delete: true) if i.even?
end
end
@@ -93,4 +93,10 @@ RSpec.describe NotificationSetting do
end
end
end
+
+ context 'email events' do
+ it 'includes EXCLUDED_WATCHER_EVENTS in EMAIL_EVENTS' do
+ expect(described_class::EMAIL_EVENTS).to include(*described_class::EXCLUDED_WATCHER_EVENTS)
+ end
+ end
end
diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb
index 9e7e525b2c0..c289ee0859a 100644
--- a/spec/models/project_authorization_spec.rb
+++ b/spec/models/project_authorization_spec.rb
@@ -8,15 +8,15 @@ describe ProjectAuthorization do
describe '.insert_authorizations' do
it 'inserts the authorizations' do
described_class
- .insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]])
+ .insert_authorizations([[user.id, project1.id, Gitlab::Access::MAINTAINER]])
expect(user.project_authorizations.count).to eq(1)
end
it 'inserts rows in batches' do
described_class.insert_authorizations([
- [user.id, project1.id, Gitlab::Access::MASTER],
- [user.id, project2.id, Gitlab::Access::MASTER]
+ [user.id, project1.id, Gitlab::Access::MAINTAINER],
+ [user.id, project2.id, Gitlab::Access::MAINTAINER]
], 1)
expect(user.project_authorizations.count).to eq(2)
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 63c6fbda3f2..cd7f77024da 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -77,7 +77,7 @@ describe ProjectFeature do
context 'repository related features' do
before do
- project.project_feature.update_attributes(
+ project.project_feature.update(
merge_requests_access_level: ProjectFeature::DISABLED,
builds_access_level: ProjectFeature::DISABLED,
repository_access_level: ProjectFeature::PRIVATE
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c3aa6cd6fed..d200e5f2e42 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -149,23 +149,25 @@ describe Project do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
-
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_length_of(:path).is_at_most(255) }
-
it { is_expected.to validate_length_of(:description).is_at_most(2000) }
-
it { is_expected.to validate_length_of(:ci_config_path).is_at_most(255) }
it { is_expected.to allow_value('').for(:ci_config_path) }
it { is_expected.not_to allow_value('test/../foo').for(:ci_config_path) }
it { is_expected.not_to allow_value('/test/foo').for(:ci_config_path) }
-
it { is_expected.to validate_presence_of(:creator) }
-
it { is_expected.to validate_presence_of(:namespace) }
-
it { is_expected.to validate_presence_of(:repository_storage) }
+ it 'validates build timeout constraints' do
+ is_expected.to validate_numericality_of(:build_timeout)
+ .only_integer
+ .is_greater_than_or_equal_to(10.minutes)
+ .is_less_than(1.month)
+ .with_message('needs to be beetween 10 minutes and 1 month')
+ end
+
it 'does not allow new projects beyond user limits' do
project2 = build(:project)
allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
@@ -336,7 +338,7 @@ describe Project do
end
describe 'delegation' do
- [:add_guest, :add_reporter, :add_developer, :add_master, :add_user, :add_users].each do |method|
+ [:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_user, :add_users].each do |method|
it { is_expected.to delegate_method(method).to(:team) }
end
@@ -567,15 +569,15 @@ describe Project do
end
it 'returns the most recent timestamp' do
- project.update_attributes(updated_at: nil,
- last_activity_at: timestamp,
- last_repository_updated_at: timestamp - 1.hour)
+ project.update(updated_at: nil,
+ last_activity_at: timestamp,
+ last_repository_updated_at: timestamp - 1.hour)
expect(project.last_activity_date).to be_like_time(timestamp)
- project.update_attributes(updated_at: timestamp,
- last_activity_at: timestamp - 1.hour,
- last_repository_updated_at: nil)
+ project.update(updated_at: timestamp,
+ last_activity_at: timestamp - 1.hour,
+ last_repository_updated_at: nil)
expect(project.last_activity_date).to be_like_time(timestamp)
end
@@ -1130,7 +1132,7 @@ describe Project do
describe 'when a user has access to a project' do
before do
- project.add_user(user, Gitlab::Access::MASTER)
+ project.add_user(user, Gitlab::Access::MAINTAINER)
end
it { is_expected.to eq([project]) }
@@ -1768,7 +1770,7 @@ describe Project do
it 'resets project import_error' do
error_message = 'Some error'
mirror = create(:project_empty_repo, :import_started)
- mirror.import_state.update_attributes(last_error: error_message)
+ mirror.import_state.update(last_error: error_message)
expect { mirror.import_finish }.to change { mirror.import_error }.from(error_message).to(nil)
end
@@ -1929,7 +1931,7 @@ describe Project do
end
it 'returns false when remote mirror is disabled' do
- project.remote_mirrors.first.update_attributes(enabled: false)
+ project.remote_mirrors.first.update(enabled: false)
is_expected.to be_falsy
end
@@ -1959,7 +1961,7 @@ describe Project do
end
it 'does not sync disabled remote mirrors' do
- project.remote_mirrors.first.update_attributes(enabled: false)
+ project.remote_mirrors.first.update(enabled: false)
expect_any_instance_of(RemoteMirror).not_to receive(:sync)
@@ -2782,6 +2784,10 @@ describe Project do
let(:legacy_project) { create(:project, :legacy_storage, :with_export) }
let(:project) { create(:project, :with_export) }
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
+
it 'removes the exports directory for the project' do
expect(File.exist?(project.export_path)).to be_truthy
@@ -2830,12 +2836,14 @@ describe Project do
let(:project) { create(:project, :with_export) }
it 'removes the exported project file' do
+ stub_feature_flags(import_export_object_storage: false)
+
exported_file = project.export_project_path
expect(File.exist?(exported_file)).to be_truthy
- allow(FileUtils).to receive(:rm_f).and_call_original
- expect(FileUtils).to receive(:rm_f).with(exported_file).and_call_original
+ allow(FileUtils).to receive(:rm_rf).and_call_original
+ expect(FileUtils).to receive(:rm_rf).with(exported_file).and_call_original
project.remove_exported_project_file
@@ -3490,8 +3498,8 @@ describe Project do
expect(project.protected_branches).not_to be_empty
expect(project.default_branch).to eq(project.protected_branches.first.name)
- expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
- expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
end
end
@@ -3727,7 +3735,7 @@ describe Project do
end
it 'does not allow access if the user cannot merge the merge request' do
- create(:protected_branch, :masters_can_push, project: target_project, name: 'target-branch')
+ create(:protected_branch, :maintainers_can_push, project: target_project, name: 'target-branch')
expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
.to be_falsy
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 9978f3e9566..c4af17f4726 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -1,7 +1,7 @@
require "spec_helper"
describe ProjectTeam do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:nonmember) { create(:user) }
@@ -10,23 +10,23 @@ describe ProjectTeam do
let(:project) { create(:project) }
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
end
describe 'members collection' do
- it { expect(project.team.masters).to include(master) }
- it { expect(project.team.masters).not_to include(guest) }
- it { expect(project.team.masters).not_to include(reporter) }
- it { expect(project.team.masters).not_to include(nonmember) }
+ it { expect(project.team.maintainers).to include(maintainer) }
+ it { expect(project.team.maintainers).not_to include(guest) }
+ it { expect(project.team.maintainers).not_to include(reporter) }
+ it { expect(project.team.maintainers).not_to include(nonmember) }
end
describe 'access methods' do
- it { expect(project.team.master?(master)).to be_truthy }
- it { expect(project.team.master?(guest)).to be_falsey }
- it { expect(project.team.master?(reporter)).to be_falsey }
- it { expect(project.team.master?(nonmember)).to be_falsey }
+ it { expect(project.team.maintainer?(maintainer)).to be_truthy }
+ it { expect(project.team.maintainer?(guest)).to be_falsey }
+ it { expect(project.team.maintainer?(reporter)).to be_falsey }
+ it { expect(project.team.maintainer?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy }
it { expect(project.team.member?(reporter, Gitlab::Access::REPORTER)).to be_truthy }
@@ -40,35 +40,35 @@ describe ProjectTeam do
let!(:project) { create(:project, group: group) }
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
group.add_guest(guest)
# If user is a group and a project member - GitLab uses highest permission
- # So we add group guest as master and add group master as guest
+ # So we add group guest as maintainer and add group maintainer as guest
# to this project to test highest access
- project.add_master(guest)
- project.add_guest(master)
+ project.add_maintainer(guest)
+ project.add_guest(maintainer)
end
describe 'members collection' do
it { expect(project.team.reporters).to include(reporter) }
- it { expect(project.team.masters).to include(master) }
- it { expect(project.team.masters).to include(guest) }
- it { expect(project.team.masters).not_to include(reporter) }
- it { expect(project.team.masters).not_to include(nonmember) }
+ it { expect(project.team.maintainers).to include(maintainer) }
+ it { expect(project.team.maintainers).to include(guest) }
+ it { expect(project.team.maintainers).not_to include(reporter) }
+ it { expect(project.team.maintainers).not_to include(nonmember) }
end
describe 'access methods' do
it { expect(project.team.reporter?(reporter)).to be_truthy }
- it { expect(project.team.master?(master)).to be_truthy }
- it { expect(project.team.master?(guest)).to be_truthy }
- it { expect(project.team.master?(reporter)).to be_falsey }
- it { expect(project.team.master?(nonmember)).to be_falsey }
+ it { expect(project.team.maintainer?(maintainer)).to be_truthy }
+ it { expect(project.team.maintainer?(guest)).to be_truthy }
+ it { expect(project.team.maintainer?(reporter)).to be_falsey }
+ it { expect(project.team.maintainer?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy }
- it { expect(project.team.member?(guest, Gitlab::Access::MASTER)).to be_truthy }
- it { expect(project.team.member?(reporter, Gitlab::Access::MASTER)).to be_falsey }
+ it { expect(project.team.member?(guest, Gitlab::Access::MAINTAINER)).to be_truthy }
+ it { expect(project.team.member?(reporter, Gitlab::Access::MAINTAINER)).to be_falsey }
it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end
end
@@ -145,13 +145,13 @@ describe ProjectTeam do
let(:requester) { create(:user) }
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
project.request_access(requester)
end
- it { expect(project.team.find_member(master.id)).to be_a(ProjectMember) }
+ it { expect(project.team.find_member(maintainer.id)).to be_a(ProjectMember) }
it { expect(project.team.find_member(reporter.id)).to be_a(ProjectMember) }
it { expect(project.team.find_member(guest.id)).to be_a(ProjectMember) }
it { expect(project.team.find_member(nonmember.id)).to be_nil }
@@ -164,13 +164,13 @@ describe ProjectTeam do
let(:requester) { create(:user) }
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
group.add_guest(guest)
group.request_access(requester)
end
- it { expect(project.team.find_member(master.id)).to be_a(GroupMember) }
+ it { expect(project.team.find_member(maintainer.id)).to be_a(GroupMember) }
it { expect(project.team.find_member(reporter.id)).to be_a(GroupMember) }
it { expect(project.team.find_member(guest.id)).to be_a(GroupMember) }
it { expect(project.team.find_member(nonmember.id)).to be_nil }
@@ -184,7 +184,7 @@ describe ProjectTeam do
group = create(:group)
project = create(:project, namespace: group)
- group.add_master(user)
+ group.add_maintainer(user)
expect(project.team.human_max_access(user.id)).to eq 'Maintainer'
end
@@ -210,13 +210,13 @@ describe ProjectTeam do
context 'when project is not shared with group' do
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
project.request_access(requester)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::MAINTAINER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -230,11 +230,11 @@ describe ProjectTeam do
group: group,
group_access: Gitlab::Access::DEVELOPER)
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::DEVELOPER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -244,7 +244,7 @@ describe ProjectTeam do
project.namespace.update(share_with_group_lock: true)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::NO_ACCESS) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::NO_ACCESS) }
end
end
@@ -257,13 +257,13 @@ describe ProjectTeam do
end
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_reporter(reporter)
group.add_guest(guest)
group.request_access(requester)
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+ it { expect(project.team.max_member_access(maintainer.id)).to eq(Gitlab::Access::MAINTAINER) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
it { expect(project.team.max_member_access(nonmember.id)).to eq(Gitlab::Access::NO_ACCESS) }
@@ -274,7 +274,7 @@ describe ProjectTeam do
describe '#member?' do
let(:group) { create(:group) }
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:personal_project) do
create(:project, namespace: developer.namespace)
@@ -288,11 +288,11 @@ describe ProjectTeam do
let(:shared_project) { create(:project) }
before do
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_developer(developer)
members_project.add_developer(developer)
- members_project.add_master(master)
+ members_project.add_maintainer(maintainer)
create(:project_group_link, project: shared_project, group: group)
end
@@ -318,14 +318,14 @@ describe ProjectTeam do
end
it 'checks for the correct minimum level access' do
- expect(group_project.team.member?(developer, Gitlab::Access::MASTER)).to be(false)
- expect(group_project.team.member?(master, Gitlab::Access::MASTER)).to be(true)
- expect(members_project.team.member?(developer, Gitlab::Access::MASTER)).to be(false)
- expect(members_project.team.member?(master, Gitlab::Access::MASTER)).to be(true)
- expect(shared_project.team.member?(developer, Gitlab::Access::MASTER)).to be(false)
- expect(shared_project.team.member?(master, Gitlab::Access::MASTER)).to be(false)
+ expect(group_project.team.member?(developer, Gitlab::Access::MAINTAINER)).to be(false)
+ expect(group_project.team.member?(maintainer, Gitlab::Access::MAINTAINER)).to be(true)
+ expect(members_project.team.member?(developer, Gitlab::Access::MAINTAINER)).to be(false)
+ expect(members_project.team.member?(maintainer, Gitlab::Access::MAINTAINER)).to be(true)
+ expect(shared_project.team.member?(developer, Gitlab::Access::MAINTAINER)).to be(false)
+ expect(shared_project.team.member?(maintainer, Gitlab::Access::MAINTAINER)).to be(false)
expect(shared_project.team.member?(developer, Gitlab::Access::DEVELOPER)).to be(true)
- expect(shared_project.team.member?(master, Gitlab::Access::DEVELOPER)).to be(true)
+ expect(shared_project.team.member?(maintainer, Gitlab::Access::DEVELOPER)).to be(true)
end
end
@@ -334,7 +334,7 @@ describe ProjectTeam do
let(:group) { create(:group) }
let(:second_group) { create(:group) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
@@ -347,23 +347,23 @@ describe ProjectTeam do
let(:second_user_without_access) { create(:user) }
let(:users) do
- [master, reporter, promoted_guest, guest, group_developer, second_developer, user_without_access].map(&:id)
+ [maintainer, reporter, promoted_guest, guest, group_developer, second_developer, user_without_access].map(&:id)
end
let(:expected) do
{
- master.id => Gitlab::Access::MASTER,
+ maintainer.id => Gitlab::Access::MAINTAINER,
reporter.id => Gitlab::Access::REPORTER,
promoted_guest.id => Gitlab::Access::DEVELOPER,
guest.id => Gitlab::Access::GUEST,
group_developer.id => Gitlab::Access::DEVELOPER,
- second_developer.id => Gitlab::Access::MASTER,
+ second_developer.id => Gitlab::Access::MAINTAINER,
user_without_access.id => Gitlab::Access::NO_ACCESS
}
end
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(promoted_guest)
project.add_guest(guest)
@@ -373,16 +373,16 @@ describe ProjectTeam do
group_access: Gitlab::Access::DEVELOPER
)
- group.add_master(promoted_guest)
+ group.add_maintainer(promoted_guest)
group.add_developer(group_developer)
group.add_developer(second_developer)
project.project_group_links.create(
group: second_group,
- group_access: Gitlab::Access::MASTER
+ group_access: Gitlab::Access::MAINTAINER
)
- second_group.add_master(second_developer)
+ second_group.add_maintainer(second_developer)
end
it 'returns correct roles for different users' do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index a3c20b3b3c1..528f5b610d7 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require "spec_helper"
describe ProjectWiki do
@@ -10,7 +11,6 @@ describe ProjectWiki do
subject { project_wiki }
- it { is_expected.to delegate_method(:empty?).to :pages }
it { is_expected.to delegate_method(:repository_storage).to :project }
it { is_expected.to delegate_method(:hashed_storage?).to :project }
@@ -92,11 +92,19 @@ describe ProjectWiki do
context "when the wiki has pages" do
before do
project_wiki.create_page("index", "This is an awesome new Gollum Wiki")
+ project_wiki.create_page("another-page", "This is another page")
end
describe '#empty?' do
subject { super().empty? }
it { is_expected.to be_falsey }
+
+ # Re-enable this when https://gitlab.com/gitlab-org/gitaly/issues/1204 is fixed
+ xit 'only instantiates a Wiki page once' do
+ expect(WikiPage).to receive(:new).once.and_call_original
+
+ subject
+ end
end
end
end
@@ -181,6 +189,22 @@ describe ProjectWiki do
end
end
+ describe '#find_sidebar' do
+ before do
+ create_page(described_class::SIDEBAR, 'This is an awesome Sidebar')
+ end
+
+ after do
+ subject.pages.each { |page| destroy_page(page.page) }
+ end
+
+ it 'finds the page defined as _sidebar' do
+ page = subject.find_page('_sidebar')
+
+ expect(page.content).to eq('This is an awesome Sidebar')
+ end
+ end
+
describe '#find_file' do
shared_examples 'finding a wiki file' do
let(:image) { File.open(Rails.root.join('spec', 'fixtures', 'big-image.png')) }
diff --git a/spec/models/protected_branch/merge_access_level_spec.rb b/spec/models/protected_branch/merge_access_level_spec.rb
index f70503eadbc..612e4a0e332 100644
--- a/spec/models/protected_branch/merge_access_level_spec.rb
+++ b/spec/models/protected_branch/merge_access_level_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
describe ProtectedBranch::MergeAccessLevel do
- it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MASTER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
+ it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
end
diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb
index f161f345761..9ccdc22fd41 100644
--- a/spec/models/protected_branch/push_access_level_spec.rb
+++ b/spec/models/protected_branch/push_access_level_spec.rb
@@ -1,5 +1,5 @@
require 'spec_helper'
describe ProtectedBranch::PushAccessLevel do
- it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MASTER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
+ it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
end
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 3597b080021..c2ef0435c8e 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -85,7 +85,7 @@ describe RemoteMirror do
expect(RepositoryRemoveRemoteWorker).to receive(:perform_async).with(mirror.project.id, mirror.remote_name).and_call_original
- mirror.update_attributes(url: 'http://test.com')
+ mirror.update(url: 'http://test.com')
end
end
end
@@ -167,7 +167,7 @@ describe RemoteMirror do
context 'with remote mirroring disabled' do
it 'returns nil' do
- remote_mirror.update_attributes(enabled: false)
+ remote_mirror.update(enabled: false)
expect(remote_mirror.sync).to be_nil
end
@@ -229,7 +229,7 @@ describe RemoteMirror do
end
before do
- remote_mirror.update_attributes(last_update_started_at: Time.now)
+ remote_mirror.update(last_update_started_at: Time.now)
end
context 'when remote mirror does not have status failed' do
@@ -244,7 +244,7 @@ describe RemoteMirror do
context 'when remote mirror has status failed' do
it 'returns false when last update started after the timestamp' do
- remote_mirror.update_attributes(update_status: 'failed')
+ remote_mirror.update(update_status: 'failed')
expect(remote_mirror.updated_since?(timestamp)).to be false
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index d060ab923d1..02d31098cfd 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -151,7 +151,9 @@ describe Repository do
it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) }
after do
- repository.rugged.tags.delete(annotated_tag_name)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.tags.delete(annotated_tag_name)
+ end
end
end
end
@@ -431,6 +433,18 @@ describe Repository do
it { is_expected.to be_falsey }
end
+
+ context 'non merged branch' do
+ subject { repository.merged_to_root_ref?('fix') }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'non existent branch' do
+ subject { repository.merged_to_root_ref?('non_existent_branch') }
+
+ it { is_expected.to be_nil }
+ end
end
describe '#can_be_merged?' do
@@ -452,17 +466,11 @@ describe Repository do
it { is_expected.to be_falsey }
end
- context 'non merged branch' do
- subject { repository.merged_to_root_ref?('fix') }
+ context 'submodule changes that confuse rugged' do
+ subject { repository.can_be_merged?('update-gitlab-shell-v-6-0-1', 'update-gitlab-shell-v-6-0-3') }
it { is_expected.to be_falsey }
end
-
- context 'non existent branch' do
- subject { repository.merged_to_root_ref?('non_existent_branch') }
-
- it { is_expected.to be_nil }
- end
end
describe '#commit' do
@@ -1014,24 +1022,6 @@ describe Repository do
end
end
- describe '#find_branch' do
- context 'fresh_repo is true' do
- it 'delegates the call to raw_repository' do
- expect(repository.raw_repository).to receive(:find_branch).with('master', true)
-
- repository.find_branch('master', fresh_repo: true)
- end
- end
-
- context 'fresh_repo is false' do
- it 'delegates the call to raw_repository' do
- expect(repository.raw_repository).to receive(:find_branch).with('master', false)
-
- repository.find_branch('master', fresh_repo: false)
- end
- end
- end
-
describe '#update_branch_with_hooks' do
let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature
let(:new_rev) { 'a74ae73c1ccde9b974a70e82b901588071dc142a' } # commit whose parent is old_rev
@@ -2225,8 +2215,11 @@ describe Repository do
create_remote_branch('joe', 'remote_branch', masterrev)
repository.add_branch(user, 'local_branch', masterrev.id)
- expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false)
- expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true)
+ # TODO: move this test to gitaly https://gitlab.com/gitlab-org/gitaly/issues/1243
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false)
+ expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true)
+ end
end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 01238a89a81..48799781b87 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -29,12 +29,12 @@ describe Route do
context 'after update' do
it 'calls #create_redirect_for_old_path' do
expect(route).to receive(:create_redirect_for_old_path)
- route.update_attributes(path: 'foo')
+ route.update(path: 'foo')
end
it 'calls #delete_conflicting_redirects' do
expect(route).to receive(:delete_conflicting_redirects)
- route.update_attributes(path: 'foo')
+ route.update(path: 'foo')
end
end
@@ -70,7 +70,7 @@ describe Route do
context 'path update' do
context 'when route name is set' do
before do
- route.update_attributes(path: 'bar')
+ route.update(path: 'bar')
end
it 'updates children routes with new path' do
@@ -89,7 +89,7 @@ describe Route do
end
it "does not fail" do
- expect(route.update_attributes(path: 'bar')).to be_truthy
+ expect(route.update(path: 'bar')).to be_truthy
end
end
@@ -100,7 +100,7 @@ describe Route do
let!(:conflicting_redirect3) { route.create_redirect('gitlab-org') }
it 'deletes the conflicting redirects' do
- route.update_attributes(path: 'bar')
+ route.update(path: 'bar')
expect(RedirectRoute.exists?(path: 'bar/test')).to be_falsey
expect(RedirectRoute.exists?(path: 'bar/test/foo')).to be_falsey
@@ -111,7 +111,7 @@ describe Route do
context 'name update' do
it 'updates children routes with new path' do
- route.update_attributes(name: 'bar')
+ route.update(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy
expect(described_class.exists?(name: 'bar / test')).to be_truthy
@@ -123,7 +123,7 @@ describe Route do
# Note: using `update_columns` to skip all validation and callbacks
route.update_columns(name: nil)
- expect { route.update_attributes(name: 'bar') }
+ expect { route.update(name: 'bar') }
.to change { route.name }.from(nil).to('bar')
end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index a849af062c5..029ad7f3e9f 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -280,7 +280,7 @@ describe Service do
service.save!
expect do
- service.update_attributes(active: false)
+ service.update(active: false)
end.to change { service.project.has_external_issue_tracker }.from(true).to(false)
end
end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index f29abcf536e..bd498269798 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -7,7 +7,6 @@ describe Todo do
it { is_expected.to belong_to(:author).class_name("User") }
it { is_expected.to belong_to(:note) }
it { is_expected.to belong_to(:project) }
- it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:target).touch(true) }
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 097144d04ce..fc46551c3be 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -383,7 +383,7 @@ describe User do
let(:secondary) { create(:email, :confirmed, email: 'secondary@example.com', user: user) }
it 'allows a verfied secondary email to be used as the primary without needing reconfirmation' do
- user.update_attributes!(email: secondary.email)
+ user.update!(email: secondary.email)
user.reload
expect(user.email).to eq secondary.email
expect(user.unconfirmed_email).to eq nil
@@ -405,11 +405,11 @@ describe User do
it 'gets called when email updated' do
expect(@user).to receive(:update_emails_with_primary_email)
- @user.update_attributes!(email: 'new_primary@example.com')
+ @user.update!(email: 'new_primary@example.com')
end
it 'adds old primary to secondary emails when secondary is a new email ' do
- @user.update_attributes!(email: 'new_primary@example.com')
+ @user.update!(email: 'new_primary@example.com')
@user.reload
expect(@user.emails.count).to eq 2
@@ -417,7 +417,7 @@ describe User do
end
it 'adds old primary to secondary emails if secondary is becoming a primary' do
- @user.update_attributes!(email: @secondary.email)
+ @user.update!(email: @secondary.email)
@user.reload
expect(@user.emails.count).to eq 1
@@ -425,7 +425,7 @@ describe User do
end
it 'transfers old confirmation values into new secondary' do
- @user.update_attributes!(email: @secondary.email)
+ @user.update!(email: @secondary.email)
@user.reload
expect(@user.emails.count).to eq 1
@@ -494,12 +494,12 @@ describe User do
it 'does nothing when the name is updated' do
expect(user).not_to receive(:update_invalid_gpg_signatures)
- user.update_attributes!(name: 'Bette')
+ user.update!(name: 'Bette')
end
it 'synchronizes the gpg keys when the email is updated' do
expect(user).to receive(:update_invalid_gpg_signatures).at_most(:twice)
- user.update_attributes!(email: 'shawnee.ritchie@denesik.com')
+ user.update!(email: 'shawnee.ritchie@denesik.com')
end
end
end
@@ -617,13 +617,13 @@ describe User do
it 'receives callback when external changes' do
expect(user).to receive(:ensure_user_rights_and_limits)
- user.update_attributes(external: false)
+ user.update(external: false)
end
it 'ensures correct rights and limits for user' do
stub_config_setting(default_can_create_group: true)
- expect { user.update_attributes(external: false) }.to change { user.can_create_group }.to(true)
+ expect { user.update(external: false) }.to change { user.can_create_group }.to(true)
.and change { user.projects_limit }.to(Gitlab::CurrentSettings.default_projects_limit)
end
end
@@ -634,11 +634,11 @@ describe User do
it 'receives callback when external changes' do
expect(user).to receive(:ensure_user_rights_and_limits)
- user.update_attributes(external: true)
+ user.update(external: true)
end
it 'ensures correct rights and limits for user' do
- expect { user.update_attributes(external: true) }.to change { user.can_create_group }.to(false)
+ expect { user.update(external: true) }.to change { user.can_create_group }.to(false)
.and change { user.projects_limit }.to(0)
end
end
@@ -700,7 +700,7 @@ describe User do
@project = create(:project, namespace: @user.namespace)
@project_2 = create(:project, group: create(:group)) do |project|
- project.add_master(@user)
+ project.add_maintainer(@user)
end
@project_3 = create(:project, group: create(:group)) do |project|
project.add_developer(@user)
@@ -836,7 +836,7 @@ describe User do
before do
# add user to project
- project.add_master(user)
+ project.add_maintainer(user)
# create invite to projet
create(:project_member, :developer, project: project, invite_token: '1234', invite_email: 'inviteduser1@example.com')
@@ -1581,8 +1581,8 @@ describe User do
let!(:merge_event) { create(:event, :created, project: project3, target: merge_request, author: subject) }
before do
- project1.add_master(subject)
- project2.add_master(subject)
+ project1.add_maintainer(subject)
+ project2.add_maintainer(subject)
end
it "includes IDs for projects the user has pushed to" do
@@ -1663,8 +1663,8 @@ describe User do
let!(:project) { create(:project, group: project_group) }
before do
- private_group.add_user(user, Gitlab::Access::MASTER)
- project.add_master(user)
+ private_group.add_user(user, Gitlab::Access::MAINTAINER)
+ project.add_maintainer(user)
end
subject { user.authorized_groups }
@@ -1678,7 +1678,7 @@ describe User do
let!(:child_group) { create(:group, parent: parent_group) }
before do
- parent_group.add_user(user, Gitlab::Access::MASTER)
+ parent_group.add_user(user, Gitlab::Access::MAINTAINER)
end
subject { user.membership_groups }
@@ -1696,7 +1696,7 @@ describe User do
it 'includes projects that belong to a user, but no other projects' do
owned = create(:project, :private, namespace: user.namespace)
- member = create(:project, :private).tap { |p| p.add_master(user) }
+ member = create(:project, :private).tap { |p| p.add_maintainer(user) }
other = create(:project)
expect(subject).to include(owned)
@@ -1726,11 +1726,11 @@ describe User do
.to contain_exactly(project)
end
- it 'includes projects for which the user is a master' do
+ it 'includes projects for which the user is a maintainer' do
user = create(:user)
project = create(:project, :private)
- project.add_master(user)
+ project.add_maintainer(user)
expect(user.authorized_projects(Gitlab::Access::REPORTER))
.to contain_exactly(project)
@@ -1824,10 +1824,10 @@ describe User do
it 'includes projects for which the user access level is above or equal to reporter' do
reporter_project = create(:project) { |p| p.add_reporter(user) }
developer_project = create(:project) { |p| p.add_developer(user) }
- master_project = create(:project) { |p| p.add_master(user) }
+ maintainer_project = create(:project) { |p| p.add_maintainer(user) }
- expect(user.projects_where_can_admin_issues.to_a).to match_array([master_project, developer_project, reporter_project])
- expect(user.can?(:admin_issue, master_project)).to eq(true)
+ expect(user.projects_where_can_admin_issues.to_a).to match_array([maintainer_project, developer_project, reporter_project])
+ expect(user.can?(:admin_issue, maintainer_project)).to eq(true)
expect(user.can?(:admin_issue, developer_project)).to eq(true)
expect(user.can?(:admin_issue, reporter_project)).to eq(true)
end
@@ -1907,9 +1907,9 @@ describe User do
end
shared_examples :member do
- context 'when the user is a master' do
+ context 'when the user is a maintainer' do
before do
- add_user(:master)
+ add_user(:maintainer)
end
it 'loads' do
@@ -2461,18 +2461,20 @@ describe User do
it 'changes the namespace (just to compare to when username is not changed)' do
expect do
- user.update_attributes!(username: new_username)
+ Timecop.freeze(1.second.from_now) do
+ user.update!(username: new_username)
+ end
end.to change { user.namespace.updated_at }
end
it 'updates the namespace name' do
- user.update_attributes!(username: new_username)
+ user.update!(username: new_username)
expect(user.namespace.name).to eq(new_username)
end
it 'updates the namespace path' do
- user.update_attributes!(username: new_username)
+ user.update!(username: new_username)
expect(user.namespace.path).to eq(new_username)
end
@@ -2481,12 +2483,12 @@ describe User do
let!(:conflicting_namespace) { create(:group, path: new_username) }
it 'causes the user save to fail' do
- expect(user.update_attributes(username: new_username)).to be_falsey
+ expect(user.update(username: new_username)).to be_falsey
expect(user.namespace.errors.messages[:path].first).to eq('has already been taken')
end
it 'adds the namespace errors to the user' do
- user.update_attributes(username: new_username)
+ user.update(username: new_username)
expect(user.errors.full_messages.first).to eq('Username has already been taken')
end
@@ -2496,7 +2498,7 @@ describe User do
context 'when the username is not changed' do
it 'does not change the namespace' do
expect do
- user.update_attributes!(email: 'asdf@asdf.com')
+ user.update!(email: 'asdf@asdf.com')
end.not_to change { user.namespace.updated_at }
end
end
@@ -2526,7 +2528,7 @@ describe User do
expect(system_hook_service).to receive(:execute_hooks_for).with(user, :rename)
expect(user).to receive(:system_hook_service).and_return(system_hook_service)
- user.update_attributes!(username: new_username)
+ user.update!(username: new_username)
end
end
@@ -2534,7 +2536,7 @@ describe User do
it 'does not trigger system hook' do
expect(user).not_to receive(:system_hook_service)
- user.update_attributes!(email: 'asdf@asdf.com')
+ user.update!(email: 'asdf@asdf.com')
end
end
end
@@ -2666,20 +2668,20 @@ describe User do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:owner_project) { create(:project, group: group) }
- let(:master_project) { create(:project) }
+ let(:maintainer_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
let(:guest_project) { create(:project) }
let(:no_access_project) { create(:project) }
let(:projects) do
- [owner_project, master_project, reporter_project, developer_project, guest_project, no_access_project].map(&:id)
+ [owner_project, maintainer_project, reporter_project, developer_project, guest_project, no_access_project].map(&:id)
end
let(:expected) do
{
owner_project.id => Gitlab::Access::OWNER,
- master_project.id => Gitlab::Access::MASTER,
+ maintainer_project.id => Gitlab::Access::MAINTAINER,
reporter_project.id => Gitlab::Access::REPORTER,
developer_project.id => Gitlab::Access::DEVELOPER,
guest_project.id => Gitlab::Access::GUEST,
@@ -2689,7 +2691,7 @@ describe User do
before do
create(:group_member, user: user, group: group)
- master_project.add_master(user)
+ maintainer_project.add_maintainer(user)
reporter_project.add_reporter(user)
developer_project.add_developer(user)
guest_project.add_guest(user)
@@ -2716,14 +2718,14 @@ describe User do
end
it 'only requests the extra projects when uncached projects are passed' do
- second_master_project = create(:project)
+ second_maintainer_project = create(:project)
second_developer_project = create(:project)
- second_master_project.add_master(user)
+ second_maintainer_project.add_maintainer(user)
second_developer_project.add_developer(user)
- all_projects = projects + [second_master_project.id, second_developer_project.id]
+ all_projects = projects + [second_maintainer_project.id, second_developer_project.id]
- expected_all = expected.merge(second_master_project.id => Gitlab::Access::MASTER,
+ expected_all = expected.merge(second_maintainer_project.id => Gitlab::Access::MAINTAINER,
second_developer_project.id => Gitlab::Access::DEVELOPER)
access_levels(projects)
@@ -2731,7 +2733,7 @@ describe User do
queries = ActiveRecord::QueryRecorder.new { access_levels(all_projects) }
expect(queries.count).to eq(1)
- expect(queries.log_message).to match(/\W(#{second_master_project.id}, #{second_developer_project.id})\W/)
+ expect(queries.log_message).to match(/\W(#{second_maintainer_project.id}, #{second_developer_project.id})\W/)
expect(access_levels(all_projects)).to eq(expected_all)
end
end
@@ -2745,20 +2747,20 @@ describe User do
shared_examples 'max member access for groups' do
let(:user) { create(:user) }
let(:owner_group) { create(:group) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
let(:guest_group) { create(:group) }
let(:no_access_group) { create(:group) }
let(:groups) do
- [owner_group, master_group, reporter_group, developer_group, guest_group, no_access_group].map(&:id)
+ [owner_group, maintainer_group, reporter_group, developer_group, guest_group, no_access_group].map(&:id)
end
let(:expected) do
{
owner_group.id => Gitlab::Access::OWNER,
- master_group.id => Gitlab::Access::MASTER,
+ maintainer_group.id => Gitlab::Access::MAINTAINER,
reporter_group.id => Gitlab::Access::REPORTER,
developer_group.id => Gitlab::Access::DEVELOPER,
guest_group.id => Gitlab::Access::GUEST,
@@ -2768,7 +2770,7 @@ describe User do
before do
owner_group.add_owner(user)
- master_group.add_master(user)
+ maintainer_group.add_maintainer(user)
reporter_group.add_reporter(user)
developer_group.add_developer(user)
guest_group.add_guest(user)
@@ -2795,14 +2797,14 @@ describe User do
end
it 'only requests the extra groups when uncached groups are passed' do
- second_master_group = create(:group)
+ second_maintainer_group = create(:group)
second_developer_group = create(:group)
- second_master_group.add_master(user)
+ second_maintainer_group.add_maintainer(user)
second_developer_group.add_developer(user)
- all_groups = groups + [second_master_group.id, second_developer_group.id]
+ all_groups = groups + [second_maintainer_group.id, second_developer_group.id]
- expected_all = expected.merge(second_master_group.id => Gitlab::Access::MASTER,
+ expected_all = expected.merge(second_maintainer_group.id => Gitlab::Access::MAINTAINER,
second_developer_group.id => Gitlab::Access::DEVELOPER)
access_levels(groups)
@@ -2810,7 +2812,7 @@ describe User do
queries = ActiveRecord::QueryRecorder.new { access_levels(all_groups) }
expect(queries.count).to eq(1)
- expect(queries.log_message).to match(/\W(#{second_master_group.id}, #{second_developer_group.id})\W/)
+ expect(queries.log_message).to match(/\W(#{second_maintainer_group.id}, #{second_developer_group.id})\W/)
expect(access_levels(all_groups)).to eq(expected_all)
end
end
diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb
index eead55d33ca..79a616899fa 100644
--- a/spec/policies/ci/build_policy_spec.rb
+++ b/spec/policies/ci/build_policy_spec.rb
@@ -204,18 +204,18 @@ describe Ci::BuildPolicy do
end
end
- context 'when a master erases a build' do
+ context 'when a maintainer erases a build' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
- context 'when masters can push to the branch' do
+ context 'when maintainers can push to the branch' do
before do
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: build.ref, project: project)
end
- context 'when the build was created by the master' do
+ context 'when the build was created by the maintainer' do
let(:owner) { user }
it { expect(policy).to be_allowed :erase_build }
diff --git a/spec/policies/ci/pipeline_schedule_policy_spec.rb b/spec/policies/ci/pipeline_schedule_policy_spec.rb
index c0c3eda4911..f1d3cd04e32 100644
--- a/spec/policies/ci/pipeline_schedule_policy_spec.rb
+++ b/spec/policies/ci/pipeline_schedule_policy_spec.rb
@@ -77,9 +77,9 @@ describe Ci::PipelineSchedulePolicy, :models do
end
end
- describe 'rules for a master' do
+ describe 'rules for a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'includes abilities to do do all operations on pipeline schedule' do
@@ -93,8 +93,8 @@ describe Ci::PipelineSchedulePolicy, :models do
let(:owner) { create(:user) }
before do
- project.add_master(owner)
- project.add_master(user)
+ project.add_maintainer(owner)
+ project.add_maintainer(user)
pipeline_schedule.update(owner: owner)
end
diff --git a/spec/policies/ci/trigger_policy_spec.rb b/spec/policies/ci/trigger_policy_spec.rb
index 14630748c90..d8a63066265 100644
--- a/spec/policies/ci/trigger_policy_spec.rb
+++ b/spec/policies/ci/trigger_policy_spec.rb
@@ -43,9 +43,9 @@ describe Ci::TriggerPolicy do
context 'when owner is undefined' do
let(:owner) { nil }
- context 'when user is master of the project' do
+ context 'when user is maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allows to admin and manage trigger'
@@ -67,9 +67,9 @@ describe Ci::TriggerPolicy do
context 'when owner is an user' do
let(:owner) { user }
- context 'when user is master of the project' do
+ context 'when user is maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allows to admin and manage trigger'
@@ -79,9 +79,9 @@ describe Ci::TriggerPolicy do
context 'when owner is another user' do
let(:owner) { create(:user) }
- context 'when user is master of the project' do
+ context 'when user is maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'allows to manage trigger'
diff --git a/spec/policies/clusters/cluster_policy_spec.rb b/spec/policies/clusters/cluster_policy_spec.rb
index 4207f42b07f..ced969830d8 100644
--- a/spec/policies/clusters/cluster_policy_spec.rb
+++ b/spec/policies/clusters/cluster_policy_spec.rb
@@ -16,9 +16,9 @@ describe Clusters::ClusterPolicy, :models do
it { expect(policy).to be_disallowed :admin_cluster }
end
- context 'when master' do
+ context 'when maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { expect(policy).to be_allowed :update_cluster }
diff --git a/spec/policies/deploy_key_policy_spec.rb b/spec/policies/deploy_key_policy_spec.rb
index ca7b7fe7ef7..e7263d49613 100644
--- a/spec/policies/deploy_key_policy_spec.rb
+++ b/spec/policies/deploy_key_policy_spec.rb
@@ -12,7 +12,7 @@ describe DeployKeyPolicy do
let(:project) { create(:project_empty_repo) }
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
project.deploy_keys << deploy_key
end
diff --git a/spec/policies/deploy_token_policy_spec.rb b/spec/policies/deploy_token_policy_spec.rb
index eea287d895e..cef5a4a22bc 100644
--- a/spec/policies/deploy_token_policy_spec.rb
+++ b/spec/policies/deploy_token_policy_spec.rb
@@ -8,15 +8,15 @@ describe DeployTokenPolicy do
subject { described_class.new(current_user, deploy_token) }
describe 'creating a deploy key' do
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
end
it { is_expected.to be_allowed(:create_deploy_token) }
end
- context 'when user is not master' do
+ context 'when user is not maintainer' do
before do
project.add_developer(current_user)
end
@@ -26,15 +26,15 @@ describe DeployTokenPolicy do
end
describe 'updating a deploy key' do
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
end
it { is_expected.to be_allowed(:update_deploy_token) }
end
- context 'when user is not master' do
+ context 'when user is not maintainer' do
before do
project.add_developer(current_user)
end
diff --git a/spec/policies/environment_policy_spec.rb b/spec/policies/environment_policy_spec.rb
index de4cb5b30c5..0442b032e89 100644
--- a/spec/policies/environment_policy_spec.rb
+++ b/spec/policies/environment_policy_spec.rb
@@ -1,57 +1,101 @@
require 'spec_helper'
describe EnvironmentPolicy do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
+ using RSpec::Parameterized::TableSyntax
- let(:environment) do
- create(:environment, :with_review_app, project: project)
- end
+ let(:user) { create(:user) }
let(:policy) do
described_class.new(user, environment)
end
describe '#rules' do
- context 'when user does not have access to the project' do
- let(:project) { create(:project, :private, :repository) }
+ shared_examples 'project permissions' do
+ context 'with stop action' do
+ let(:environment) do
+ create(:environment, :with_review_app, project: project)
+ end
- it 'does not include ability to stop environment' do
- expect(policy).to be_disallowed :stop_environment
- end
- end
+ where(:access_level, :allowed?) do
+ nil | false
+ :guest | false
+ :reporter | false
+ :developer | true
+ :maintainer | true
+ end
- context 'when anonymous user has access to the project' do
- let(:project) { create(:project, :public, :repository) }
+ with_them do
+ before do
+ project.add_user(user, access_level) unless access_level.nil?
+ end
- it 'does not include ability to stop environment' do
- expect(policy).to be_disallowed :stop_environment
- end
- end
+ it { expect(policy.allowed?(:stop_environment)).to be allowed? }
+ end
- context 'when team member has access to the project' do
- let(:project) { create(:project, :public, :repository) }
+ context 'when an admin user' do
+ let(:user) { create(:user, :admin) }
- before do
- project.add_developer(user)
- end
+ it { expect(policy).to be_allowed :stop_environment }
+ end
+
+ context 'with protected branch' do
+ with_them do
+ before do
+ project.add_user(user, access_level) unless access_level.nil?
+ create(:protected_branch, :no_one_can_push,
+ name: 'master', project: project)
+ end
- context 'when team member has ability to stop environment' do
- it 'does includes ability to stop environment' do
- expect(policy).to be_allowed :stop_environment
+ it { expect(policy).to be_disallowed :stop_environment }
+ end
+
+ context 'when an admin user' do
+ let(:user) { create(:user, :admin) }
+
+ it { expect(policy).to be_allowed :stop_environment }
+ end
end
end
- context 'when team member has no ability to stop environment' do
- before do
- create(:protected_branch, :no_one_can_push,
- name: 'master', project: project)
+ context 'without stop action' do
+ let(:environment) do
+ create(:environment, project: project)
+ end
+
+ where(:access_level, :allowed?) do
+ nil | false
+ :guest | false
+ :reporter | false
+ :developer | false
+ :maintainer | true
end
- it 'does not include ability to stop environment' do
- expect(policy).to be_disallowed :stop_environment
+ with_them do
+ before do
+ project.add_user(user, access_level) unless access_level.nil?
+ end
+
+ it { expect(policy.allowed?(:stop_environment)).to be allowed? }
+ end
+
+ context 'when an admin user' do
+ let(:user) { create(:user, :admin) }
+
+ it { expect(policy).to be_allowed :stop_environment }
end
end
end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ include_examples 'project permissions'
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository) }
+
+ include_examples 'project permissions'
+ end
end
end
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index 873673b50ef..a2047b54deb 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -65,12 +65,12 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:create_fork) }
end
- context "when user is a master in a group" do
+ context "when user is a maintainer in a group" do
let(:group) { create(:group) }
let(:current_user) { create(:user, projects_limit: 0) }
before do
- group.add_master(current_user)
+ group.add_maintainer(current_user)
end
it { is_expected.to be_allowed(:create_fork) }
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index d6d340bd806..35951251bc5 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -4,7 +4,7 @@ describe GroupPolicy do
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:owner) { create(:user) }
let(:admin) { create(:admin) }
let(:group) { create(:group, :private) }
@@ -19,7 +19,7 @@ describe GroupPolicy do
let(:developer_permissions) { [:admin_milestones] }
- let(:master_permissions) do
+ let(:maintainer_permissions) do
[
:create_projects
]
@@ -39,7 +39,7 @@ describe GroupPolicy do
group.add_guest(guest)
group.add_reporter(reporter)
group.add_developer(developer)
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_owner(owner)
end
@@ -62,7 +62,7 @@ describe GroupPolicy do
expect_disallowed(:upload_file)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
expect_disallowed(:read_namespace)
end
@@ -97,7 +97,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -109,7 +109,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -121,19 +121,19 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
- context 'master' do
- let(:current_user) { master }
+ context 'maintainer' do
+ let(:current_user) { maintainer }
it do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -147,7 +147,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
end
@@ -161,7 +161,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
end
@@ -203,7 +203,7 @@ describe GroupPolicy do
nested_group.add_guest(guest)
nested_group.add_guest(reporter)
nested_group.add_guest(developer)
- nested_group.add_guest(master)
+ nested_group.add_guest(maintainer)
group.owners.destroy_all
@@ -220,7 +220,7 @@ describe GroupPolicy do
expect_disallowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -232,7 +232,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -244,7 +244,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -256,19 +256,19 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
- context 'master' do
- let(:current_user) { master }
+ context 'maintainer' do
+ let(:current_user) { maintainer }
it do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
@@ -282,7 +282,7 @@ describe GroupPolicy do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 6d4676c25a5..dd3fa4e6a51 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -4,7 +4,7 @@ describe ProjectPolicy do
set(:guest) { create(:user) }
set(:reporter) { create(:user) }
set(:developer) { create(:user) }
- set(:master) { create(:user) }
+ set(:maintainer) { create(:user) }
set(:owner) { create(:user) }
set(:admin) { create(:admin) }
let(:project) { create(:project, :public, namespace: owner.namespace) }
@@ -42,7 +42,7 @@ describe ProjectPolicy do
]
end
- let(:base_master_permissions) do
+ let(:base_maintainer_permissions) do
%i[
push_to_delete_protected_branch update_project_snippet update_environment
update_deployment admin_project_snippet
@@ -70,15 +70,15 @@ describe ProjectPolicy do
# Used in EE specs
let(:additional_guest_permissions) { [] }
let(:additional_reporter_permissions) { [] }
- let(:additional_master_permissions) { [] }
+ let(:additional_maintainer_permissions) { [] }
let(:guest_permissions) { base_guest_permissions + additional_guest_permissions }
let(:reporter_permissions) { base_reporter_permissions + additional_reporter_permissions }
- let(:master_permissions) { base_master_permissions + additional_master_permissions }
+ let(:maintainer_permissions) { base_maintainer_permissions + additional_maintainer_permissions }
before do
project.add_guest(guest)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.add_developer(developer)
project.add_reporter(reporter)
end
@@ -276,7 +276,7 @@ describe ProjectPolicy do
expect_disallowed(*reporter_public_build_permissions)
expect_disallowed(*team_member_reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
@@ -326,7 +326,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_disallowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
@@ -347,7 +347,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_disallowed(*master_permissions)
+ expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
@@ -357,23 +357,23 @@ describe ProjectPolicy do
end
end
- shared_examples 'project policies as master' do
+ shared_examples 'project policies as maintainer' do
context 'abilities for non-public projects' do
let(:project) { create(:project, namespace: owner.namespace) }
- subject { described_class.new(master, project) }
+ subject { described_class.new(maintainer, project) }
it do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
it_behaves_like 'archived project policies' do
- let(:regular_abilities) { master_permissions }
+ let(:regular_abilities) { maintainer_permissions }
end
end
end
@@ -389,7 +389,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
@@ -410,7 +410,7 @@ describe ProjectPolicy do
expect_allowed(*reporter_permissions)
expect_disallowed(*team_member_reporter_permissions)
expect_allowed(*developer_permissions)
- expect_allowed(*master_permissions)
+ expect_allowed(*maintainer_permissions)
expect_allowed(*owner_permissions)
end
@@ -424,7 +424,7 @@ describe ProjectPolicy do
it_behaves_like 'project policies as guest'
it_behaves_like 'project policies as reporter'
it_behaves_like 'project policies as developer'
- it_behaves_like 'project policies as master'
+ it_behaves_like 'project policies as maintainer'
it_behaves_like 'project policies as owner'
it_behaves_like 'project policies as admin'
diff --git a/spec/policies/protected_branch_policy_spec.rb b/spec/policies/protected_branch_policy_spec.rb
index b39de42d721..1587196754d 100644
--- a/spec/policies/protected_branch_policy_spec.rb
+++ b/spec/policies/protected_branch_policy_spec.rb
@@ -8,8 +8,8 @@ describe ProtectedBranchPolicy do
subject { described_class.new(user, protected_branch) }
- it 'branches can be updated via project masters' do
- project.add_master(user)
+ it 'branches can be updated via project maintainers' do
+ project.add_maintainer(user)
is_expected.to be_allowed(:update_protected_branch)
end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index e3b37739e8e..46ba6f442f5 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -270,7 +270,7 @@ describe MergeRequestPresenter do
context 'when can create issue and issues enabled' do
it 'returns path' do
allow(project).to receive(:issues_enabled?) { true }
- project.add_master(user)
+ project.add_maintainer(user)
is_expected
.to eq("/#{resource.project.full_path}/issues/new?merge_request_to_resolve_discussions_of=#{resource.iid}")
@@ -288,7 +288,7 @@ describe MergeRequestPresenter do
context 'when issues disabled' do
it 'returns nil' do
allow(project).to receive(:issues_enabled?) { false }
- project.add_master(user)
+ project.add_maintainer(user)
is_expected.to be_nil
end
@@ -307,7 +307,7 @@ describe MergeRequestPresenter do
context 'when merge request enabled and has permission' do
it 'has remove_wip_path' do
allow(project).to receive(:merge_requests_enabled?) { true }
- project.add_master(user)
+ project.add_maintainer(user)
is_expected
.to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/remove_wip")
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index 830d2ee3b20..01085dbcb49 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -326,7 +326,7 @@ describe ProjectPresenter do
context 'when user can admin pipeline and CI yml does not exists' do
it 'returns anchor data' do
- project.add_master(user)
+ project.add_maintainer(user)
allow(project).to receive(:auto_devops_enabled?).and_return(false)
allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil)
@@ -340,7 +340,7 @@ describe ProjectPresenter do
describe '#kubernetes_cluster_anchor_data' do
context 'when user can create Kubernetes cluster' do
it 'returns link to cluster if only one exists' do
- project.add_master(user)
+ project.add_maintainer(user)
cluster = create(:cluster, projects: [project])
expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
@@ -349,7 +349,7 @@ describe ProjectPresenter do
end
it 'returns link to clusters page if more than one exists' do
- project.add_master(user)
+ project.add_maintainer(user)
create(:cluster, :production_environment, projects: [project])
create(:cluster, projects: [project])
@@ -359,7 +359,7 @@ describe ProjectPresenter do
end
it 'returns link to create a cluster if no cluster exists' do
- project.add_master(user)
+ project.add_maintainer(user)
expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: false,
label: 'Add Kubernetes cluster',
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb
index 24389f28b21..e13129967b2 100644
--- a/spec/requests/api/access_requests_spec.rb
+++ b/spec/requests/api/access_requests_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
describe API::AccessRequests do
- set(:master) { create(:user) }
+ set(:maintainer) { create(:user) }
set(:developer) { create(:user) }
set(:access_requester) { create(:user) }
set(:stranger) { create(:user) }
set(:project) do
- create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.request_access(access_requester)
end
end
@@ -17,7 +17,7 @@ describe API::AccessRequests do
set(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_developer(developer)
- group.add_owner(master)
+ group.add_owner(maintainer)
group.request_access(access_requester)
end
end
@@ -28,7 +28,7 @@ describe API::AccessRequests do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/access_requests", stranger) }
end
- context 'when authenticated as a non-master/owner' do
+ context 'when authenticated as a non-maintainer/owner' do
%i[developer access_requester stranger].each do |type|
context "as a #{type}" do
it 'returns 403' do
@@ -41,9 +41,9 @@ describe API::AccessRequests do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'returns access requesters' do
- get api("/#{source_type.pluralize}/#{source.id}/access_requests", master)
+ get api("/#{source_type.pluralize}/#{source.id}/access_requests", maintainer)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
@@ -61,7 +61,7 @@ describe API::AccessRequests do
end
context 'when authenticated as a member' do
- %i[developer master].each do |type|
+ %i[developer maintainer].each do |type|
context "as a #{type}" do
it 'returns 403' do
expect do
@@ -88,7 +88,7 @@ describe API::AccessRequests do
context 'when authenticated as a stranger' do
context "when access request is disabled for the #{source_type}" do
before do
- source.update_attributes(request_access_enabled: false)
+ source.update(request_access_enabled: false)
end
it 'returns 403' do
@@ -128,7 +128,7 @@ describe API::AccessRequests do
let(:route) { put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}/approve", stranger) }
end
- context 'when authenticated as a non-master/owner' do
+ context 'when authenticated as a non-maintainer/owner' do
%i[developer access_requester stranger].each do |type|
context "as a #{type}" do
it 'returns 403' do
@@ -141,11 +141,11 @@ describe API::AccessRequests do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'returns 201' do
expect do
- put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}/approve", master),
- access_level: Member::MASTER
+ put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}/approve", maintainer),
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(201)
end.to change { source.members.count }.by(1)
@@ -158,13 +158,13 @@ describe API::AccessRequests do
expect(json_response['web_url']).to eq(Gitlab::Routing.url_helpers.user_url(access_requester))
# Member attributes
- expect(json_response['access_level']).to eq(Member::MASTER)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
end
context 'user_id does not match an existing access requester' do
it 'returns 404' do
expect do
- put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}/approve", master)
+ put api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}/approve", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.members.count }
@@ -180,7 +180,7 @@ describe API::AccessRequests do
let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}", stranger) }
end
- context 'when authenticated as a non-master/owner' do
+ context 'when authenticated as a non-maintainer/owner' do
%i[developer stranger].each do |type|
context "as a #{type}" do
it 'returns 403' do
@@ -203,10 +203,10 @@ describe API::AccessRequests do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'deletes the access requester' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{access_requester.id}", maintainer)
expect(response).to have_gitlab_http_status(204)
end.to change { source.requesters.count }.by(-1)
@@ -215,7 +215,7 @@ describe API::AccessRequests do
context 'user_id matches a member, not an access requester' do
it 'returns 404' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{developer.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{developer.id}", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.requesters.count }
@@ -225,7 +225,7 @@ describe API::AccessRequests do
context 'user_id does not match an existing access requester' do
it 'returns 404' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/access_requests/#{stranger.id}", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.requesters.count }
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 5adfb33677f..0921fecb933 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -10,7 +10,7 @@ describe API::AwardEmoji do
set(:note) { create(:note, project: project, noteable: issue) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb
index ae64a9ca162..e232e2e04ee 100644
--- a/spec/requests/api/badges_spec.rb
+++ b/spec/requests/api/badges_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe API::Badges do
- let(:master) { create(:user, username: 'master_user') }
+ let(:maintainer) { create(:user, username: 'maintainer_user') }
let(:developer) { create(:user) }
let(:access_requester) { create(:user) }
let(:stranger) { create(:user) }
@@ -25,7 +25,7 @@ describe API::Badges do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/badges", stranger) }
end
- %i[master developer access_requester stranger].each do |type|
+ %i[maintainer developer access_requester stranger].each do |type|
context "when authenticated as a #{type}" do
it 'returns 200' do
user = public_send(type)
@@ -43,16 +43,16 @@ describe API::Badges do
it 'avoids N+1 queries' do
# Establish baseline
- get api("/#{source_type.pluralize}/#{source.id}/badges", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
control = ActiveRecord::QueryRecorder.new do
- get api("/#{source_type.pluralize}/#{source.id}/badges", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
end
project.add_developer(create(:user))
expect do
- get api("/#{source_type.pluralize}/#{source.id}/badges", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges", maintainer)
end.not_to exceed_query_limit(control)
end
end
@@ -69,7 +69,7 @@ describe API::Badges do
end
context 'when authenticated as a non-member' do
- %i[master developer access_requester stranger].each do |type|
+ %i[maintainer developer access_requester stranger].each do |type|
let(:badge) { source.badges.first }
context "as a #{type}" do
@@ -122,10 +122,10 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'creates a new badge' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
link_url: example_url, image_url: example_url2
expect(response).to have_gitlab_http_status(201)
@@ -138,21 +138,21 @@ describe API::Badges do
end
it 'returns 400 when link_url is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
link_url: example_url
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when image_url is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
image_url: example_url2
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when link_url or image_url is not valid' do
- post api("/#{source_type.pluralize}/#{source.id}/badges", master),
+ post api("/#{source_type.pluralize}/#{source.id}/badges", maintainer),
link_url: 'whatever', image_url: 'whatever'
expect(response).to have_gitlab_http_status(400)
@@ -192,9 +192,9 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'updates the member' do
- put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master),
+ put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer),
link_url: example_url, image_url: example_url2
expect(response).to have_gitlab_http_status(200)
@@ -205,7 +205,7 @@ describe API::Badges do
end
it 'returns 400 when link_url or image_url is not valid' do
- put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master),
+ put api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer),
link_url: 'whatever', image_url: 'whatever'
expect(response).to have_gitlab_http_status(400)
@@ -239,22 +239,22 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'deletes the badge' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer)
expect(response).to have_gitlab_http_status(204)
end.to change { source.badges.count }.by(-1)
end
it_behaves_like '412 response' do
- let(:request) { api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", master) }
+ let(:request) { api("/#{source_type.pluralize}/#{source.id}/badges/#{badge.id}", maintainer) }
end
end
it 'returns 404 if badge does not exist' do
- delete api("/#{source_type.pluralize}/#{source.id}/badges/123", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/badges/123", maintainer)
expect(response).to have_gitlab_http_status(404)
end
@@ -289,9 +289,9 @@ describe API::Badges do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'gets the rendered badge values' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}&image_url=#{example_url2}", maintainer)
expect(response).to have_gitlab_http_status(200)
@@ -304,19 +304,19 @@ describe API::Badges do
end
it 'returns 400 when link_url is not given' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=#{example_url}", maintainer)
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when image_url is not given' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?image_url=#{example_url}", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?image_url=#{example_url}", maintainer)
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when link_url or image_url is not valid' do
- get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=whatever&image_url=whatever", master)
+ get api("/#{source_type.pluralize}/#{source.id}/badges/render?link_url=whatever&image_url=whatever", maintainer)
expect(response).to have_gitlab_http_status(400)
end
@@ -326,7 +326,7 @@ describe API::Badges do
context 'when deleting a badge' do
context 'and the source is a project' do
it 'cannot delete badges owned by the project group' do
- delete api("/projects/#{project.id}/badges/#{project_group.badges.first.id}", master)
+ delete api("/projects/#{project.id}/badges/#{project_group.badges.first.id}", maintainer)
expect(response).to have_gitlab_http_status(403)
end
@@ -345,9 +345,9 @@ describe API::Badges do
end
def setup_project
- create(:project, :public, :access_requestable, creator_id: master.id, namespace: project_group) do |project|
+ create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: project_group) do |project|
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.request_access(access_requester)
project.project_badges << build(:project_badge, project: project)
project.project_badges << build(:project_badge, project: project)
@@ -358,7 +358,7 @@ describe API::Badges do
def setup_group
create(:group, :public, :access_requestable) do |group|
group.add_developer(developer)
- group.add_owner(master)
+ group.add_owner(maintainer)
group.request_access(access_requester)
group.badges << build(:group_badge, group: group)
group.badges << build(:group_badge, group: group)
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 9bb6ed62393..7fff0a6cce6 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -13,7 +13,7 @@ describe API::Branches do
let(:current_user) { nil }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "GET /projects/:id/repository/branches" do
@@ -75,7 +75,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository branches'
@@ -170,7 +170,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository branch'
@@ -324,7 +324,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
@@ -381,8 +381,8 @@ describe API::Branches do
expect(json_response['protected']).to eq(true)
expect(json_response['developers_can_push']).to eq(false)
expect(json_response['developers_can_merge']).to eq(false)
- expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
- expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER)
+ expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MAINTAINER)
+ expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -458,7 +458,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
@@ -534,7 +534,7 @@ describe API::Branches do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index e73d1a252f5..246947e58a8 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -12,7 +12,7 @@ describe API::Commits do
let(:current_user) { nil }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/repository/commits' do
@@ -55,7 +55,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'project commits'
@@ -514,6 +514,38 @@ describe API::Commits do
expect(response).to have_gitlab_http_status(400)
end
end
+
+ context 'when committing into a fork as a maintainer' do
+ include_context 'merge request allowing collaboration'
+
+ let(:project_id) { forked_project.id }
+
+ def push_params(branch_name)
+ {
+ branch: branch_name,
+ commit_message: 'Hello world',
+ actions: [
+ {
+ action: 'create',
+ file_path: 'foo/bar/baz.txt',
+ content: 'puts 8'
+ }
+ ]
+ }
+ end
+
+ it 'allows pushing to the source branch of the merge request' do
+ post api(url, user), push_params('feature')
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'denies pushing to another branch' do
+ post api(url, user), push_params('other-branch')
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
describe 'GET /projects/:id/repository/commits/:sha/refs' do
@@ -667,7 +699,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'ref commit'
@@ -785,7 +817,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'ref diff'
@@ -884,7 +916,7 @@ describe API::Commits do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'ref comments'
@@ -1065,11 +1097,29 @@ describe API::Commits do
it 'returns 400 if you are not allowed to push to the target branch' do
post api(route, current_user), branch: 'feature'
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']).to eq('You are not allowed to push into this branch')
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(json_response['message']).to match(/You are not allowed to push into this branch/)
end
end
end
+
+ context 'when cherry picking to a fork as a maintainer' do
+ include_context 'merge request allowing collaboration'
+
+ let(:project_id) { forked_project.id }
+
+ it 'allows access from a maintainer that to the source branch' do
+ post api(route, user), branch: 'feature'
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it 'denies cherry picking to another branch' do
+ post api(route, user), branch: 'master'
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
describe 'POST /projects/:id/repository/commits/:sha/comments' do
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 51b70fda148..61ae053cea7 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -5,7 +5,7 @@ describe API::Deployments do
let(:non_member) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/deployments' do
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index fdddca5d0ef..bf93555b0f0 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -7,7 +7,7 @@ describe API::Environments do
let!(:environment) { create(:environment, project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/environments' do
@@ -126,7 +126,7 @@ describe API::Environments do
end
describe 'DELETE /projects/:id/environments/:environment_id' do
- context 'as a master' do
+ context 'as a maintainer' do
it 'returns a 200 for an existing environment' do
delete api("/projects/#{project.id}/environments/#{environment.id}", user)
@@ -155,7 +155,7 @@ describe API::Environments do
end
describe 'POST /projects/:id/environments/:environment_id/stop' do
- context 'as a master' do
+ context 'as a maintainer' do
context 'with a stoppable environment' do
before do
environment.update(state: :available)
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index 64fa7dc824c..f87e035c89d 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -9,7 +9,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'returns group variables' do
@@ -42,7 +42,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'returns group variable details' do
@@ -82,7 +82,7 @@ describe API::GroupVariables do
let!(:variable) { create(:ci_group_variable, group: group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'creates variable' do
@@ -138,7 +138,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'updates variable data' do
@@ -184,7 +184,7 @@ describe API::GroupVariables do
context 'authorized user with proper permissions' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'deletes variable' do
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index da23fdd7dca..65b387a2170 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -215,7 +215,7 @@ describe API::Groups do
context 'when using owned in the request' do
it 'returns an array of groups the user owns' do
- group1.add_master(user2)
+ group1.add_maintainer(user2)
get api('/groups', user2), owned: true
@@ -251,14 +251,22 @@ describe API::Groups do
projects
end
+ def response_project_ids(json_response, key)
+ json_response[key].map do |project|
+ project['id'].to_i
+ end
+ end
+
context 'when unauthenticated' do
it 'returns 404 for a private group' do
get api("/groups/#{group2.id}")
+
expect(response).to have_gitlab_http_status(404)
end
it 'returns 200 for a public group' do
get api("/groups/#{group1.id}")
+
expect(response).to have_gitlab_http_status(200)
end
@@ -268,7 +276,7 @@ describe API::Groups do
get api("/groups/#{public_group.id}")
- expect(json_response['projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'projects'))
.to contain_exactly(projects[:public].id)
end
@@ -278,7 +286,7 @@ describe API::Groups do
get api("/groups/#{group1.id}")
- expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'shared_projects'))
.to contain_exactly(projects[:public].id)
end
end
@@ -309,6 +317,17 @@ describe API::Groups do
expect(json_response['shared_projects'][0]['id']).to eq(project.id)
end
+ it "returns one of user1's groups without projects when with_projects option is set to false" do
+ project = create(:project, namespace: group2, path: 'Foo')
+ create(:project_group_link, project: project, group: group1)
+
+ get api("/groups/#{group1.id}", user1), with_projects: false
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['projects']).to be_nil
+ expect(json_response['shared_projects']).to be_nil
+ end
+
it "does not return a non existing group" do
get api("/groups/1328", user1)
@@ -327,7 +346,7 @@ describe API::Groups do
get api("/groups/#{public_group.id}", user2)
- expect(json_response['projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'projects'))
.to contain_exactly(projects[:public].id, projects[:internal].id)
end
@@ -337,7 +356,7 @@ describe API::Groups do
get api("/groups/#{group1.id}", user2)
- expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ expect(response_project_ids(json_response, 'shared_projects'))
.to contain_exactly(projects[:public].id, projects[:internal].id)
end
end
@@ -715,9 +734,9 @@ describe API::Groups do
end
end
- context 'as master', :nested_groups do
+ context 'as maintainer', :nested_groups do
before do
- group2.add_master(user1)
+ group2.add_maintainer(user1)
end
it 'cannot create subgroups' do
@@ -793,7 +812,7 @@ describe API::Groups do
it "does not remove a group if not an owner" do
user4 = create(:user)
- group1.add_master(user4)
+ group1.add_maintainer(user4)
delete api("/groups/#{group1.id}", user3)
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index d8a51f36dba..0a789d58fd8 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -201,7 +201,7 @@ describe API::Helpers do
end
it 'does not allow expired tokens' do
- personal_access_token.update_attributes!(expires_at: 1.day.ago)
+ personal_access_token.update!(expires_at: 1.day.ago)
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::ExpiredError
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 95eff029f98..66eb18229fa 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1083,7 +1083,7 @@ describe API::Issues do
let(:project) { merge_request.source_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'resolving all discussions in a merge request' do
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 50d6f4b4d99..7d1a5c12805 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -535,12 +535,14 @@ describe API::Jobs do
context 'authorized user' do
context 'when trace is in ObjectStorage' do
let!(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
- allow_any_instance_of(JobArtifactUploader).to receive(:url) { remote_trace_url }
- allow_any_instance_of(JobArtifactUploader).to receive(:size) { remote_trace_size }
+ allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
+ allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
end
it 'returns specific job trace' do
@@ -643,7 +645,7 @@ describe API::Jobs do
end
describe 'POST /projects/:id/jobs/:job_id/erase' do
- let(:role) { :master }
+ let(:role) { :maintainer }
before do
project.add_role(user, role)
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 34cbf75f4c1..a4220f5b2be 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -7,7 +7,7 @@ describe API::Labels do
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/labels' do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index ec500838eb2..01bbe7f5ec6 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
describe API::Members do
- let(:master) { create(:user, username: 'master_user') }
+ let(:maintainer) { create(:user, username: 'maintainer_user') }
let(:developer) { create(:user) }
let(:access_requester) { create(:user) }
let(:stranger) { create(:user) }
let(:project) do
- create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
+ create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
project.request_access(access_requester)
end
end
@@ -17,7 +17,7 @@ describe API::Members do
let!(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_developer(developer)
- group.add_owner(master)
+ group.add_owner(maintainer)
group.request_access(access_requester)
end
end
@@ -28,7 +28,7 @@ describe API::Members do
let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members", stranger) }
end
- %i[master developer access_requester stranger].each do |type|
+ %i[maintainer developer access_requester stranger].each do |type|
context "when authenticated as a #{type}" do
it 'returns 200' do
user = public_send(type)
@@ -39,23 +39,23 @@ describe API::Members do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
- expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
end
end
end
it 'avoids N+1 queries' do
# Establish baseline
- get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ get api("/#{source_type.pluralize}/#{source.id}/members", maintainer)
control = ActiveRecord::QueryRecorder.new do
- get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ get api("/#{source_type.pluralize}/#{source.id}/members", maintainer)
end
project.add_developer(create(:user))
expect do
- get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ get api("/#{source_type.pluralize}/#{source.id}/members", maintainer)
end.not_to exceed_query_limit(control)
end
@@ -68,17 +68,17 @@ describe API::Members do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
- expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
end
it 'finds members with query string' do
- get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: master.username
+ get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: maintainer.username
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.count).to eq(1)
- expect(json_response.first['username']).to eq(master.username)
+ expect(json_response.first['username']).to eq(maintainer.username)
end
it 'finds all members with no query specified' do
@@ -88,7 +88,7 @@ describe API::Members do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.count).to eq(2)
- expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
+ expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
end
end
end
@@ -129,7 +129,7 @@ describe API::Members do
it_behaves_like 'a 404 response when source is private' do
let(:route) do
post api("/#{source_type.pluralize}/#{source.id}/members", stranger),
- user_id: access_requester.id, access_level: Member::MASTER
+ user_id: access_requester.id, access_level: Member::MAINTAINER
end
end
@@ -139,7 +139,7 @@ describe API::Members do
it 'returns 403' do
user = public_send(type)
post api("/#{source_type.pluralize}/#{source.id}/members", user),
- user_id: access_requester.id, access_level: Member::MASTER
+ user_id: access_requester.id, access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(403)
end
@@ -147,24 +147,24 @@ describe API::Members do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
context 'and new member is already a requester' do
it 'transforms the requester into a proper member' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
- user_id: access_requester.id, access_level: Member::MASTER
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ user_id: access_requester.id, access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(201)
end.to change { source.members.count }.by(1)
expect(source.requesters.count).to eq(0)
expect(json_response['id']).to eq(access_requester.id)
- expect(json_response['access_level']).to eq(Member::MASTER)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
end
end
it 'creates a new member' do
expect do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
user_id: stranger.id, access_level: Member::DEVELOPER, expires_at: '2016-08-05'
expect(response).to have_gitlab_http_status(201)
@@ -176,28 +176,28 @@ describe API::Members do
end
it "returns 409 if member already exists" do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
- user_id: master.id, access_level: Member::MASTER
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ user_id: maintainer.id, access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(409)
end
it 'returns 400 when user_id is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
- access_level: Member::MASTER
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when access_level is not given' do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
user_id: stranger.id
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when access_level is not valid' do
- post api("/#{source_type.pluralize}/#{source.id}/members", master),
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
user_id: stranger.id, access_level: 1234
expect(response).to have_gitlab_http_status(400)
@@ -210,7 +210,7 @@ describe API::Members 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),
- access_level: Member::MASTER
+ access_level: Member::MAINTAINER
end
end
@@ -220,7 +220,7 @@ describe API::Members do
it 'returns 403' do
user = public_send(type)
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user),
- access_level: Member::MASTER
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(403)
end
@@ -228,33 +228,33 @@ describe API::Members do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
it 'updates the member' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
- access_level: Member::MASTER, expires_at: '2016-08-05'
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
+ access_level: Member::MAINTAINER, expires_at: '2016-08-05'
expect(response).to have_gitlab_http_status(200)
expect(json_response['id']).to eq(developer.id)
- expect(json_response['access_level']).to eq(Member::MASTER)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
expect(json_response['expires_at']).to eq('2016-08-05')
end
end
it 'returns 409 if member does not exist' do
- put api("/#{source_type.pluralize}/#{source.id}/members/123", master),
- access_level: Member::MASTER
+ put api("/#{source_type.pluralize}/#{source.id}/members/123", maintainer),
+ access_level: Member::MAINTAINER
expect(response).to have_gitlab_http_status(404)
end
it 'returns 400 when access_level is not given' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master)
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
expect(response).to have_gitlab_http_status(400)
end
it 'returns 400 when access level is not valid' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
access_level: 1234
expect(response).to have_gitlab_http_status(400)
@@ -291,11 +291,11 @@ describe API::Members do
end
end
- context 'when authenticated as a master/owner' do
+ context 'when authenticated as a maintainer/owner' do
context 'and member is a requester' do
it 'returns 404' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/members/#{access_requester.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/members/#{access_requester.id}", maintainer)
expect(response).to have_gitlab_http_status(404)
end.not_to change { source.requesters.count }
@@ -304,19 +304,19 @@ describe API::Members do
it 'deletes the member' do
expect do
- delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
expect(response).to have_gitlab_http_status(204)
end.to change { source.members.count }.by(-1)
end
it_behaves_like '412 response' do
- let(:request) { api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master) }
+ let(:request) { api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer) }
end
end
it 'returns 404 if member does not exist' do
- delete api("/#{source_type.pluralize}/#{source.id}/members/123", master)
+ delete api("/#{source_type.pluralize}/#{source.id}/members/123", maintainer)
expect(response).to have_gitlab_http_status(404)
end
@@ -366,7 +366,7 @@ describe API::Members do
context 'Adding owner to project' do
it 'returns 403' do
expect do
- post api("/projects/#{project.id}/members", master),
+ post api("/projects/#{project.id}/members", maintainer),
user_id: stranger.id, access_level: Member::OWNER
expect(response).to have_gitlab_http_status(400)
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index cb647aee70f..6530dc956cb 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -8,7 +8,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
before do
merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index eba39bb6ccc..1716d182782 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -306,6 +306,14 @@ describe API::MergeRequests do
expect(json_response['changes_count']).to eq(merge_request.merge_request_diff.real_size)
end
+ it 'exposes description and title html when render_html is true' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), render_html: true
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response).to include('title_html', 'description_html')
+ end
+
context 'merge_request_metrics' do
before do
merge_request.metrics.update!(merged_by: user,
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 98102fcd6a7..e2000ab42e8 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -23,10 +23,10 @@ describe API::Namespaces do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
- expect(group_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
- 'parent_id', 'members_count_with_descendants')
+ expect(group_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
+ 'parent_id', 'members_count_with_descendants')
- expect(user_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
+ expect(user_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
end
it "admin: returns an array of all namespaces" do
@@ -58,8 +58,8 @@ describe API::Namespaces do
owned_group_response = json_response.find { |resource| resource['id'] == group1.id }
- expect(owned_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
- 'parent_id', 'members_count_with_descendants')
+ expect(owned_group_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
+ 'parent_id', 'members_count_with_descendants')
end
it "returns correct attributes when user cannot admin group" do
@@ -69,7 +69,7 @@ describe API::Namespaces do
guest_group_response = json_response.find { |resource| resource['id'] == group1.id }
- expect(guest_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
+ expect(guest_group_response.keys).to include('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
end
it "user: returns an array of namespaces" do
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index dd568c24c72..3fb45449c74 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -44,7 +44,7 @@ describe API::Notes do
# For testing the cross-reference of a private issue in a public project
let(:private_project) do
create(:project, namespace: private_user.namespace)
- .tap { |p| p.add_master(private_user) }
+ .tap { |p| p.add_maintainer(private_user) }
end
let(:private_issue) { create(:issue, project: private_project) }
@@ -71,7 +71,7 @@ describe API::Notes do
context "issue is confidential" do
before do
- ext_issue.update_attributes(confidential: true)
+ ext_issue.update(confidential: true)
end
it "returns 404" do
@@ -104,7 +104,7 @@ describe API::Notes do
context "when issue is confidential" do
before do
- issue.update_attributes(confidential: true)
+ issue.update(confidential: true)
end
it "returns 404" do
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index a9ccbb32666..35b6ed8d5c0 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -80,7 +80,7 @@ describe API::PagesDomains do
context 'when pages is disabled' do
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -88,9 +88,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'get pages domains'
@@ -177,7 +177,7 @@ describe API::PagesDomains do
context 'when domain is vacant' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -185,9 +185,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'get pages domain'
@@ -270,9 +270,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'post pages domains'
@@ -380,7 +380,7 @@ describe API::PagesDomains do
context 'when domain is vacant' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -388,9 +388,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'put pages domain'
@@ -444,7 +444,7 @@ describe API::PagesDomains do
context 'when domain is vacant' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like '404 response' do
@@ -452,9 +452,9 @@ describe API::PagesDomains do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'delete pages domain'
diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb
index 91d4d5d3de9..997d413eb4f 100644
--- a/spec/requests/api/pipeline_schedules_spec.rb
+++ b/spec/requests/api/pipeline_schedules_spec.rb
@@ -22,7 +22,7 @@ describe API::PipelineSchedules do
.each do |pipeline_schedule|
create(:user).tap do |user|
project.add_developer(user)
- pipeline_schedule.update_attributes(owner: user)
+ pipeline_schedule.update(owner: user)
end
pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
end
@@ -270,38 +270,38 @@ describe API::PipelineSchedules do
end
describe 'DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let!(:pipeline_schedule) do
create(:ci_pipeline_schedule, project: project, owner: developer)
end
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context 'authenticated user with valid permissions' do
it 'deletes pipeline_schedule' do
expect do
- delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", maintainer)
end.to change { project.pipeline_schedules.count }.by(-1)
expect(response).to have_gitlab_http_status(204)
end
it 'responds with 404 Not Found if requesting non-existing pipeline_schedule' do
- delete api("/projects/#{project.id}/pipeline_schedules/-5", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/-5", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
it_behaves_like '412 response' do
- let(:request) { api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", master) }
+ let(:request) { api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", maintainer) }
end
end
context 'authenticated user with invalid permissions' do
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: maintainer) }
it 'does not delete pipeline_schedule' do
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer)
@@ -415,7 +415,7 @@ describe API::PipelineSchedules do
end
describe 'DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
set(:pipeline_schedule) do
create(:ci_pipeline_schedule, project: project, owner: developer)
@@ -426,13 +426,13 @@ describe API::PipelineSchedules do
end
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context 'authenticated user with valid permissions' do
it 'deletes pipeline_schedule_variable' do
expect do
- delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", maintainer)
end.to change { Ci::PipelineScheduleVariable.count }.by(-1)
expect(response).to have_gitlab_http_status(:accepted)
@@ -440,14 +440,14 @@ describe API::PipelineSchedules do
end
it 'responds with 404 Not Found if requesting non-existing pipeline_schedule_variable' do
- delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/____", master)
+ delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/____", maintainer)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'authenticated user with invalid permissions' do
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: master) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: maintainer) }
it 'does not delete pipeline_schedule_variable' do
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", developer)
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 78ea77cb3bb..e2ca27f5d41 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -11,7 +11,7 @@ describe API::Pipelines do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/pipelines ' do
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index 3834d27d0a9..45e4e35d773 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -109,13 +109,13 @@ describe API::ProjectExport do
it_behaves_like 'get project export status ok'
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
- project_none.add_master(user)
- project_started.add_master(user)
- project_finished.add_master(user)
- project_after_export.add_master(user)
+ project.add_maintainer(user)
+ project_none.add_maintainer(user)
+ project_started.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
end
it_behaves_like 'get project export status ok'
@@ -192,6 +192,13 @@ describe API::ProjectExport do
context 'when upload complete' do
before do
FileUtils.rm_rf(project_after_export.export_path)
+
+ if project_after_export.export_project_object_exists?
+ upload = project_after_export.import_export_upload
+
+ upload.remove_export_file!
+ upload.save
+ end
end
it_behaves_like '404 response' do
@@ -221,13 +228,13 @@ describe API::ProjectExport do
it_behaves_like 'get project download by strategy'
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
- project_none.add_master(user)
- project_started.add_master(user)
- project_finished.add_master(user)
- project_after_export.add_master(user)
+ project.add_maintainer(user)
+ project_none.add_maintainer(user)
+ project_started.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
end
it_behaves_like 'get project download by strategy'
@@ -261,6 +268,22 @@ describe API::ProjectExport do
it_behaves_like 'get project export download not found'
end
end
+
+ context 'when an uploader is used' do
+ before do
+ stub_uploads_object_storage(ImportExportUploader)
+
+ [project, project_finished, project_after_export].each do |p|
+ p.add_maintainer(user)
+
+ upload = ImportExportUpload.new(project: p)
+ upload.export_file = fixture_file_upload('spec/fixtures/project_export.tar.gz', "`/tar.gz")
+ upload.save!
+ end
+ end
+
+ it_behaves_like 'get project download by strategy'
+ end
end
describe 'POST /projects/:project_id/export' do
@@ -315,13 +338,13 @@ describe API::ProjectExport do
it_behaves_like 'post project export start'
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
- project_none.add_master(user)
- project_started.add_master(user)
- project_finished.add_master(user)
- project_after_export.add_master(user)
+ project.add_maintainer(user)
+ project_none.add_maintainer(user)
+ project_started.add_maintainer(user)
+ project_finished.add_maintainer(user)
+ project_after_export.add_maintainer(user)
end
it_behaves_like 'post project export start'
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index 12a183fed1e..bc45a63d9f1 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -13,7 +13,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user3)
end
@@ -214,7 +214,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user)
other_project = create(:project)
- other_project.add_master(test_user)
+ other_project.add_maintainer(test_user)
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
expect(response).to have_gitlab_http_status(404)
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 97dffdc9233..41243854ebc 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -146,7 +146,7 @@ describe API::ProjectImport do
describe 'GET /projects/:id/import' do
it 'returns the import status' do
project = create(:project, :import_started)
- project.add_master(user)
+ project.add_maintainer(user)
get api("/projects/#{project.id}/import", user)
@@ -156,8 +156,8 @@ describe API::ProjectImport do
it 'returns the import status and the error if failed' do
project = create(:project, :import_failed)
- project.add_master(user)
- project.import_state.update_attributes(last_error: 'error')
+ project.add_maintainer(user)
+ project.import_state.update(last_error: 'error')
get api("/projects/#{project.id}/import", user)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index abf9ad738bd..f72c01561d8 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -40,7 +40,7 @@ describe API::Projects do
create(:project_member,
user: user4,
project: project3,
- access_level: ProjectMember::MASTER)
+ access_level: ProjectMember::MAINTAINER)
end
let(:project4) do
create(:project,
@@ -237,6 +237,39 @@ describe API::Projects do
end
end
+ context 'and using archived' do
+ let!(:archived_project) { create(:project, creator_id: user.id, namespace: user.namespace, archived: true) }
+
+ it 'returns archived projects' do
+ get api('/projects?archived=true', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(Project.public_or_visible_to_user(user).where(archived: true).size)
+ expect(json_response.map { |project| project['id'] }).to include(archived_project.id)
+ end
+
+ it 'returns non-archived projects' do
+ get api('/projects?archived=false', user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(Project.public_or_visible_to_user(user).where(archived: false).size)
+ expect(json_response.map { |project| project['id'] }).not_to include(archived_project.id)
+ end
+
+ it 'returns every project' do
+ get api('/projects', user)
+
+ 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 { |project| project['id'] }).to contain_exactly(*Project.public_or_visible_to_user(user).pluck(:id))
+ end
+ end
+
context 'and using search' do
it_behaves_like 'projects response' do
let(:filter) { { search: project.name } }
@@ -312,7 +345,7 @@ describe API::Projects do
before do
project_member
- user3.update_attributes(starred_projects: [project, project2, project3, public_project])
+ user3.update(starred_projects: [project, project2, project3, public_project])
end
it 'returns the starred projects viewable by the user' do
@@ -333,7 +366,7 @@ describe API::Projects do
let!(:project9) { create(:project, :public, path: 'gitlab9') }
before do
- user.update_attributes(starred_projects: [project5, project7, project8, project9])
+ user.update(starred_projects: [project5, project7, project8, project9])
end
context 'including owned filter' do
@@ -353,7 +386,7 @@ describe API::Projects do
create(:project_member,
user: user,
project: project5,
- access_level: ProjectMember::MASTER)
+ access_level: ProjectMember::MAINTAINER)
end
it 'returns only projects that satisfy all query parameters' do
@@ -961,7 +994,7 @@ describe API::Projects do
describe 'permissions' do
context 'all projects' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'contains permission information' do
@@ -969,19 +1002,19 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(200)
expect(json_response.first['permissions']['project_access']['access_level'])
- .to eq(Gitlab::Access::MASTER)
+ .to eq(Gitlab::Access::MAINTAINER)
expect(json_response.first['permissions']['group_access']).to be_nil
end
end
context 'personal project' do
it 'sets project access and returns 200' do
- project.add_master(user)
+ project.add_maintainer(user)
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['permissions']['project_access']['access_level'])
- .to eq(Gitlab::Access::MASTER)
+ .to eq(Gitlab::Access::MAINTAINER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
@@ -1451,7 +1484,7 @@ describe API::Projects do
end
it 'updates visibility_level from public to private' do
- project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
+ project3.update({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
project_param = { visibility: 'private' }
put api("/projects/#{project3.id}", user), project_param
@@ -1526,9 +1559,23 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'updates avatar' do
+ project_param = {
+ avatar: fixture_file_upload('spec/fixtures/banana_sample.gif',
+ 'image/gif')
+ }
+
+ put api("/projects/#{project3.id}", user), project_param
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
+ '-/system/project/avatar/'\
+ "#{project3.id}/banana_sample.gif")
+ end
end
- context 'when authenticated as project master' do
+ context 'when authenticated as project maintainer' do
it 'updates path' do
project_param = { path: 'bar' }
put api("/projects/#{project3.id}", user4), project_param
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 576fde46615..69a601d7b40 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -26,9 +26,9 @@ describe API::ProtectedBranches do
end
end
- context 'when authenticated as a master' do
+ context 'when authenticated as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'protected branches'
@@ -54,8 +54,8 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(::Gitlab::Access::MAINTAINER)
end
context 'when protected branch does not exist' do
@@ -68,9 +68,9 @@ describe API::ProtectedBranches do
end
end
- context 'when authenticated as a master' do
+ context 'when authenticated as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'protected branch'
@@ -108,9 +108,9 @@ describe API::ProtectedBranches do
expect(json_response['name']).to eq(branch_name)
end
- context 'when authenticated as a master' do
+ context 'when authenticated as a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'protects a single branch' do
@@ -118,8 +118,8 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
it 'protects a single branch and developers can push' do
@@ -128,7 +128,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
it 'protects a single branch and developers can merge' do
@@ -136,7 +136,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::DEVELOPER)
end
@@ -155,7 +155,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
it 'protects a single branch and no one can merge' do
@@ -163,7 +163,7 @@ describe API::ProtectedBranches do
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(branch_name)
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::NO_ACCESS)
end
@@ -189,8 +189,8 @@ describe API::ProtectedBranches do
post post_endpoint, name: branch_name
expect_protection_to_be_successful
- expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
- expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MASTER)
+ expect(json_response['push_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
+ expect(json_response['merge_access_levels'][0]['access_level']).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -225,7 +225,7 @@ describe API::ProtectedBranches do
let(:delete_endpoint) { api("/projects/#{project.id}/protected_branches/#{branch_name}", user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it "unprotects a single branch" do
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 28f8564ae92..6063afc213d 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -8,7 +8,7 @@ describe API::Repositories do
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } }
let!(:project) { create(:project, :repository, creator: user) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
+ let!(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
describe "GET /projects/:id/repository/tree" do
let(:route) { "/projects/#{project.id}/repository/tree" }
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index b5e4b6011ea..3ebdb54f71f 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -18,8 +18,8 @@ describe API::Runners do
before do
# Set project access for users
- create(:project_member, :master, user: user, project: project)
- create(:project_member, :master, user: user, project: project2)
+ create(:project_member, :maintainer, user: user, project: project)
+ create(:project_member, :maintainer, user: user, project: project2)
create(:project_member, :reporter, user: user2, project: project)
end
@@ -211,6 +211,69 @@ describe API::Runners do
describe 'PUT /runners/:id' do
context 'admin user' do
+ # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48625
+ context 'single parameter update' do
+ it 'runner description' do
+ description = shared_runner.description
+ update_runner(shared_runner.id, admin, description: "#{description}_updated")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.description).to eq("#{description}_updated")
+ end
+
+ it 'runner active state' do
+ active = shared_runner.active
+ update_runner(shared_runner.id, admin, active: !active)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.active).to eq(!active)
+ end
+
+ it 'runner tag list' do
+ update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
+ end
+
+ it 'runner untagged flag' do
+ # Ensure tag list is non-empty before setting untagged to false.
+ update_runner(shared_runner.id, admin, tag_list: ['ruby2.1', 'pgsql', 'mysql'])
+ update_runner(shared_runner.id, admin, run_untagged: 'false')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.run_untagged?).to be(false)
+ end
+
+ it 'runner unlocked flag' do
+ update_runner(shared_runner.id, admin, locked: 'true')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.locked?).to be(true)
+ end
+
+ it 'runner access level' do
+ update_runner(shared_runner.id, admin, access_level: 'ref_protected')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.ref_protected?).to be_truthy
+ end
+
+ it 'runner maximum timeout' do
+ update_runner(shared_runner.id, admin, maximum_timeout: 1234)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(shared_runner.reload.maximum_timeout).to eq(1234)
+ end
+
+ it 'fails with no parameters' do
+ put api("/runners/#{shared_runner.id}", admin)
+
+ shared_runner.reload
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+
context 'when runner is shared' do
it 'updates runner' do
description = shared_runner.description
@@ -513,7 +576,7 @@ describe API::Runners do
end
describe 'GET /projects/:id/runners' do
- context 'authorized user with master privileges' do
+ context 'authorized user with maintainer privileges' do
it "returns project's runners" do
get api("/projects/#{project.id}/runners", user)
@@ -526,7 +589,7 @@ describe API::Runners do
end
end
- context 'authorized user without master privileges' do
+ context 'authorized user without maintainer privileges' do
it "does not return project's runners" do
get api("/projects/#{project.id}/runners", user2)
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 969710d6613..98f995df06f 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -10,7 +10,7 @@ describe API::Tags do
let(:current_user) { nil }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'GET /projects/:id/repository/tags' do
@@ -86,7 +86,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository tags'
@@ -109,7 +109,7 @@ describe API::Tags do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ release.update(description: description)
end
it 'returns an array of project tags with release info' do
@@ -168,7 +168,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository tag'
@@ -222,7 +222,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
context "when a protected branch doesn't already exist" do
@@ -341,7 +341,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository delete tag'
@@ -386,7 +386,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository new release'
@@ -400,7 +400,7 @@ describe API::Tags do
context 'on tag with existing release' do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ release.update(description: description)
end
it 'returns 409 if there is already a release' do
@@ -422,7 +422,7 @@ describe API::Tags do
context 'on tag with existing release' do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
- release.update_attributes(description: description)
+ release.update(description: description)
end
it 'updates the release description' do
@@ -452,7 +452,7 @@ describe API::Tags do
end
end
- context 'when authenticated', 'as a master' do
+ context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
it_behaves_like 'repository update release'
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index b2c56f7af2c..0ae6796d1e4 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -6,7 +6,7 @@ describe API::Triggers do
let!(:trigger_token) { 'secure_token' }
let!(:trigger_token_2) { 'secure_token_2' }
let!(:project) { create(:project, :repository, creator: user) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
+ let!(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token, owner: user) }
let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2, owner: user2) }
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index 62215ea3d7d..be333df1d78 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -4,7 +4,7 @@ describe API::Variables do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
- let!(:master) { create(:project_member, :master, user: user, project: project) }
+ let!(:maintainer) { create(:project_member, :maintainer, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:variable) { create(:ci_variable, project: project) }
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index 850ba696098..489cb001b82 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -7,7 +7,7 @@ require 'spec_helper'
# Every state is tested for 3 user roles:
# - guest
# - developer
-# - master
+# - maintainer
# because they are 3 edge cases of using wiki pages.
describe API::Wikis do
@@ -163,9 +163,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -193,9 +193,9 @@ describe API::Wikis do
include_examples 'returns list of wiki pages'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'returns list of wiki pages'
@@ -221,9 +221,9 @@ describe API::Wikis do
include_examples 'returns list of wiki pages'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'returns list of wiki pages'
@@ -256,9 +256,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -293,9 +293,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -337,9 +337,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
get api(url, user)
end
@@ -379,9 +379,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
post(api(url, user), payload)
end
@@ -408,9 +408,9 @@ describe API::Wikis do
include_examples 'creates wiki page'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'creates wiki page'
@@ -436,9 +436,9 @@ describe API::Wikis do
include_examples 'creates wiki page'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
include_examples 'creates wiki page'
@@ -472,9 +472,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
put(api(url, user), payload)
end
@@ -510,9 +510,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
put(api(url, user), payload)
end
@@ -554,9 +554,9 @@ describe API::Wikis do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
put(api(url, user), payload)
end
@@ -607,9 +607,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
delete(api(url, user))
end
@@ -639,9 +639,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
delete(api(url, user))
end
@@ -671,9 +671,9 @@ describe API::Wikis do
include_examples '403 Forbidden'
end
- context 'when user is master' do
+ context 'when user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
delete(api(url, user))
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 92fcfb65269..b030d9862c6 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -312,7 +312,7 @@ describe 'Git HTTP requests' do
let(:project) { fork_project(canonical_project, nil, repository: true) }
before do
- canonical_project.add_master(user)
+ canonical_project.add_maintainer(user)
create(:merge_request,
source_project: project,
target_project: canonical_project,
@@ -398,13 +398,13 @@ describe 'Git HTTP requests' do
context "when the user has access to the project" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "when the user is blocked" do
it "rejects pulls with 401 Unauthorized" do
user.block
- project.add_master(user)
+ project.add_maintainer(user)
download(path, env) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
@@ -467,7 +467,7 @@ describe 'Git HTTP requests' do
let(:path) { "#{project.full_path}.git" }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when username and password are provided' do
@@ -827,7 +827,7 @@ describe 'Git HTTP requests' do
context 'and the user is on the team' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it "responds with status 200" do
@@ -850,7 +850,7 @@ describe 'Git HTTP requests' do
let(:env) { { user: user.username, password: user.password } }
before do
- project.add_master(user)
+ project.add_maintainer(user)
enforce_terms
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 4d30b99262e..de39abdb746 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -63,7 +63,7 @@ describe 'Git LFS API and storage' do
context 'with LFS disabled globally' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
end
@@ -106,7 +106,7 @@ describe 'Git LFS API and storage' do
context 'with LFS enabled globally' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
enable_lfs
end
@@ -236,7 +236,7 @@ describe 'Git LFS API and storage' do
context 'and does have project access' do
let(:update_permissions) do
- project.add_master(user)
+ project.add_maintainer(user)
project.lfs_objects << lfs_object
end
@@ -293,7 +293,7 @@ describe 'Git LFS API and storage' do
context 'when user allowed' do
let(:update_permissions) do
- project.add_master(user)
+ project.add_maintainer(user)
project.lfs_objects << lfs_object
end
@@ -829,7 +829,7 @@ describe 'Git LFS API and storage' do
context 'when user is not authenticated' do
context 'when user has push access' do
let(:update_user_permissions) do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'responds with status 401' do
@@ -874,7 +874,7 @@ describe 'Git LFS API and storage' do
before do
allow(Gitlab::Database).to receive(:read_only?) { true }
- project.add_master(user)
+ project.add_maintainer(user)
enable_lfs
end
@@ -1307,7 +1307,7 @@ describe 'Git LFS API and storage' do
let(:authorization) { authorize_user }
before do
- second_project.add_master(user)
+ second_project.add_maintainer(user)
upstream_project.lfs_objects << lfs_object
end
diff --git a/spec/requests/lfs_locks_api_spec.rb b/spec/requests/lfs_locks_api_spec.rb
index e44a11a7232..a44b43a591f 100644
--- a/spec/requests/lfs_locks_api_spec.rb
+++ b/spec/requests/lfs_locks_api_spec.rb
@@ -4,7 +4,7 @@ describe 'Git LFS File Locking API' do
include WorkhorseHelpers
let(:project) { create(:project) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:developer) { create(:user) }
let(:guest) { create(:user) }
let(:path) { 'README.md' }
@@ -29,7 +29,7 @@ describe 'Git LFS File Locking API' do
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- project.add_developer(master)
+ project.add_developer(maintainer)
project.add_developer(developer)
project.add_guest(guest)
end
@@ -99,7 +99,7 @@ describe 'Git LFS File Locking API' do
include_examples 'unauthorized request'
it 'returns the list of locked files grouped by owner' do
- lock_file('README.md', master)
+ lock_file('README.md', maintainer)
lock_file('README', developer)
post_lfs_json url, nil, headers
diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb
index 2bd8162d1b7..01264cf7fb5 100644
--- a/spec/serializers/deploy_key_entity_spec.rb
+++ b/spec/serializers/deploy_key_entity_spec.rb
@@ -44,9 +44,9 @@ describe DeployKeyEntity do
it { expect(entity.as_json).to eq(expected_result) }
end
- describe 'returns can_edit true if user is a master of project' do
+ describe 'returns can_edit true if user is a maintainer of project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { expect(entity.as_json).to include(can_edit: true) }
diff --git a/spec/serializers/environment_entity_spec.rb b/spec/serializers/environment_entity_spec.rb
index 8f32c5639a1..b7324a26ed2 100644
--- a/spec/serializers/environment_entity_spec.rb
+++ b/spec/serializers/environment_entity_spec.rb
@@ -1,8 +1,9 @@
require 'spec_helper'
describe EnvironmentEntity do
+ let(:request) { double('request') }
let(:entity) do
- described_class.new(environment, request: double)
+ described_class.new(environment, request: spy('request'))
end
let(:environment) { create(:environment) }
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index ca9b520fb38..0f0ab5ac796 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -54,7 +54,9 @@ describe EnvironmentSerializer do
context 'when representing environments within folders' do
let(:serializer) do
- described_class.new(project: project).within_folders
+ described_class
+ .new(current_user: user, project: project)
+ .within_folders
end
let(:resource) { Environment.all }
@@ -123,7 +125,8 @@ describe EnvironmentSerializer do
let(:pagination) { { page: 1, per_page: 2 } }
let(:serializer) do
- described_class.new(project: project)
+ described_class
+ .new(current_user: user, project: project)
.with_pagination(request, response)
end
@@ -169,7 +172,8 @@ describe EnvironmentSerializer do
context 'when grouping environments within folders' do
let(:serializer) do
- described_class.new(project: project)
+ described_class
+ .new(current_user: user, project: project)
.with_pagination(request, response)
.within_folders
end
diff --git a/spec/serializers/group_child_entity_spec.rb b/spec/serializers/group_child_entity_spec.rb
index 505a9eaac5a..dbc40bddc30 100644
--- a/spec/serializers/group_child_entity_spec.rb
+++ b/spec/serializers/group_child_entity_spec.rb
@@ -42,7 +42,7 @@ describe GroupChildEntity do
end
before do
- object.add_master(user)
+ object.add_maintainer(user)
end
it 'has the correct type' do
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index d2072198d83..0ba2539a717 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -11,6 +11,21 @@ describe MergeRequestWidgetEntity do
described_class.new(resource, request: request).as_json
end
+ describe 'source_project_full_path' do
+ it 'includes the full path of the source project' do
+ expect(subject[:source_project_full_path]).to be_present
+ end
+
+ context 'when the source project is missing' do
+ it 'returns `nil` for the source project' do
+ resource.allow_broken = true
+ resource.update!(source_project: nil)
+
+ expect(subject[:source_project_full_path]).to be_nil
+ end
+ end
+ end
+
describe 'pipeline' do
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.source_branch, sha: resource.source_branch_sha, head_pipeline_of: resource) }
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index fce73e0ac1f..037484931b8 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -348,7 +348,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
end
- context 'delete authorized as master' do
+ context 'delete authorized as maintainer' do
let(:current_project) { create(:project) }
let(:current_user) { create(:user) }
@@ -357,7 +357,7 @@ describe Auth::ContainerRegistryAuthenticationService do
end
before do
- current_project.add_master(current_user)
+ current_project.add_maintainer(current_user)
end
it_behaves_like 'a valid token'
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 2b88fcc9a96..054b7b1561c 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -462,11 +462,11 @@ describe Ci::CreatePipelineService do
end
end
- context 'when user is master' do
+ context 'when user is maintainer' do
let(:pipeline) { execute_service }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'creates a protected pipeline' do
@@ -503,13 +503,13 @@ describe Ci::CreatePipelineService do
end
end
- context 'when trigger belongs to a master' do
+ context 'when trigger belongs to a maintainer' do
let(:user) { create(:user) }
let(:trigger) { create(:ci_trigger, owner: user) }
let(:trigger_request) { create(:ci_trigger_request, trigger: trigger) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'creates a pipeline' do
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index decb5d22f59..ecf5d849d3f 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -49,7 +49,7 @@ describe Ci::RetryBuildService do
# Make sure that build has both `stage_id` and `stage` because FactoryBot
# can reset one of the fields when assigning another. We plan to deprecate
# and remove legacy `stage` column in the future.
- build.update_attributes(stage: 'test', stage_id: stage.id)
+ build.update(stage: 'test', stage_id: stage.id)
end
describe 'clone accessors' do
@@ -100,7 +100,11 @@ describe Ci::RetryBuildService do
end
describe '#execute' do
- let(:new_build) { service.execute(build) }
+ let(:new_build) do
+ Timecop.freeze(1.second.from_now) do
+ service.execute(build)
+ end
+ end
context 'when user has ability to execute build' do
before do
@@ -150,7 +154,11 @@ describe Ci::RetryBuildService do
end
describe '#reprocess' do
- let(:new_build) { service.reprocess!(build) }
+ let(:new_build) do
+ Timecop.freeze(1.second.from_now) do
+ service.reprocess!(build)
+ end
+ end
context 'when user has ability to execute build' do
before do
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 688d3b8c038..55445e71539 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -237,7 +237,7 @@ describe Ci::RetryPipelineService, '#execute' do
context 'when user is not allowed to trigger manual action' do
before do
project.add_developer(user)
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: pipeline.ref, project: project)
end
@@ -275,7 +275,7 @@ describe Ci::RetryPipelineService, '#execute' do
let(:pipeline) { create(:ci_pipeline, project: forked_project, ref: 'fixes') }
before do
- project.add_master(user)
+ project.add_maintainer(user)
create(:merge_request,
source_project: forked_project,
target_project: project,
diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb
index 3fc4e499b0c..cdd3d851f61 100644
--- a/spec/services/ci/stop_environments_service_spec.rb
+++ b/spec/services/ci/stop_environments_service_spec.rb
@@ -86,7 +86,7 @@ describe Ci::StopEnvironmentsService do
context 'when user has permission to stop environments' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'does not stop environment' do
diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb
index 3895a0b3aea..4e0d4749239 100644
--- a/spec/services/discussions/resolve_service_spec.rb
+++ b/spec/services/discussions/resolve_service_spec.rb
@@ -9,7 +9,7 @@ describe Discussions::ResolveService do
let(:service) { described_class.new(discussion.noteable.project, user, merge_request: merge_request) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it "doesn't resolve discussions the user can't resolve" do
diff --git a/spec/services/files/create_service_spec.rb b/spec/services/files/create_service_spec.rb
index abe99b9e794..30d94e4318d 100644
--- a/spec/services/files/create_service_spec.rb
+++ b/spec/services/files/create_service_spec.rb
@@ -23,7 +23,7 @@ describe Files::CreateService do
subject { described_class.new(project, user, commit_params) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "#execute" do
diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb
index ace5f293097..73566afe8c8 100644
--- a/spec/services/files/delete_service_spec.rb
+++ b/spec/services/files/delete_service_spec.rb
@@ -37,7 +37,7 @@ describe Files::DeleteService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "#execute" do
diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb
index 59984c10990..3bdedaf3770 100644
--- a/spec/services/files/multi_service_spec.rb
+++ b/spec/services/files/multi_service_spec.rb
@@ -38,7 +38,7 @@ describe Files::MultiService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index eaee89fb1a5..e01fe487ffa 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -24,7 +24,7 @@ describe Files::UpdateService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe "#execute" do
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 35826de5814..3f62e7e61e1 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -11,7 +11,7 @@ describe GitPushService, services: true do
let(:ref) { 'refs/heads/master' }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'with remote mirrors' do
@@ -267,8 +267,8 @@ describe GitPushService, services: true do
expect(project.default_branch).to eq("master")
execute_service(project, user, blankrev, 'newrev', ref)
expect(project.protected_branches).not_to be_empty
- expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
- expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
it "when pushing a branch for the first time with default branch protection disabled" do
@@ -290,7 +290,7 @@ describe GitPushService, services: true do
expect(project.protected_branches).not_to be_empty
expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
it "when pushing a branch for the first time with an existing branch permission configured" do
@@ -315,7 +315,7 @@ describe GitPushService, services: true do
expect(project.default_branch).to eq("master")
execute_service(project, user, blankrev, 'newrev', ref)
expect(project.protected_branches).not_to be_empty
- expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
end
@@ -442,7 +442,7 @@ describe GitPushService, services: true do
allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
.and_return(closing_commit)
- project.add_master(commit_author)
+ project.add_maintainer(commit_author)
end
context "to default branches" do
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index 1737fd0a9fc..48d689e11d4 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -12,7 +12,7 @@ describe Groups::UpdateService do
let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
before do
- public_group.add_user(user, Gitlab::Access::MASTER)
+ public_group.add_user(user, Gitlab::Access::MAINTAINER)
create(:project, :public, group: public_group)
end
@@ -26,7 +26,7 @@ describe Groups::UpdateService do
let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
- internal_group.add_user(user, Gitlab::Access::MASTER)
+ internal_group.add_user(user, Gitlab::Access::MAINTAINER)
create(:project, :internal, group: internal_group)
end
@@ -55,7 +55,7 @@ describe Groups::UpdateService do
context "unauthorized visibility_level validation" do
let!(:service) { described_class.new(internal_group, user, visibility_level: 99) }
before do
- internal_group.add_user(user, Gitlab::Access::MASTER)
+ internal_group.add_user(user, Gitlab::Access::MAINTAINER)
end
it "does not change permission level" do
@@ -68,7 +68,7 @@ describe Groups::UpdateService do
let!(:service) { described_class.new(internal_group, user, path: SecureRandom.hex) }
before do
- internal_group.add_user(user, Gitlab::Access::MASTER)
+ internal_group.add_user(user, Gitlab::Access::MAINTAINER)
create(:project, :internal, group: internal_group)
end
diff --git a/spec/services/import_export_clean_up_service_spec.rb b/spec/services/import_export_clean_up_service_spec.rb
index 1875d0448cd..d5fcef1246f 100644
--- a/spec/services/import_export_clean_up_service_spec.rb
+++ b/spec/services/import_export_clean_up_service_spec.rb
@@ -11,7 +11,6 @@ describe ImportExportCleanUpService do
path = '/invalid/path/'
stub_repository_downloads_path(path)
- expect(File).to receive(:directory?).with(path + tmp_import_export_folder).and_return(false).at_least(:once)
expect(service).not_to receive(:clean_up_export_files)
service.execute
@@ -38,6 +37,24 @@ describe ImportExportCleanUpService do
end
end
+ context 'with uploader exports' do
+ it 'removes old files' do
+ upload = create(:import_export_upload,
+ updated_at: 2.days.ago,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+
+ expect { service.execute }.to change { upload.reload.export_file.file.nil? }.to(true)
+ end
+
+ it 'does not remove new files' do
+ upload = create(:import_export_upload,
+ updated_at: 1.hour.ago,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+
+ expect { service.execute }.not_to change { upload.reload.export_file.file.nil? }
+ end
+ end
+
def in_directory_with_files(mtime:)
Dir.mktmpdir do |tmpdir|
stub_repository_downloads_path(tmpdir)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 7ae49c06896..5e38d0aeb6a 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -9,7 +9,7 @@ describe Issues::CloseService do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: issue, author: user2) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_guest(guest)
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 79bcdc41fb0..c61c1ddcb3d 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -13,8 +13,8 @@ describe Issues::CreateService do
let(:labels) { create_pair(:label, project: project) }
before do
- project.add_master(user)
- project.add_master(assignee)
+ project.add_maintainer(user)
+ project.add_maintainer(assignee)
end
let(:opts) do
@@ -130,7 +130,7 @@ describe Issues::CreateService do
end
it 'invalidates open issues counter for assignees when issue is assigned' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
described_class.new(project, user, opts).execute
@@ -160,7 +160,7 @@ describe Issues::CreateService do
context 'issue create service' do
context 'assignees' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'removes assignee when user id is invalid' do
@@ -180,7 +180,7 @@ describe Issues::CreateService do
end
it 'saves assignee when user id is valid' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
opts = { title: 'Title', description: 'Description', assignee_ids: [assignee.id] }
issue = described_class.new(project, user, opts).execute
@@ -224,8 +224,8 @@ describe Issues::CreateService do
end
before do
- project.add_master(user)
- project.add_master(assignee)
+ project.add_maintainer(user)
+ project.add_maintainer(assignee)
end
it 'assigns and sets milestone to issuable from command' do
@@ -242,7 +242,7 @@ describe Issues::CreateService do
let(:project) { merge_request.source_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe 'for a single discussion' do
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index 42e5d544f4c..3b617d4ca54 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -24,7 +24,7 @@ describe Issues::ReopenService do
let(:user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'invalidates counter cache for assignees' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 158541d36e3..fa98d05c61b 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -18,7 +18,7 @@ describe Issues::UpdateService, :mailer do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_developer(user3)
end
@@ -501,7 +501,7 @@ describe Issues::UpdateService, :mailer do
let(:params) { { label_ids: [], remove_label_ids: [label.id] } }
before do
- issue.update_attributes(labels: [label, label3])
+ issue.update(labels: [label, label3])
end
it 'ignores the label_ids parameter' do
@@ -517,7 +517,7 @@ describe Issues::UpdateService, :mailer do
let(:params) { { add_label_ids: [label3.id], remove_label_ids: [label.id] } }
before do
- issue.update_attributes(labels: [label])
+ issue.update(labels: [label])
end
it 'adds the passed labels' do
@@ -596,7 +596,7 @@ describe Issues::UpdateService, :mailer do
context 'valid project' do
before do
- target_project.add_master(user)
+ target_project.add_maintainer(user)
end
it 'calls the move service with the proper issue and project' do
diff --git a/spec/services/lfs/unlock_file_service_spec.rb b/spec/services/lfs/unlock_file_service_spec.rb
index 948401d7bdc..539417644db 100644
--- a/spec/services/lfs/unlock_file_service_spec.rb
+++ b/spec/services/lfs/unlock_file_service_spec.rb
@@ -62,11 +62,11 @@ describe Lfs::UnlockFileService do
context 'when forced' do
let(:developer) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
before do
project.add_developer(developer)
- project.add_master(master)
+ project.add_maintainer(maintainer)
end
context 'by a regular user' do
@@ -86,7 +86,7 @@ describe Lfs::UnlockFileService do
end
context 'by a maintainer user' do
- let(:current_user) { master }
+ let(:current_user) { maintainer }
let(:params) do
{ id: lock.id,
force: true }
diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb
index 7076571b753..5c30f5b6a61 100644
--- a/spec/services/members/approve_access_request_service_spec.rb
+++ b/spec/services/members/approve_access_request_service_spec.rb
@@ -34,9 +34,9 @@ describe Members::ApproveAccessRequestService do
context 'with a custom access level' do
it 'returns a ProjectMember with the custom access level' do
- member = described_class.new(current_user, access_level: Gitlab::Access::MASTER).execute(access_requester, opts)
+ member = described_class.new(current_user, access_level: Gitlab::Access::MAINTAINER).execute(access_requester, opts)
- expect(member.access_level).to eq(Gitlab::Access::MASTER)
+ expect(member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
end
@@ -97,7 +97,7 @@ describe Members::ApproveAccessRequestService do
context 'when current user can approve access request to the project' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
group.add_owner(current_user)
end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 1831c62d788..5c01463d757 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -6,7 +6,7 @@ describe Members::CreateService do
let(:project_user) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'adds user to members' do
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 36b6e5a701e..ef47b0a450b 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -114,7 +114,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given member' do
before do
- group_project.add_master(current_user)
+ group_project.add_maintainer(current_user)
group.add_owner(current_user)
end
@@ -142,8 +142,8 @@ describe Members::DestroyService do
context 'with an access requester' do
before do
- group_project.update_attributes(request_access_enabled: true)
- group.update_attributes(request_access_enabled: true)
+ group_project.update(request_access_enabled: true)
+ group.update(request_access_enabled: true)
group_project.request_access(member_user)
group.request_access(member_user)
end
@@ -170,7 +170,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given access requester' do
before do
- group_project.add_master(current_user)
+ group_project.add_maintainer(current_user)
group.add_owner(current_user)
end
@@ -210,7 +210,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given invited user' do
before do
- group_project.add_master(current_user)
+ group_project.add_maintainer(current_user)
group.add_owner(current_user)
end
diff --git a/spec/services/members/update_service_spec.rb b/spec/services/members/update_service_spec.rb
index a451272dd1f..6d19a95ffeb 100644
--- a/spec/services/members/update_service_spec.rb
+++ b/spec/services/members/update_service_spec.rb
@@ -8,7 +8,7 @@ describe Members::UpdateService do
let(:permission) { :update }
let(:member) { source.members_and_requesters.find_by!(user_id: member_user.id) }
let(:params) do
- { access_level: Gitlab::Access::MASTER }
+ { access_level: Gitlab::Access::MAINTAINER }
end
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
@@ -23,7 +23,7 @@ describe Members::UpdateService do
updated_member = described_class.new(current_user, params).execute(member, permission: permission)
expect(updated_member).to be_valid
- expect(updated_member.access_level).to eq(Gitlab::Access::MASTER)
+ expect(updated_member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -44,7 +44,7 @@ describe Members::UpdateService do
context 'when current user can update the given member' do
before do
- project.add_master(current_user)
+ project.add_maintainer(current_user)
group.add_owner(current_user)
end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 216e0cd4266..433ffbd97f0 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -9,7 +9,7 @@ describe MergeRequests::CloseService do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: merge_request, author: user2) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_guest(guest)
end
diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb
index d57852615d9..c81fa95e4b7 100644
--- a/spec/services/merge_requests/conflicts/list_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/list_service_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
describe MergeRequests::Conflicts::ListService do
describe '#can_be_resolved_in_ui?' do
- def create_merge_request(source_branch)
- create(:merge_request, source_branch: source_branch, target_branch: 'conflict-start', merge_status: :unchecked) do |mr|
+ def create_merge_request(source_branch, target_branch = 'conflict-start')
+ create(:merge_request, source_branch: source_branch, target_branch: target_branch, merge_status: :unchecked) do |mr|
mr.mark_as_unmergeable
end
end
@@ -34,7 +34,7 @@ describe MergeRequests::Conflicts::ListService do
it 'returns a falsey value when the MR does not support new diff notes' do
merge_request = create_merge_request('conflict-resolvable')
- merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
+ merge_request.merge_request_diff.update(start_commit_sha: nil)
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
end
@@ -85,22 +85,10 @@ describe MergeRequests::Conflicts::ListService do
expect(service.can_be_resolved_in_ui?).to be_falsey
end
- context 'with gitaly disabled', :skip_gitaly_mock do
- it 'returns a falsey value when the MR has a missing ref after a force push' do
- merge_request = create_merge_request('conflict-resolvable')
- service = conflicts_service(merge_request)
- allow_any_instance_of(Rugged::Repository).to receive(:merge_commits).and_raise(Rugged::OdbError)
+ it 'returns a falsey value when the conflict is in a submodule revision' do
+ merge_request = create_merge_request('update-gitlab-shell-v-6-0-3', 'update-gitlab-shell-v-6-0-1')
- expect(service.can_be_resolved_in_ui?).to be_falsey
- end
-
- it 'returns a falsey value when the MR has a missing revision after a force push' do
- merge_request = create_merge_request('conflict-resolvable')
- service = conflicts_service(merge_request)
- allow(merge_request).to receive_message_chain(:target_branch_head, :raw, :id).and_return(Gitlab::Git::BLANK_SHA)
-
- expect(service.can_be_resolved_in_ui?).to be_falsey
- end
+ expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
end
end
end
diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
index cff09237005..7edf8a96c94 100644
--- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
@@ -123,17 +123,6 @@ describe MergeRequests::Conflicts::ResolveService do
expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
.to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
end
-
- context 'when gitaly is disabled', :skip_gitaly_mock do
- it 'gets conflicts from the source project' do
- # REFACTOR NOTE: We used to test that `project.repository.rugged` wasn't
- # used in this case, but since the refactor, for simplification,
- # we always use that repository for read only operations.
- expect(forked_project.repository.rugged).to receive(:merge_commits).and_call_original
-
- subject
- end
- end
end
end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 736a50b2c15..06fb61baf33 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -23,7 +23,7 @@ describe MergeRequests::CreateService do
let(:merge_request) { service.execute }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(assignee)
allow(service).to receive(:execute_hooks)
end
@@ -185,8 +185,8 @@ describe MergeRequests::CreateService do
end
before do
- project.add_master(user)
- project.add_master(assignee)
+ project.add_maintainer(user)
+ project.add_maintainer(assignee)
end
it 'assigns and sets milestone to issuable from command' do
@@ -202,7 +202,7 @@ describe MergeRequests::CreateService do
let(:assignee) { create(:user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'removes assignee_id when user id is invalid' do
@@ -222,7 +222,7 @@ describe MergeRequests::CreateService do
end
it 'saves assignee when user id is valid' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
opts = { title: 'Title', description: 'Description', assignee_id: assignee.id }
merge_request = described_class.new(project, user, opts).execute
@@ -242,7 +242,7 @@ describe MergeRequests::CreateService do
end
it 'invalidates open merge request counter for assignees when merge request is assigned' do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
described_class.new(project, user, opts).execute
@@ -286,7 +286,7 @@ describe MergeRequests::CreateService do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(assignee)
end
@@ -316,7 +316,7 @@ describe MergeRequests::CreateService do
context 'when user can not access source project' do
before do
target_project.add_developer(assignee)
- target_project.add_master(user)
+ target_project.add_maintainer(user)
end
it 'raises an error' do
@@ -328,7 +328,7 @@ describe MergeRequests::CreateService do
context 'when user can not access target project' do
before do
target_project.add_developer(assignee)
- target_project.add_master(user)
+ target_project.add_maintainer(user)
end
it 'raises an error' do
@@ -372,7 +372,7 @@ describe MergeRequests::CreateService do
before do
project.add_developer(assignee)
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'ignores source_project_id' do
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index 28f56d19657..fe673de46aa 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -13,7 +13,7 @@ describe MergeRequests::FfMergeService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index ef2738ef504..9dd235f6660 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -7,7 +7,7 @@ describe MergeRequests::MergeService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
end
@@ -63,7 +63,7 @@ describe MergeRequests::MergeService do
let(:commit) { double('commit', safe_message: "Fixes #{jira_issue.to_reference}") }
before do
- project.update_attributes!(has_external_issue_tracker: true)
+ project.update!(has_external_issue_tracker: true)
jira_service_settings
stub_jira_urls(jira_issue.id)
allow(merge_request).to receive(:commits).and_return([commit])
diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb
index 46e4e3559dc..ba2b062875b 100644
--- a/spec/services/merge_requests/post_merge_service_spec.rb
+++ b/spec/services/merge_requests/post_merge_service_spec.rb
@@ -6,7 +6,7 @@ describe MergeRequests::PostMergeService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index 4daa25f8cf2..2703da7ae44 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -15,7 +15,7 @@ describe MergeRequests::RebaseService do
subject(:service) { described_class.new(project, user, {}) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index 9ee37c51d95..e10eaa95da4 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -8,7 +8,7 @@ describe MergeRequests::ReopenService do
let(:project) { merge_request.project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_guest(guest)
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 443dcd92a8b..f0029af83cc 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -19,7 +19,7 @@ describe MergeRequests::UpdateService, :mailer do
end
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_developer(user3)
end
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index adad73f7e11..3f7a544ea0a 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -6,7 +6,7 @@ describe Milestones::CloseService do
let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index f2a18c7295a..0c91112026f 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -7,7 +7,7 @@ describe Milestones::CreateService do
describe '#execute' do
context "valid params" do
before do
- project.add_master(user)
+ project.add_maintainer(user)
opts = {
title: 'v2.1.9',
diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb
index 9703780b0e9..6f3612501f4 100644
--- a/spec/services/milestones/destroy_service_spec.rb
+++ b/spec/services/milestones/destroy_service_spec.rb
@@ -8,7 +8,7 @@ describe Milestones::DestroyService do
let!(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
def service
diff --git a/spec/services/milestones/promote_service_spec.rb b/spec/services/milestones/promote_service_spec.rb
index a0a2843b676..df212d912e9 100644
--- a/spec/services/milestones/promote_service_spec.rb
+++ b/spec/services/milestones/promote_service_spec.rb
@@ -10,7 +10,7 @@ describe Milestones::PromoteService do
describe '#execute' do
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
context 'validations' do
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 2b2b983494f..0fd37c95e42 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -10,7 +10,7 @@ describe Notes::CreateService do
describe '#execute' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context "valid params" do
diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb
index 4e2ab919f0f..5aae0d711c3 100644
--- a/spec/services/notes/post_process_service_spec.rb
+++ b/spec/services/notes/post_process_service_spec.rb
@@ -7,7 +7,7 @@ describe Notes::PostProcessService do
describe '#execute' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
note_opts = {
note: 'Awesome comment',
noteable_type: 'Issue',
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index b1e218821d2..784dac55454 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -3,11 +3,11 @@ require 'spec_helper'
describe Notes::QuickActionsService do
shared_context 'note on noteable' do
let(:project) { create(:project) }
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
before do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
end
end
@@ -184,7 +184,7 @@ describe Notes::QuickActionsService do
include_context 'note on noteable'
it 'delegates to the class method' do
- service = described_class.new(project, master)
+ service = described_class.new(project, maintainer)
note = create(:note_on_issue, project: project)
expect(described_class).to receive(:supported?).with(note)
@@ -194,7 +194,7 @@ describe Notes::QuickActionsService do
end
describe '#execute' do
- let(:service) { described_class.new(project, master) }
+ let(:service) { described_class.new(project, maintainer) }
it_behaves_like 'note on noteable that supports quick actions' do
let(:note) { build(:note_on_issue, project: project) }
@@ -212,19 +212,19 @@ describe Notes::QuickActionsService do
context 'CE restriction for issue assignees' do
describe '/assign' do
let(:project) { create(:project) }
- let(:master) { create(:user).tap { |u| project.add_master(u) } }
+ let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
- let(:master) { create(:user) }
- let(:service) { described_class.new(project, master) }
+ let(:maintainer) { create(:user) }
+ let(:service) { described_class.new(project, maintainer) }
let(:note) { create(:note_on_issue, note: note_text, project: project) }
let(:note_text) do
- %(/assign @#{assignee.username} @#{master.username}\n")
+ %(/assign @#{assignee.username} @#{maintainer.username}\n")
end
before do
- project.add_master(master)
- project.add_master(assignee)
+ project.add_maintainer(maintainer)
+ project.add_maintainer(assignee)
end
it 'adds only one assignee from the list' do
diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb
index 65b1d613998..533dcdcd6cd 100644
--- a/spec/services/notes/update_service_spec.rb
+++ b/spec/services/notes/update_service_spec.rb
@@ -9,7 +9,7 @@ describe Notes::UpdateService do
let(:note) { create(:note, project: project, noteable: issue, author: user, note: "Old note #{user2.to_reference}") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
project.add_developer(user2)
project.add_developer(user3)
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 0eadc83bfe3..c442f6fe32f 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -172,9 +172,9 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
- project.add_master(issue.author)
- project.add_master(assignee)
- project.add_master(note.author)
+ project.add_maintainer(issue.author)
+ project.add_maintainer(assignee)
+ project.add_maintainer(note.author)
@u_custom_off = create_user_with_notification(:custom, 'custom_off')
project.add_guest(@u_custom_off)
@@ -255,8 +255,8 @@ describe NotificationService, :mailer do
describe 'new note on issue in project that belongs to a group' do
before do
note.project.namespace_id = group.id
- group.add_user(@u_watcher, GroupMember::MASTER)
- group.add_user(@u_custom_global, GroupMember::MASTER)
+ group.add_user(@u_watcher, GroupMember::MAINTAINER)
+ group.add_user(@u_custom_global, GroupMember::MAINTAINER)
note.project.save
@u_watcher.notification_settings_for(note.project).participating!
@@ -373,7 +373,7 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
build_group(note.project)
- note.project.add_master(note.author)
+ note.project.add_maintainer(note.author)
add_users_with_subscription(note.project, issue)
reset_delivered_emails!
end
@@ -436,7 +436,7 @@ describe NotificationService, :mailer do
project.add_guest(@u_guest_watcher)
project.add_guest(@u_guest_custom)
add_member_for_parent_group(@pg_watcher, project)
- note.project.add_master(note.author)
+ note.project.add_maintainer(note.author)
reset_delivered_emails!
end
@@ -577,8 +577,8 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
- project.add_master(merge_request.author)
- project.add_master(merge_request.assignee)
+ project.add_maintainer(merge_request.author)
+ project.add_maintainer(merge_request.assignee)
end
describe '#new_note' do
@@ -1088,8 +1088,8 @@ describe NotificationService, :mailer do
let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' }
before do
- project.add_master(merge_request.author)
- project.add_master(merge_request.assignee)
+ project.add_maintainer(merge_request.author)
+ project.add_maintainer(merge_request.assignee)
build_team(merge_request.target_project)
add_users_with_subscription(merge_request.target_project, merge_request)
update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
@@ -1314,7 +1314,7 @@ describe NotificationService, :mailer do
describe 'when merge_when_pipeline_succeeds is true' do
before do
- merge_request.update_attributes(
+ merge_request.update(
merge_when_pipeline_succeeds: true,
merge_user: create(:user)
)
@@ -1529,13 +1529,13 @@ describe NotificationService, :mailer do
let(:added_user) { create(:user) }
describe '#new_access_request' do
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:owner) { create(:user) }
let(:developer) { create(:user) }
let!(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_owner(owner)
- group.add_master(master)
+ group.add_maintainer(maintainer)
group.add_developer(developer)
end
end
@@ -1544,11 +1544,11 @@ describe NotificationService, :mailer do
reset_delivered_emails!
end
- it 'sends notification to group owners_and_masters' do
+ it 'sends notification to group owners_and_maintainers' do
group.request_access(added_user)
should_email(owner)
- should_email(master)
+ should_email(maintainer)
should_not_email(developer)
end
end
@@ -1601,11 +1601,11 @@ describe NotificationService, :mailer do
context 'for a project in a user namespace' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
- project.add_master(project.owner)
+ project.add_maintainer(project.owner)
end
end
- it 'sends notification to project owners_and_masters' do
+ it 'sends notification to project owners_and_maintainers' do
project.request_access(added_user)
should_only_email(project.owner)
@@ -1621,7 +1621,7 @@ describe NotificationService, :mailer do
reset_delivered_emails!
end
- it 'sends notification to group owners_and_masters' do
+ it 'sends notification to group owners_and_maintainers' do
project.request_access(added_user)
should_only_email(group_owner)
@@ -1759,11 +1759,11 @@ describe NotificationService, :mailer do
end
before do
- project.add_master(u_member)
- project.add_master(u_watcher)
- project.add_master(u_custom_notification_unset)
- project.add_master(u_custom_notification_enabled)
- project.add_master(u_custom_notification_disabled)
+ project.add_maintainer(u_member)
+ project.add_maintainer(u_watcher)
+ project.add_maintainer(u_custom_notification_unset)
+ project.add_maintainer(u_custom_notification_enabled)
+ project.add_maintainer(u_custom_notification_disabled)
reset_delivered_emails!
end
@@ -1903,15 +1903,15 @@ describe NotificationService, :mailer do
set(:u_blocked) { create(:user, :blocked) }
set(:u_silence) { create_user_with_notification(:disabled, 'silent', project) }
set(:u_owner) { project.owner }
- set(:u_master1) { create(:user) }
- set(:u_master2) { create(:user) }
+ set(:u_maintainer1) { create(:user) }
+ set(:u_maintainer2) { create(:user) }
set(:u_developer) { create(:user) }
before do
- project.add_master(u_blocked)
- project.add_master(u_silence)
- project.add_master(u_master1)
- project.add_master(u_master2)
+ project.add_maintainer(u_blocked)
+ project.add_maintainer(u_silence)
+ project.add_maintainer(u_maintainer1)
+ project.add_maintainer(u_maintainer2)
project.add_developer(u_developer)
reset_delivered_emails!
@@ -1926,12 +1926,12 @@ describe NotificationService, :mailer do
describe "##{sym}" do
subject(:notify!) { notification.send(sym, domain) }
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original
notify!
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
it 'emails nobody if the project is missing' do
@@ -1945,26 +1945,26 @@ describe NotificationService, :mailer do
end
describe '#pages_domain_verification_failed' do
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
notification.pages_domain_verification_failed(domain)
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
end
describe '#pages_domain_enabled' do
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
notification.pages_domain_enabled(domain)
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
end
describe '#pages_domain_disabled' do
- it 'emails current watching masters' do
+ it 'emails current watching maintainers' do
notification.pages_domain_disabled(domain)
- should_only_email(u_master1, u_master2, u_owner)
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
end
end
end
@@ -1988,15 +1988,15 @@ describe NotificationService, :mailer do
@u_guest_watcher = create_user_with_notification(:watch, 'guest_watching')
@u_guest_custom = create_user_with_notification(:custom, 'guest_custom')
- project.add_master(@u_watcher)
- project.add_master(@u_participating)
- project.add_master(@u_participant_mentioned)
- project.add_master(@u_disabled)
- project.add_master(@u_mentioned)
- project.add_master(@u_committer)
- project.add_master(@u_not_mentioned)
- project.add_master(@u_lazy_participant)
- project.add_master(@u_custom_global)
+ project.add_maintainer(@u_watcher)
+ project.add_maintainer(@u_participating)
+ project.add_maintainer(@u_participant_mentioned)
+ project.add_maintainer(@u_disabled)
+ project.add_maintainer(@u_mentioned)
+ project.add_maintainer(@u_committer)
+ project.add_maintainer(@u_not_mentioned)
+ project.add_maintainer(@u_lazy_participant)
+ project.add_maintainer(@u_custom_global)
end
# Users in the project's group but not part of project's team
@@ -2011,7 +2011,7 @@ describe NotificationService, :mailer do
# Group member: global=watch, group=global
@g_global_watcher ||= create_global_setting_for(create(:user), :watch)
- group.add_users([@g_watcher, @g_global_watcher], :master)
+ group.add_users([@g_watcher, @g_global_watcher], :maintainer)
group
end
@@ -2050,7 +2050,7 @@ describe NotificationService, :mailer do
project.reload
- project.group.parent.add_master(user)
+ project.group.parent.add_maintainer(user)
end
def should_email_nested_group_user(user, times: 1, recipients: email_recipients)
@@ -2072,11 +2072,11 @@ describe NotificationService, :mailer do
@subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
@watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
- project.add_master(@subscribed_participant)
- project.add_master(@subscriber)
- project.add_master(@unsubscriber)
- project.add_master(@watcher_and_subscriber)
- project.add_master(@unsubscribed_mentioned)
+ project.add_maintainer(@subscribed_participant)
+ project.add_maintainer(@subscriber)
+ project.add_maintainer(@unsubscriber)
+ project.add_maintainer(@watcher_and_subscriber)
+ project.add_maintainer(@unsubscribed_mentioned)
issuable.subscriptions.create(user: @unsubscribed_mentioned, project: project, subscribed: false)
issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true)
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index 64a9559791f..81dc7c57f4a 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -64,4 +64,16 @@ describe PreviewMarkdownService do
expect(result[:commands]).to eq 'Sets time estimate to 2y.'
end
end
+
+ it 'sets correct markdown engine' do
+ service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION })
+ result = service.execute
+
+ expect(result[:markdown_engine]).to eq :redcarpet
+
+ service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION })
+ result = service.execute
+
+ expect(result[:markdown_engine]).to eq :common_mark
+ end
end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index 6fd73a50511..e98df375d48 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -131,4 +131,58 @@ describe Projects::AutocompleteService do
end
end
end
+
+ describe '#labels_as_hash' do
+ def expect_labels_to_equal(labels, expected_labels)
+ expect(labels.size).to eq(expected_labels.size)
+ extract_title = lambda { |label| label['title'] }
+ expect(labels.map(&extract_title)).to eq(expected_labels.map(&extract_title))
+ end
+
+ let(:user) { create(:user) }
+ let(:group) { create(:group, :nested) }
+ let!(:sub_group) { create(:group, parent: group) }
+ let(:project) { create(:project, :public, group: group) }
+ let(:issue) { create(:issue, project: project) }
+
+ let!(:label1) { create(:label, project: project) }
+ let!(:label2) { create(:label, project: project) }
+ let!(:sub_group_label) { create(:group_label, group: sub_group) }
+ let!(:parent_group_label) { create(:group_label, group: group.parent) }
+
+ before do
+ create(:group_member, group: group, user: user)
+ end
+
+ it 'returns labels from project and ancestor groups' do
+ service = described_class.new(project, user)
+ results = service.labels_as_hash
+ expected_labels = [label1, label2, parent_group_label]
+
+ expect_labels_to_equal(results, expected_labels)
+ end
+
+ context 'some labels are already assigned' do
+ before do
+ issue.labels << label1
+ end
+
+ it 'marks already assigned as set' do
+ service = described_class.new(project, user)
+ results = service.labels_as_hash(issue)
+ expected_labels = [label1, label2, parent_group_label]
+
+ expect_labels_to_equal(results, expected_labels)
+
+ assigned_label_titles = issue.labels.map(&:title)
+ results.each do |hash|
+ if assigned_label_titles.include?(hash['title'])
+ expect(hash[:set]).to eq(true)
+ else
+ expect(hash.key?(:set)).to eq(false)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 4e4329e898e..fd69fe04053 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -23,7 +23,7 @@ describe Projects::CreateService, '#execute' do
expect(project).to be_valid
expect(project.owner).to eq(user)
- expect(project.team.masters).to include(user)
+ expect(project.team.maintainers).to include(user)
expect(project.namespace).to eq(user.namespace)
end
end
@@ -47,7 +47,7 @@ describe Projects::CreateService, '#execute' do
expect(project).to be_persisted
expect(project.owner).to eq(user)
- expect(project.team.masters).to contain_exactly(user)
+ expect(project.team.maintainers).to contain_exactly(user)
expect(project.namespace).to eq(user.namespace)
end
end
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index c15f5120b8a..f89f9b54f53 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -135,7 +135,7 @@ describe Projects::ForkService do
context "when project has restricted visibility level" do
context "and only one visibility level is restricted" do
before do
- @from_project.update_attributes(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ @from_project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
end
diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb
index a820ebd91f4..88d9d93c33b 100644
--- a/spec/services/projects/move_access_service_spec.rb
+++ b/spec/services/projects/move_access_service_spec.rb
@@ -4,18 +4,18 @@ describe Projects::MoveAccessService do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project_with_access) { create(:project, namespace: user.namespace) }
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
before do
- project_with_access.add_master(master_user)
+ project_with_access.add_maintainer(maintainer_user)
project_with_access.add_developer(developer_user)
project_with_access.add_reporter(reporter_user)
- project_with_access.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_with_access.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
project_with_access.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
project_with_access.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
end
@@ -87,7 +87,7 @@ describe Projects::MoveAccessService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining memberships' do
- target_project.add_master(master_user)
+ target_project.add_maintainer(maintainer_user)
subject.execute(project_with_access, options)
@@ -95,7 +95,7 @@ describe Projects::MoveAccessService do
end
it 'does not remove remaining group links' do
- target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ target_project.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
subject.execute(project_with_access, options)
diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb
index f7262b9b887..b4408393624 100644
--- a/spec/services/projects/move_project_authorizations_service_spec.rb
+++ b/spec/services/projects/move_project_authorizations_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::MoveProjectAuthorizationsService do
let!(:user) { create(:user) }
let(:project_with_users) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
@@ -12,7 +12,7 @@ describe Projects::MoveProjectAuthorizationsService do
describe '#execute' do
before do
- project_with_users.add_master(master_user)
+ project_with_users.add_maintainer(maintainer_user)
project_with_users.add_developer(developer_user)
project_with_users.add_reporter(reporter_user)
end
@@ -28,7 +28,7 @@ describe Projects::MoveProjectAuthorizationsService do
end
it 'does not move existent authorizations to the current project' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
expect(project_with_users.authorized_users.count).to eq 4
@@ -44,7 +44,7 @@ describe Projects::MoveProjectAuthorizationsService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining project authorizations' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
subject.execute(project_with_users, options)
diff --git a/spec/services/projects/move_project_group_links_service_spec.rb b/spec/services/projects/move_project_group_links_service_spec.rb
index e3d06e6d3d7..7ca8cf304fe 100644
--- a/spec/services/projects/move_project_group_links_service_spec.rb
+++ b/spec/services/projects/move_project_group_links_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::MoveProjectGroupLinksService do
let!(:user) { create(:user) }
let(:project_with_groups) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
@@ -12,7 +12,7 @@ describe Projects::MoveProjectGroupLinksService do
describe '#execute' do
before do
- project_with_groups.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_with_groups.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
project_with_groups.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
project_with_groups.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
end
@@ -28,7 +28,7 @@ describe Projects::MoveProjectGroupLinksService do
end
it 'does not move existent group links in the current project' do
- target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ target_project.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
target_project.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
expect(project_with_groups.project_group_links.count).to eq 3
@@ -53,7 +53,7 @@ describe Projects::MoveProjectGroupLinksService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining project group links' do
- target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ target_project.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
target_project.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
subject.execute(project_with_groups, options)
diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb
index 9c9a2d2fde1..c8c0eac1f13 100644
--- a/spec/services/projects/move_project_members_service_spec.rb
+++ b/spec/services/projects/move_project_members_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::MoveProjectMembersService do
let!(:user) { create(:user) }
let(:project_with_users) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
@@ -12,7 +12,7 @@ describe Projects::MoveProjectMembersService do
describe '#execute' do
before do
- project_with_users.add_master(master_user)
+ project_with_users.add_maintainer(maintainer_user)
project_with_users.add_developer(developer_user)
project_with_users.add_reporter(reporter_user)
end
@@ -28,7 +28,7 @@ describe Projects::MoveProjectMembersService do
end
it 'does not move existent members to the current project' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
expect(project_with_users.project_members.count).to eq 4
@@ -53,7 +53,7 @@ describe Projects::MoveProjectMembersService do
let(:options) { { remove_remaining_elements: false } }
it 'does not remove remaining project members' do
- target_project.add_master(developer_user)
+ target_project.add_maintainer(developer_user)
target_project.add_developer(reporter_user)
subject.execute(project_with_users, options)
diff --git a/spec/services/projects/overwrite_project_service_spec.rb b/spec/services/projects/overwrite_project_service_spec.rb
index 252c61f4224..c7900629f5f 100644
--- a/spec/services/projects/overwrite_project_service_spec.rb
+++ b/spec/services/projects/overwrite_project_service_spec.rb
@@ -96,10 +96,10 @@ describe Projects::OverwriteProjectService do
context 'when project with elements' do
it_behaves_like 'overwrite actions' do
- let(:master_user) { create(:user) }
+ let(:maintainer_user) { create(:user) }
let(:reporter_user) { create(:user) }
let(:developer_user) { create(:user) }
- let(:master_group) { create(:group) }
+ let(:maintainer_group) { create(:group) }
let(:reporter_group) { create(:group) }
let(:developer_group) { create(:group) }
@@ -107,10 +107,10 @@ describe Projects::OverwriteProjectService do
create_list(:deploy_keys_project, 2, project: project_from)
create_list(:notification_setting, 2, source: project_from)
create_list(:users_star_project, 2, project: project_from)
- project_from.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_from.project_group_links.create(group: maintainer_group, group_access: Gitlab::Access::MAINTAINER)
project_from.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
project_from.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
- project_from.add_master(master_user)
+ project_from.add_maintainer(maintainer_user)
project_from.add_developer(developer_user)
project_from.add_reporter(reporter_user)
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 5100987c2fe..7e85f599afb 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -247,7 +247,7 @@ describe Projects::TransferService do
let(:group_member) { create(:user) }
before do
- group.add_user(owner, GroupMember::MASTER)
+ group.add_user(owner, GroupMember::MAINTAINER)
group.add_user(group_member, GroupMember::DEVELOPER)
end
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index 1bffeee6790..a4c103e6f30 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -24,8 +24,8 @@ describe Projects::UpdatePagesService do
let(:extension) { 'zip' }
before do
- build.update_attributes(legacy_artifacts_file: file)
- build.update_attributes(legacy_artifacts_metadata: metadata)
+ build.update(legacy_artifacts_file: file)
+ build.update(legacy_artifacts_metadata: metadata)
end
describe 'pages artifacts' do
@@ -62,13 +62,13 @@ describe Projects::UpdatePagesService do
end
it 'fails if sha on branch is not latest' do
- build.update_attributes(ref: 'feature')
+ build.update(ref: 'feature')
expect(execute).not_to eq(:success)
end
it 'fails for empty file fails' do
- build.update_attributes(legacy_artifacts_file: empty_file)
+ build.update(legacy_artifacts_file: empty_file)
expect { execute }
.to raise_error(Projects::UpdatePagesService::FailedToExtractError)
@@ -118,7 +118,7 @@ describe Projects::UpdatePagesService do
end
it 'fails if sha on branch is not latest' do
- build.update_attributes(ref: 'feature')
+ build.update(ref: 'feature')
expect(execute).not_to eq(:success)
end
@@ -188,7 +188,7 @@ describe Projects::UpdatePagesService do
end
it 'fails for invalid archive' do
- build.update_attributes(legacy_artifacts_file: invalid_file)
+ build.update(legacy_artifacts_file: invalid_file)
expect(execute).not_to eq(:success)
end
@@ -199,8 +199,8 @@ describe Projects::UpdatePagesService do
file = fixture_file_upload('spec/fixtures/pages.zip')
metafile = fixture_file_upload('spec/fixtures/pages.zip.meta')
- build.update_attributes(legacy_artifacts_file: file)
- build.update_attributes(legacy_artifacts_metadata: metafile)
+ build.update(legacy_artifacts_file: file)
+ build.update(legacy_artifacts_metadata: metafile)
allow(build).to receive(:artifacts_metadata_entry)
.and_return(metadata)
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index 786493c3577..79b744142c6 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -6,8 +6,8 @@ describe ProtectedBranches::CreateService do
let(:params) do
{
name: 'master',
- merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }],
- push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
}
end
@@ -16,8 +16,8 @@ describe ProtectedBranches::CreateService do
it 'creates a new protected branch' do
expect { service.execute }.to change(ProtectedBranch, :count).by(1)
- expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
+ expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
context 'when user does not have permission' do
diff --git a/spec/services/protected_tags/create_service_spec.rb b/spec/services/protected_tags/create_service_spec.rb
index c3ed95aaebf..b16acf1d36c 100644
--- a/spec/services/protected_tags/create_service_spec.rb
+++ b/spec/services/protected_tags/create_service_spec.rb
@@ -6,7 +6,7 @@ describe ProtectedTags::CreateService do
let(:params) do
{
name: 'master',
- create_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]
+ create_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
}
end
@@ -15,7 +15,7 @@ describe ProtectedTags::CreateService do
it 'creates a new protected tag' do
expect { service.execute }.to change(ProtectedTag, :count).by(1)
- expect(project.protected_tags.last.create_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_tags.last.create_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
end
end
diff --git a/spec/services/reset_project_cache_service_spec.rb b/spec/services/reset_project_cache_service_spec.rb
index de475d16586..1490ad5fe3b 100644
--- a/spec/services/reset_project_cache_service_spec.rb
+++ b/spec/services/reset_project_cache_service_spec.rb
@@ -18,7 +18,7 @@ describe ResetProjectCacheService do
context 'when project cache_index is a numeric value' do
before do
- project.update_attributes(jobs_cache_index: 1)
+ project.update(jobs_cache_index: 1)
end
it 'increments project cache index' do
diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb
index d8dba26e194..980545b8083 100644
--- a/spec/services/search/global_service_spec.rb
+++ b/spec/services/search/global_service_spec.rb
@@ -10,7 +10,7 @@ describe Search::GlobalService do
let!(:public_project) { create(:project, :public, name: 'searchable_public_project') }
before do
- found_project.add_master(user)
+ found_project.add_maintainer(user)
end
describe '#execute' do
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 02de83a2df8..e5e036c7d44 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -16,7 +16,7 @@ describe SearchService do
let(:public_project) { create(:project, :public, name: 'public_project') }
before do
- accessible_project.add_master(user)
+ accessible_project.add_maintainer(user)
end
describe '#project' do
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 51396d34f8f..e0335880e8e 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -75,7 +75,7 @@ describe SystemHooksService do
end
it 'handles nil datetime columns' do
- user.update_attributes(created_at: nil, updated_at: nil)
+ user.update(created_at: nil, updated_at: nil)
data = event_data(user, :destroy)
expect(data[:created_at]).to be(nil)
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index e5fde07a6eb..122b96ef216 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -30,10 +30,10 @@ describe Users::RefreshAuthorizedProjectsService do
it 'updates the authorized projects of the user' do
project2 = create(:project)
to_remove = user.project_authorizations
- .create!(project: project2, access_level: Gitlab::Access::MASTER)
+ .create!(project: project2, access_level: Gitlab::Access::MAINTAINER)
expect(service).to receive(:update_authorizations)
- .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+ .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
service.execute_without_lease
end
@@ -45,7 +45,7 @@ describe Users::RefreshAuthorizedProjectsService do
.create!(project: project, access_level: Gitlab::Access::DEVELOPER)
expect(service).to receive(:update_authorizations)
- .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+ .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
service.execute_without_lease
end
@@ -76,14 +76,14 @@ describe Users::RefreshAuthorizedProjectsService do
it 'inserts authorizations that should be added' do
user.project_authorizations.delete_all
- service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MASTER]])
+ service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MAINTAINER]])
authorizations = user.project_authorizations
expect(authorizations.length).to eq(1)
expect(authorizations[0].user_id).to eq(user.id)
expect(authorizations[0].project_id).to eq(project.id)
- expect(authorizations[0].access_level).to eq(Gitlab::Access::MASTER)
+ expect(authorizations[0].access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -99,12 +99,12 @@ describe Users::RefreshAuthorizedProjectsService do
end
it 'sets the values to the access levels' do
- expect(hash.values).to eq([Gitlab::Access::MASTER])
+ expect(hash.values).to eq([Gitlab::Access::MAINTAINER])
end
context 'personal projects' do
it 'includes the project with the right access level' do
- expect(hash[project.id]).to eq(Gitlab::Access::MASTER)
+ expect(hash[project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -139,11 +139,11 @@ describe Users::RefreshAuthorizedProjectsService do
let!(:other_project) { create(:project, group: nested_group) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'includes the project with the right access level' do
- expect(hash[other_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(hash[other_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -153,7 +153,7 @@ describe Users::RefreshAuthorizedProjectsService do
let!(:project_group_link) { create(:project_group_link, project: other_project, group: group, group_access: Gitlab::Access::GUEST) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'includes the project with the right access level' do
@@ -168,7 +168,7 @@ describe Users::RefreshAuthorizedProjectsService do
let!(:project_group_link) { create(:project_group_link, project: other_project, group: nested_group, group_access: Gitlab::Access::DEVELOPER) }
before do
- group.add_master(user)
+ group.add_maintainer(user)
end
it 'includes the project with the right access level' do
@@ -194,7 +194,7 @@ describe Users::RefreshAuthorizedProjectsService do
value = hash.values[0]
expect(value.project_id).to eq(project.id)
- expect(value.access_level).to eq(Gitlab::Access::MASTER)
+ expect(value.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
@@ -219,7 +219,7 @@ describe Users::RefreshAuthorizedProjectsService do
end
it 'includes the access level for every row' do
- expect(row.access_level).to eq(Gitlab::Access::MASTER)
+ expect(row.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
end
@@ -235,7 +235,7 @@ describe Users::RefreshAuthorizedProjectsService do
rows = service.fresh_authorizations.to_a
expect(rows.length).to eq(1)
- expect(rows.first.access_level).to eq(Gitlab::Access::MASTER)
+ expect(rows.first.access_level).to eq(Gitlab::Access::MAINTAINER)
end
context 'every returned row' do
@@ -246,7 +246,7 @@ describe Users::RefreshAuthorizedProjectsService do
end
it 'includes the access level' do
- expect(row.access_level).to eq(Gitlab::Access::MASTER)
+ expect(row.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 46ec1bcef24..bd564cc60a6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,7 +4,7 @@ SimpleCovEnv.start!
ENV["RAILS_ENV"] = 'test'
ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
-require File.expand_path("../../config/environment", __FILE__)
+require File.expand_path('../config/environment', __dir__)
require 'rspec/rails'
require 'shoulda/matchers'
require 'rspec/retry'
diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb
index ea38fe4f5b8..f1341804e56 100644
--- a/spec/support/api/repositories_shared_context.rb
+++ b/spec/support/api/repositories_shared_context.rb
@@ -1,6 +1,6 @@
shared_context 'disabled repository' do
before do
- project.project_feature.update_attributes!(
+ project.project_feature.update!(
repository_access_level: ProjectFeature::DISABLED,
merge_requests_access_level: ProjectFeature::DISABLED,
builds_access_level: ProjectFeature::DISABLED
diff --git a/spec/support/api/time_tracking_shared_examples.rb b/spec/support/api/time_tracking_shared_examples.rb
index 52e1bc55191..fee464c15a3 100644
--- a/spec/support/api/time_tracking_shared_examples.rb
+++ b/spec/support/api/time_tracking_shared_examples.rb
@@ -85,7 +85,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
it 'subtracts time of the total spent time' do
Timecop.travel(1.minute.from_now) do
expect do
- issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
+ issuable.update!(spend_time: { duration: 7200, user_id: user.id })
end.to change { issuable.reload.updated_at }
end
@@ -99,7 +99,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
context 'when time to subtract is greater than the total spent time' do
it 'does not modify the total time spent' do
- issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
+ issuable.update!(spend_time: { duration: 7200, user_id: user.id })
Timecop.travel(1.minute.from_now) do
expect do
@@ -135,8 +135,8 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "GET /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_stats" do
it "returns the time stats for #{issuable_name}" do
- issuable.update_attributes!(spend_time: { duration: 1800, user_id: user.id },
- time_estimate: 3600)
+ issuable.update!(spend_time: { duration: 1800, user_id: user.id },
+ time_estimate: 3600)
get api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_stats", user)
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index 1bd6c25100e..9b44c532ff6 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -4,7 +4,7 @@
shared_examples 'issuable record that supports quick actions in its description and notes' do |issuable_type|
include Spec::Support::Helpers::Features::NotesHelpers
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:project) do
case issuable_type
when :merge_request
@@ -19,9 +19,9 @@ shared_examples 'issuable record that supports quick actions in its description
let(:new_url_opts) { {} }
before do
- project.add_master(master)
+ project.add_maintainer(maintainer)
- gitlab_sign_in(master)
+ gitlab_sign_in(maintainer)
end
after do
@@ -210,31 +210,31 @@ shared_examples 'issuable record that supports quick actions in its description
expect(page).not_to have_content '/todo'
expect(page).to have_content 'Commands applied'
- todos = TodosFinder.new(master).execute
+ todos = TodosFinder.new(maintainer).execute
todo = todos.first
expect(todos.size).to eq 1
expect(todo).to be_pending
expect(todo.target).to eq issuable
- expect(todo.author).to eq master
- expect(todo.user).to eq master
+ expect(todo.author).to eq maintainer
+ expect(todo.user).to eq maintainer
end
end
context "with a note marking the #{issuable_type} as done" do
before do
- TodoService.new.mark_todo(issuable, master)
+ TodoService.new.mark_todo(issuable, maintainer)
end
it "creates a new todo for the #{issuable_type}" do
- todos = TodosFinder.new(master).execute
+ todos = TodosFinder.new(maintainer).execute
todo = todos.first
expect(todos.size).to eq 1
expect(todos.first).to be_pending
expect(todo.target).to eq issuable
- expect(todo.author).to eq master
- expect(todo.user).to eq master
+ expect(todo.author).to eq maintainer
+ expect(todo.user).to eq maintainer
add_note("/done")
@@ -247,31 +247,31 @@ shared_examples 'issuable record that supports quick actions in its description
context "with a note subscribing to the #{issuable_type}" do
it "creates a new todo for the #{issuable_type}" do
- expect(issuable.subscribed?(master, project)).to be_falsy
+ expect(issuable.subscribed?(maintainer, project)).to be_falsy
add_note("/subscribe")
expect(page).not_to have_content '/subscribe'
expect(page).to have_content 'Commands applied'
- expect(issuable.subscribed?(master, project)).to be_truthy
+ expect(issuable.subscribed?(maintainer, project)).to be_truthy
end
end
context "with a note unsubscribing to the #{issuable_type} as done" do
before do
- issuable.subscribe(master, project)
+ issuable.subscribe(maintainer, project)
end
it "creates a new todo for the #{issuable_type}" do
- expect(issuable.subscribed?(master, project)).to be_truthy
+ expect(issuable.subscribed?(maintainer, project)).to be_truthy
add_note("/unsubscribe")
expect(page).not_to have_content '/unsubscribe'
expect(page).to have_content 'Commands applied'
- expect(issuable.subscribed?(master, project)).to be_falsy
+ expect(issuable.subscribed?(maintainer, project)).to be_falsy
end
end
@@ -282,7 +282,7 @@ shared_examples 'issuable record that supports quick actions in its description
expect(page).not_to have_content '/assign me'
expect(page).to have_content 'Commands applied'
- expect(issuable.reload.assignees).to eq [master]
+ expect(issuable.reload.assignees).to eq [maintainer]
end
end
end
diff --git a/spec/support/generate-seed-repo-rb b/spec/support/generate-seed-repo-rb
index 44b3de23b99..bee9d419376 100755
--- a/spec/support/generate-seed-repo-rb
+++ b/spec/support/generate-seed-repo-rb
@@ -15,7 +15,7 @@
require 'erb'
require 'tempfile'
-SOURCE = File.expand_path('../gitlab-git-test.git', __FILE__).freeze
+SOURCE = File.expand_path('gitlab-git-test.git', __dir__).freeze
SCRIPT_NAME = 'generate-seed-repo-rb'.freeze
REPO_NAME = 'gitlab-git-test.git'.freeze
diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb
index 88a7aeba461..f4d5343c4ed 100644
--- a/spec/support/helpers/jira_service_helper.rb
+++ b/spec/support/helpers/jira_service_helper.rb
@@ -12,7 +12,7 @@ module JiraServiceHelper
jira_issue_transition_id: '1'
}
- jira_tracker.update_attributes(properties: properties, active: true)
+ jira_tracker.update(properties: properties, active: true)
end
def jira_issue_comments
diff --git a/spec/support/helpers/key_generator_helper.rb b/spec/support/helpers/key_generator_helper.rb
index b1c289ffef7..d55d8312c65 100644
--- a/spec/support/helpers/key_generator_helper.rb
+++ b/spec/support/helpers/key_generator_helper.rb
@@ -24,7 +24,7 @@ module Spec
private
# Encodes an openssh-mpi-encoded integer.
- def encode_mpi(n)
+ def encode_mpi(n) # rubocop:disable Naming/UncommunicativeMethodParamName
chars, n = [], n.to_i
chars << (n & 0xff) && n >>= 8 while n != 0
chars << 0 if chars.empty? || chars.last >= 0x80
diff --git a/spec/support/helpers/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb
index 39e94ad53de..346f5b1cc4d 100644
--- a/spec/support/helpers/markdown_feature.rb
+++ b/spec/support/helpers/markdown_feature.rb
@@ -24,7 +24,7 @@ class MarkdownFeature
def project
@project ||= create(:project, :repository, group: group).tap do |project|
- project.add_master(user)
+ project.add_maintainer(user)
end
end
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 471b0a74a19..58b5c6a6435 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -25,6 +25,11 @@ module StubObjectStorage
::Fog::Storage.new(connection_params).tap do |connection|
begin
connection.directories.create(key: remote_directory)
+
+ # Cleanup remaining files
+ connection.directories.each do |directory|
+ directory.files.map(&:destroy)
+ end
rescue Excon::Error::Conflict
end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 05a8e6206ae..e531495d917 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -49,7 +49,9 @@ module TestEnv
'add-pdf-file' => 'e774ebd',
'squash-large-files' => '54cec52',
'add-pdf-text-binary' => '79faa7b',
- 'add_images_and_changes' => '010d106'
+ 'add_images_and_changes' => '010d106',
+ 'update-gitlab-shell-v-6-0-1' => '2f61d70',
+ 'update-gitlab-shell-v-6-0-3' => 'de78448'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/support/http_io/http_io_helpers.rb b/spec/support/http_io/http_io_helpers.rb
index 2c68c2cd9a6..42144870eb5 100644
--- a/spec/support/http_io/http_io_helpers.rb
+++ b/spec/support/http_io/http_io_helpers.rb
@@ -1,65 +1,49 @@
module HttpIOHelpers
- def stub_remote_trace_206
- WebMock.stub_request(:get, remote_trace_url)
- .to_return { |request| remote_trace_response(request, 206) }
+ def stub_remote_url_206(url, file_path)
+ WebMock.stub_request(:get, url)
+ .to_return { |request| remote_url_response(file_path, request, 206) }
end
- def stub_remote_trace_200
- WebMock.stub_request(:get, remote_trace_url)
- .to_return { |request| remote_trace_response(request, 200) }
+ def stub_remote_url_200(url, file_path)
+ WebMock.stub_request(:get, url)
+ .to_return { |request| remote_url_response(file_path, request, 200) }
end
- def stub_remote_trace_500
- WebMock.stub_request(:get, remote_trace_url)
+ def stub_remote_url_500(url)
+ WebMock.stub_request(:get, url)
.to_return(status: [500, "Internal Server Error"])
end
- def remote_trace_url
- "http://trace.com/trace"
- end
-
- def remote_trace_response(request, responce_status)
+ def remote_url_response(file_path, request, response_status)
range = request.headers['Range'].match(/bytes=(\d+)-(\d+)/)
+ body = File.read(file_path).force_encoding(Encoding::BINARY)
+ size = body.bytesize
+
{
- status: responce_status,
- headers: remote_trace_response_headers(responce_status, range[1].to_i, range[2].to_i),
- body: range_trace_body(range[1].to_i, range[2].to_i)
+ status: response_status,
+ headers: remote_url_response_headers(response_status, range[1].to_i, range[2].to_i, size),
+ body: body[range[1].to_i..range[2].to_i]
}
end
- def remote_trace_response_headers(responce_status, from, to)
- headers = { 'Content-Type' => 'text/plain' }
-
- if responce_status == 206
- headers.merge('Content-Range' => "bytes #{from}-#{to}/#{remote_trace_size}")
+ def remote_url_response_headers(response_status, from, to, size)
+ { 'Content-Type' => 'text/plain' }.tap do |headers|
+ if response_status == 206
+ headers.merge('Content-Range' => "bytes #{from}-#{to}/#{size}")
+ end
end
-
- headers
- end
-
- def range_trace_body(from, to)
- remote_trace_body[from..to]
- end
-
- def remote_trace_body
- @remote_trace_body ||= File.read(expand_fixture_path('trace/sample_trace'))
- .force_encoding(Encoding::BINARY)
- end
-
- def remote_trace_size
- remote_trace_body.bytesize
end
def set_smaller_buffer_size_than(file_size)
blocks = (file_size / 128)
new_size = (blocks / 2) * 128
- stub_const("Gitlab::Ci::Trace::HttpIO::BUFFER_SIZE", new_size)
+ stub_const("Gitlab::HttpIO::BUFFER_SIZE", new_size)
end
def set_larger_buffer_size_than(file_size)
blocks = (file_size / 128)
new_size = (blocks * 2) * 128
- stub_const("Gitlab::Ci::Trace::HttpIO::BUFFER_SIZE", new_size)
+ stub_const("Gitlab::HttpIO::BUFFER_SIZE", new_size)
end
end
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 562423afc2a..4d925ac77f4 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -37,7 +37,7 @@ module ExportFileHelper
event = create(:event, :created, target: milestone, project: project, author: user, action: 5)
create(:push_event_payload, event: event)
- create(:project_member, :master, user: user, project: project)
+ create(:project_member, :maintainer, user: user, project: project)
create(:ci_variable, project: project)
create(:ci_trigger, project: project)
key = create(:deploy_key)
diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb
index 42a9ed9ff34..429401a5da8 100644
--- a/spec/support/matchers/access_matchers_for_controller.rb
+++ b/spec/support/matchers/access_matchers_for_controller.rb
@@ -24,7 +24,7 @@ module AccessMatchersForController
when User
user = role
sign_in(user)
- when *Gitlab::Access.sym_options_with_owner.keys # owner, master, developer, reporter, guest
+ when *Gitlab::Access.sym_options_with_owner.keys # owner, maintainer, developer, reporter, guest
raise ArgumentError, "cannot emulate #{role} without membership parent" unless membership
user = create_user_by_membership(role, membership)
diff --git a/spec/support/matchers/disallow_request_matchers.rb b/spec/support/matchers/disallow_request_matchers.rb
new file mode 100644
index 00000000000..db4d90e4fd0
--- /dev/null
+++ b/spec/support/matchers/disallow_request_matchers.rb
@@ -0,0 +1,15 @@
+RSpec::Matchers.define :disallow_request do
+ match do |middleware|
+ alert = middleware.env['rack.session'].to_hash
+ .dig('flash', 'flashes', 'alert')
+
+ alert&.include?('You cannot perform write operations')
+ end
+end
+
+RSpec::Matchers.define :disallow_request_in_json do
+ match do |response|
+ json_response = JSON.parse(response.body)
+ response.body.include?('You cannot perform write operations') && json_response.key?('message')
+ end
+end
diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
index 7b064162726..8b4cffaac19 100644
--- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
+++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
@@ -3,7 +3,7 @@
shared_examples 'new issuable record that supports quick actions' do
let!(:project) { create(:project, :repository) }
- let(:user) { create(:user).tap { |u| project.add_master(u) } }
+ let(:user) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:assignee) { create(:user) }
let!(:milestone) { create(:milestone, project: project) }
let!(:labels) { create_list(:label, 3, project: project) }
@@ -12,7 +12,7 @@ shared_examples 'new issuable record that supports quick actions' do
let(:issuable) { described_class.new(project, user, params).execute }
before do
- project.add_master(assignee)
+ project.add_maintainer(assignee)
end
context 'with labels in command only' do
diff --git a/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
new file mode 100644
index 00000000000..05424d08b9d
--- /dev/null
+++ b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
@@ -0,0 +1,15 @@
+shared_context 'merge request allowing collaboration' do
+ include ProjectForksHelper
+
+ let(:canonical) { create(:project, :public, :repository) }
+ let(:forked_project) { fork_project(canonical, nil, repository: true) }
+
+ before do
+ canonical.add_maintainer(user)
+ create(:merge_request,
+ target_project: canonical,
+ source_project: forked_project,
+ source_branch: 'feature',
+ allow_collaboration: true)
+ end
+end
diff --git a/spec/support/shared_examples/controllers/todos_shared_examples.rb b/spec/support/shared_examples/controllers/todos_shared_examples.rb
deleted file mode 100644
index bafd9bac8d0..00000000000
--- a/spec/support/shared_examples/controllers/todos_shared_examples.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-shared_examples 'todos actions' do
- context 'when authorized' do
- before do
- sign_in(user)
- parent.add_developer(user)
- end
-
- it 'creates todo' do
- expect do
- post_create
- end.to change { user.todos.count }.by(1)
-
- expect(response).to have_gitlab_http_status(200)
- end
-
- it 'returns todo path and pending count' do
- post_create
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['count']).to eq 1
- expect(json_response['delete_path']).to match(%r{/dashboard/todos/\d{1}})
- end
- end
-
- context 'when not authorized for project/group' do
- it 'does not create todo for resource that user has no access to' do
- sign_in(user)
- expect do
- post_create
- end.to change { user.todos.count }.by(0)
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- it 'does not create todo when user is not logged in' do
- expect do
- post_create
- end.to change { user.todos.count }.by(0)
-
- expect(response).to have_gitlab_http_status(parent.is_a?(Group) ? 401 : 302)
- end
- end
-end
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 5a569d233bc..7038a366144 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -10,9 +10,9 @@ RSpec.shared_examples 'a creatable merge request' do
let!(:label2) { create(:label, project: target_project) }
before do
- source_project.add_master(user)
- target_project.add_master(user)
- target_project.add_master(user2)
+ source_project.add_maintainer(user)
+ target_project.add_maintainer(user)
+ target_project.add_maintainer(user2)
sign_in(user)
visit project_new_merge_request_path(
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index 645db41cddc..3057845061b 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -15,9 +15,9 @@ RSpec.shared_examples 'an editable merge request' do
end
before do
- source_project.add_master(user)
- target_project.add_master(user)
- target_project.add_master(user2)
+ source_project.add_maintainer(user)
+ target_project.add_maintainer(user)
+ target_project.add_maintainer(user2)
sign_in(user)
visit edit_project_merge_request_path(target_project, merge_request)
diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
index b29bb3c2fc0..75ad948e42c 100644
--- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
+++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
@@ -1,20 +1,20 @@
-RSpec.shared_examples 'Master manages access requests' do
+RSpec.shared_examples 'Maintainer manages access requests' do
let(:user) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
before do
entity.request_access(user)
- entity.respond_to?(:add_owner) ? entity.add_owner(master) : entity.add_master(master)
- sign_in(master)
+ entity.respond_to?(:add_owner) ? entity.add_owner(maintainer) : entity.add_maintainer(maintainer)
+ sign_in(maintainer)
end
- it 'master can see access requests' do
+ it 'maintainer can see access requests' do
visit members_page_path
expect_visible_access_request(entity, user)
end
- it 'master can grant access', :js do
+ it 'maintainer can grant access', :js do
visit members_page_path
expect_visible_access_request(entity, user)
@@ -28,7 +28,7 @@ RSpec.shared_examples 'Master manages access requests' do
end
end
- it 'master can deny access', :js do
+ it 'maintainer can deny access', :js do
visit members_page_path
expect_visible_access_request(entity, user)
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
index 56e86a87ab9..05d53a13fd3 100644
--- a/spec/support/shared_examples/helm_generated_script.rb
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -6,7 +6,7 @@ shared_examples 'helm commands' do
ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
- apk add -U ca-certificates openssl >/dev/null
+ apk add -U wget ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
EOS
diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb
index 76611e54306..ef5cea3f2a5 100644
--- a/spec/support/shared_examples/models/members_notifications_shared_example.rb
+++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb
@@ -21,7 +21,7 @@ RSpec.shared_examples 'members notifications' do |entity_type|
it "calls NotificationService.update_#{entity_type}_member" do
expect(notification_service).to receive(:"update_#{entity_type}_member").with(member)
- member.update_attribute(:access_level, Member::MASTER)
+ member.update_attribute(:access_level, Member::MAINTAINER)
end
it "does not send an email when the access level has not changed" do
diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb
index 79b2196660c..1b563021244 100644
--- a/spec/support/shared_examples/requests/api/notes.rb
+++ b/spec/support/shared_examples/requests/api/notes.rb
@@ -121,6 +121,7 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
end
end
diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index 7c34c7b4977..940c24c8d67 100644
--- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
@@ -130,7 +130,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
context "event channels" do
it "uses the right channel for push event" do
- chat_service.update_attributes(push_channel: "random")
+ chat_service.update(push_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -142,7 +142,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel for merge request event" do
- chat_service.update_attributes(merge_request_channel: "random")
+ chat_service.update(merge_request_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -154,7 +154,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel for issue event" do
- chat_service.update_attributes(issue_channel: "random")
+ chat_service.update(issue_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -169,7 +169,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
let(:issue_service_options) { { title: 'Secret', confidential: true } }
it "uses confidential issue channel" do
- chat_service.update_attributes(confidential_issue_channel: 'confidential')
+ chat_service.update(confidential_issue_channel: 'confidential')
expect(Slack::Notifier).to execute_with_options(channel: 'confidential')
@@ -177,7 +177,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it 'falls back to issue channel' do
- chat_service.update_attributes(issue_channel: 'fallback_channel')
+ chat_service.update(issue_channel: 'fallback_channel')
expect(Slack::Notifier).to execute_with_options(channel: 'fallback_channel')
@@ -186,7 +186,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel for wiki event" do
- chat_service.update_attributes(wiki_page_channel: "random")
+ chat_service.update(wiki_page_channel: "random")
expect(Slack::Notifier).to receive(:new)
.with(webhook_url, channel: "random")
@@ -203,7 +203,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses the right channel" do
- chat_service.update_attributes(note_channel: "random")
+ chat_service.update(note_channel: "random")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
@@ -222,7 +222,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it "uses confidential channel" do
- chat_service.update_attributes(confidential_note_channel: "confidential")
+ chat_service.update(confidential_note_channel: "confidential")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
@@ -232,7 +232,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
it 'falls back to note channel' do
- chat_service.update_attributes(note_channel: "fallback_channel")
+ chat_service.update(note_channel: "fallback_channel")
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 7ba28b4fc1f..3efe512a59c 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -124,6 +124,15 @@ describe FileUploader do
end
end
+ describe '.extract_dynamic_path' do
+ it 'works with hashed storage' do
+ path = 'export/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a/test/uploads/72a497a02fe3ee09edae2ed06d390038/dummy.txt'
+
+ expect(described_class.extract_dynamic_path(path)[:identifier]).to eq('dummy.txt')
+ expect(described_class.extract_dynamic_path(path)[:secret]).to eq('72a497a02fe3ee09edae2ed06d390038')
+ end
+ end
+
describe '#secret' do
it 'generates a secret if none is provided' do
expect(described_class).to receive(:generate_secret).and_return('secret')
diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb
index 362f89424d4..44718ed1212 100644
--- a/spec/uploaders/gitlab_uploader_spec.rb
+++ b/spec/uploaders/gitlab_uploader_spec.rb
@@ -68,4 +68,66 @@ describe GitlabUploader do
expect(subject.file.path).to match(/#{subject.cache_dir}/)
end
end
+
+ describe '#open' do
+ context 'when trace is stored in File storage' do
+ context 'when file exists' do
+ let(:file) do
+ fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain')
+ end
+
+ before do
+ subject.store!(file)
+ end
+
+ it 'returns io stream' do
+ expect(subject.open).to be_a(IO)
+ end
+
+ it 'when passing block it yields' do
+ expect { |b| subject.open(&b) }.to yield_control
+ end
+ end
+
+ context 'when file does not exist' do
+ it 'returns nil' do
+ expect(subject.open).to be_nil
+ end
+
+ it 'when passing block it does not yield' do
+ expect { |b| subject.open(&b) }.not_to yield_control
+ end
+ end
+ end
+
+ context 'when trace is stored in Object storage' do
+ before do
+ allow(subject).to receive(:file_storage?) { false }
+ end
+
+ context 'when file exists' do
+ before do
+ allow(subject).to receive(:url) { 'http://object_storage.com/trace' }
+ end
+
+ it 'returns http io stream' do
+ expect(subject.open).to be_a(Gitlab::HttpIO)
+ end
+
+ it 'when passing block it yields' do
+ expect { |b| subject.open(&b) }.to yield_control.once
+ end
+ end
+
+ context 'when file does not exist' do
+ it 'returns nil' do
+ expect(subject.open).to be_nil
+ end
+
+ it 'when passing block it does not yield' do
+ expect { |b| subject.open(&b) }.not_to yield_control
+ end
+ end
+ end
+ end
end
diff --git a/spec/uploaders/import_export_uploader_spec.rb b/spec/uploaders/import_export_uploader_spec.rb
new file mode 100644
index 00000000000..51b173b682d
--- /dev/null
+++ b/spec/uploaders/import_export_uploader_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe ImportExportUploader do
+ let(:model) { build_stubbed(:import_export_upload) }
+ let(:upload) { create(:upload, model: model) }
+
+ subject { described_class.new(model, :import_file) }
+
+ context "object_store is REMOTE" do
+ before do
+ stub_uploads_object_storage
+ end
+
+ include_context 'with storage', described_class::Store::REMOTE
+
+ it_behaves_like 'builds correct paths',
+ store_dir: %r[import_export_upload/import_file/],
+ upload_path: %r[import_export_upload/import_file/]
+ end
+end
diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb
index 026e4356ed6..3ad5fe7e3b3 100644
--- a/spec/uploaders/job_artifact_uploader_spec.rb
+++ b/spec/uploaders/job_artifact_uploader_spec.rb
@@ -23,43 +23,6 @@ describe JobArtifactUploader do
store_dir: %r[\h{2}/\h{2}/\h{64}/\d{4}_\d{1,2}_\d{1,2}/\d+/\d+\z]
end
- describe '#open' do
- subject { uploader.open }
-
- context 'when trace is stored in File storage' do
- context 'when file exists' do
- let(:file) do
- fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain')
- end
-
- before do
- uploader.store!(file)
- end
-
- it 'returns io stream' do
- is_expected.to be_a(IO)
- end
- end
-
- context 'when file does not exist' do
- it 'returns nil' do
- is_expected.to be_nil
- end
- end
- end
-
- context 'when trace is stored in Object storage' do
- before do
- allow(uploader).to receive(:file_storage?) { false }
- allow(uploader).to receive(:url) { 'http://object_storage.com/trace' }
- end
-
- it 'returns http io stream' do
- is_expected.to be_a(Gitlab::Ci::Trace::HttpIO)
- end
- end
- end
-
context 'file is stored in valid local_path' do
let(:file) do
fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip')
diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb
index 32d73d0c5ab..11fe144d1d2 100644
--- a/spec/views/projects/imports/new.html.haml_spec.rb
+++ b/spec/views/projects/imports/new.html.haml_spec.rb
@@ -7,9 +7,9 @@ describe "projects/imports/new.html.haml" do
let(:project) { create(:project_empty_repo, :import_failed, import_type: :gitlab_project, import_source: '/var/opt/gitlab/gitlab-rails/shared/tmp/project_exports/uploads/t.tar.gz', import_url: nil) }
before do
- project.import_state.update_attributes(last_error: '<a href="http://googl.com">Foo</a>')
+ project.import_state.update(last_error: '<a href="http://googl.com">Foo</a>')
sign_in(user)
- project.add_master(user)
+ project.add_maintainer(user)
end
it "escapes HTML in import errors" do
diff --git a/spec/views/projects/merge_requests/show.html.haml_spec.rb b/spec/views/projects/merge_requests/show.html.haml_spec.rb
index 264e0ce0b40..fe6ad26a6f6 100644
--- a/spec/views/projects/merge_requests/show.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb
@@ -52,7 +52,7 @@ describe 'projects/merge_requests/show.html.haml' do
context 'when the merge request is open' do
it 'closes the merge request if the source project does not exist' do
- closed_merge_request.update_attributes(state: 'open')
+ closed_merge_request.update(state: 'open')
forked_project.destroy
# Reload merge request so MergeRequest#source_project turns to `nil`
closed_merge_request.reload
diff --git a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
index 6e7d8db99c4..5d60d6bc5e7 100644
--- a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
+++ b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'projects/pipeline_schedules/_pipeline_schedule' do
let(:owner) { create(:user) }
- let(:master) { create(:user) }
+ let(:maintainer) { create(:user) }
let(:project) { create(:project) }
let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project) }
@@ -17,10 +17,10 @@ describe 'projects/pipeline_schedules/_pipeline_schedule' do
context 'taking ownership of schedule' do
context 'when non-owner is signed in' do
- let(:user) { master }
+ let(:user) { maintainer }
before do
- allow(view).to receive(:can?).with(master, :take_ownership_pipeline_schedule, pipeline_schedule).and_return(true)
+ allow(view).to receive(:can?).with(maintainer, :take_ownership_pipeline_schedule, pipeline_schedule).and_return(true)
end
it 'non-owner can take ownership of pipeline' do
diff --git a/spec/views/shared/notes/_form.html.haml_spec.rb b/spec/views/shared/notes/_form.html.haml_spec.rb
index 50980718e66..c57319869f3 100644
--- a/spec/views/shared/notes/_form.html.haml_spec.rb
+++ b/spec/views/shared/notes/_form.html.haml_spec.rb
@@ -7,7 +7,7 @@ describe 'shared/notes/_form' do
let(:project) { create(:project, :repository) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
assign(:project, project)
assign(:note, note)
diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb
index 199825b5097..ce38cde9208 100644
--- a/spec/workers/concerns/waitable_worker_spec.rb
+++ b/spec/workers/concerns/waitable_worker_spec.rb
@@ -18,8 +18,8 @@ describe WaitableWorker do
def self.bulk_perform_inline(args_list)
end
- def perform(i = 0)
- self.class.counter += i
+ def perform(count = 0)
+ self.class.counter += count
end
end
end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index e39dec556fc..30e67e67e0e 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -27,6 +27,12 @@ describe GitGarbageCollectWorker do
subject.perform(project.id, :gc, lease_key, lease_uuid)
end
+
+ it 'handles gRPC errors' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect).and_raise(GRPC::NotFound)
+
+ expect { subject.perform(project.id, :gc, lease_key, lease_uuid) }.to raise_exception(Gitlab::Git::Repository::NoRepository)
+ end
end
context 'with different lease than the active one' do
@@ -203,12 +209,7 @@ describe GitGarbageCollectWorker do
tree: old_commit.tree,
parents: [old_commit]
)
- Gitlab::Git::OperationService.new(nil, project.repository.raw_repository).send(
- :update_ref,
- "refs/heads/#{SecureRandom.hex(6)}",
- new_commit_sha,
- Gitlab::Git::BLANK_SHA
- )
+ rugged.references.create("refs/heads/#{SecureRandom.hex(6)}", new_commit_sha)
end
def packs(project)
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
index c861a56497e..b57c275c770 100644
--- a/spec/workers/merge_worker_spec.rb
+++ b/spec/workers/merge_worker_spec.rb
@@ -8,7 +8,7 @@ describe MergeWorker do
let!(:author) { merge_request.author }
before do
- source_project.add_master(author)
+ source_project.add_maintainer(author)
source_project.repository.expire_branches_cache
end
diff --git a/spec/workers/object_storage_upload_worker_spec.rb b/spec/workers/object_storage_upload_worker_spec.rb
deleted file mode 100644
index 32ddcbe9757..00000000000
--- a/spec/workers/object_storage_upload_worker_spec.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-require 'spec_helper'
-
-describe ObjectStorageUploadWorker do
- let(:local) { ObjectStorage::Store::LOCAL }
- let(:remote) { ObjectStorage::Store::REMOTE }
-
- def perform
- described_class.perform_async(uploader_class.name, subject_class, file_field, subject_id)
- end
-
- context 'for LFS' do
- let!(:lfs_object) { create(:lfs_object, :with_file, file_store: local) }
- let(:uploader_class) { LfsObjectUploader }
- let(:subject_class) { LfsObject }
- let(:file_field) { :file }
- let(:subject_id) { lfs_object.id }
-
- context 'when object storage is enabled' do
- before do
- stub_lfs_object_storage(background_upload: true)
- end
-
- it 'uploads object to storage' do
- expect { perform }.to change { lfs_object.reload.file_store }.from(local).to(remote)
- end
-
- context 'when background upload is disabled' do
- before do
- allow(Gitlab.config.lfs.object_store).to receive(:background_upload) { false }
- end
-
- it 'is skipped' do
- expect { perform }.not_to change { lfs_object.reload.file_store }
- end
- end
- end
-
- context 'when object storage is disabled' do
- before do
- stub_lfs_object_storage(enabled: false)
- end
-
- it "doesn't migrate files" do
- perform
-
- expect(lfs_object.reload.file_store).to eq(local)
- end
- end
- end
-
- context 'for legacy artifacts' do
- let(:build) { create(:ci_build, :legacy_artifacts) }
- let(:uploader_class) { LegacyArtifactUploader }
- let(:subject_class) { Ci::Build }
- let(:file_field) { :artifacts_file }
- let(:subject_id) { build.id }
-
- context 'when local storage is used' do
- let(:store) { local }
-
- context 'and remote storage is defined' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it "migrates file to remote storage" do
- perform
-
- expect(build.reload.artifacts_file_store).to eq(remote)
- end
-
- context 'for artifacts_metadata' do
- let(:file_field) { :artifacts_metadata }
-
- it 'migrates metadata to remote storage' do
- perform
-
- expect(build.reload.artifacts_metadata_store).to eq(remote)
- end
- end
- end
- end
- end
-
- context 'for job artifacts' do
- let(:artifact) { create(:ci_job_artifact, :archive) }
- let(:uploader_class) { JobArtifactUploader }
- let(:subject_class) { Ci::JobArtifact }
- let(:file_field) { :file }
- let(:subject_id) { artifact.id }
-
- context 'when local storage is used' do
- let(:store) { local }
-
- context 'and remote storage is defined' do
- before do
- stub_artifacts_object_storage(background_upload: true)
- end
-
- it "migrates file to remote storage" do
- perform
-
- expect(artifact.reload.file_store).to eq(remote)
- end
- end
- end
- end
-end
diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb
index e7a4ac0f3d6..a2fe4734d47 100644
--- a/spec/workers/pipeline_schedule_worker_spec.rb
+++ b/spec/workers/pipeline_schedule_worker_spec.rb
@@ -18,7 +18,7 @@ describe PipelineScheduleWorker do
context 'when the schedule is runnable by the user' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when there is a scheduled pipeline within next_run_at' do
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index ac79d9c0ac1..2d071c181c2 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe ProcessCommitWorker do
+ include ProjectForksHelper
+
let(:worker) { described_class.new }
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
@@ -32,15 +34,41 @@ describe ProcessCommitWorker do
worker.perform(project.id, user.id, commit.to_hash)
end
- context 'when commit already exists in upstream project' do
- let(:forked) { create(:project, :public, :repository) }
+ context 'when the project is forked' do
+ context 'when commit already exists in the upstream project' do
+ it 'does not process the commit message' do
+ forked = fork_project(project, user, repository: true)
+
+ expect(worker).not_to receive(:process_commit_message)
+
+ worker.perform(forked.id, user.id, forked.commit.to_hash)
+ end
+ end
+
+ context 'when the commit does not exist in the upstream project' do
+ it 'processes the commit message' do
+ empty_project = create(:project, :public)
+ forked = fork_project(empty_project, user, repository: true)
+
+ TestEnv.copy_repo(forked,
+ bare_repo: TestEnv.factory_repo_path_bare,
+ refs: TestEnv::BRANCH_SHA)
+
+ expect(worker).to receive(:process_commit_message)
+
+ worker.perform(forked.id, user.id, forked.commit.to_hash)
+ end
+ end
- it 'does not process commit message' do
- create(:forked_project_link, forked_to_project: forked, forked_from_project: project)
+ context 'when the upstream project no longer exists' do
+ it 'processes the commit message' do
+ forked = fork_project(project, user, repository: true)
+ project.destroy!
- expect(worker).not_to receive(:process_commit_message)
+ expect(worker).to receive(:process_commit_message)
- worker.perform(forked.id, user.id, forked.commit.to_hash)
+ worker.perform(forked.id, user.id, forked.commit.to_hash)
+ end
end
end
end
diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb
index 6bc551be9ad..ede271b2cdd 100644
--- a/spec/workers/repository_check/batch_worker_spec.rb
+++ b/spec/workers/repository_check/batch_worker_spec.rb
@@ -62,4 +62,12 @@ describe RepositoryCheck::BatchWorker do
expect(subject.perform(shard_name)).to eq([])
end
+
+ it 'does not run if the exclusive lease is taken' do
+ allow(subject).to receive(:try_obtain_lease).and_return(false)
+
+ expect(subject).not_to receive(:perform_repository_checks)
+
+ subject.perform(shard_name)
+ end
end
diff --git a/spec/workers/repository_check/dispatch_worker_spec.rb b/spec/workers/repository_check/dispatch_worker_spec.rb
index 20a4f1f5344..7877429aa8f 100644
--- a/spec/workers/repository_check/dispatch_worker_spec.rb
+++ b/spec/workers/repository_check/dispatch_worker_spec.rb
@@ -11,6 +11,14 @@ describe RepositoryCheck::DispatchWorker do
subject.perform
end
+ it 'does nothing if the exclusive lease is taken' do
+ allow(subject).to receive(:try_obtain_lease).and_return(false)
+
+ expect(RepositoryCheck::BatchWorker).not_to receive(:perform_async)
+
+ subject.perform
+ end
+
it 'dispatches work to RepositoryCheck::BatchWorker' do
expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once)
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index f0884ad0aff..d07e40377d4 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -51,7 +51,7 @@ describe RepositoryImportWorker do
it 'hide the credentials that were used in the import URL' do
error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
- project.update_attributes(import_jid: '123')
+ project.update(import_jid: '123')
expect_any_instance_of(Projects::ImportService).to receive(:execute).and_return({ status: :error, message: error })
expect do
@@ -63,7 +63,7 @@ describe RepositoryImportWorker do
it 'updates the error on Import/Export' do
error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
- project.update_attributes(import_jid: '123', import_type: 'gitlab_project')
+ project.update(import_jid: '123', import_type: 'gitlab_project')
expect_any_instance_of(Projects::ImportService).to receive(:execute).and_return({ status: :error, message: error })
expect do
diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb
index 152ba2509b9..4f1ad2474f5 100644
--- a/spec/workers/repository_update_remote_mirror_worker_spec.rb
+++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb
@@ -13,7 +13,7 @@ describe RepositoryUpdateRemoteMirrorWorker do
describe '#perform' do
context 'with status none' do
before do
- remote_mirror.update_attributes(update_status: 'none')
+ remote_mirror.update(update_status: 'none')
end
it 'sets status as finished when update remote mirror service executes successfully' do
@@ -34,7 +34,7 @@ describe RepositoryUpdateRemoteMirrorWorker do
end
it 'does nothing if last_update_started_at is higher than the time the job was scheduled in' do
- remote_mirror.update_attributes(last_update_started_at: Time.now)
+ remote_mirror.update(last_update_started_at: Time.now)
expect_any_instance_of(RemoteMirror).to receive(:updated_since?).with(scheduled_time).and_return(true)
expect_any_instance_of(Projects::UpdateRemoteMirrorService).not_to receive(:execute).with(remote_mirror)
@@ -56,7 +56,7 @@ describe RepositoryUpdateRemoteMirrorWorker do
context 'with another worker already running' do
before do
- remote_mirror.update_attributes(update_status: 'started')
+ remote_mirror.update(update_status: 'started')
end
it 'raises RemoteMirrorUpdateAlreadyInProgressError' do
@@ -68,11 +68,11 @@ describe RepositoryUpdateRemoteMirrorWorker do
context 'with status failed' do
before do
- remote_mirror.update_attributes(update_status: 'failed')
+ remote_mirror.update(update_status: 'failed')
end
it 'sets status as finished if last_update_started_at is higher than the time the job was scheduled in' do
- remote_mirror.update_attributes(last_update_started_at: Time.now)
+ remote_mirror.update(last_update_started_at: Time.now)
expect_any_instance_of(RemoteMirror).to receive(:updated_since?).with(scheduled_time).and_return(false)
expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :success)
diff --git a/spec/workers/stuck_import_jobs_worker_spec.rb b/spec/workers/stuck_import_jobs_worker_spec.rb
index af7675c8cab..2169c14218b 100644
--- a/spec/workers/stuck_import_jobs_worker_spec.rb
+++ b/spec/workers/stuck_import_jobs_worker_spec.rb
@@ -51,7 +51,7 @@ describe StuckImportJobsWorker do
let(:project) { create(:project, :import_scheduled) }
before do
- project.import_state.update_attributes(jid: '123')
+ project.import_state.update(jid: '123')
end
end
end
@@ -61,7 +61,7 @@ describe StuckImportJobsWorker do
let(:project) { create(:project, :import_started) }
before do
- project.import_state.update_attributes(jid: '123')
+ project.import_state.update(jid: '123')
end
end
end
diff --git a/vendor/gitignore/Autotools.gitignore b/vendor/gitignore/Autotools.gitignore
index ffa6ecc3f9b..96d6ed2cfea 100644
--- a/vendor/gitignore/Autotools.gitignore
+++ b/vendor/gitignore/Autotools.gitignore
@@ -9,7 +9,7 @@ Makefile.in
# http://www.gnu.org/software/autoconf
-/autom4te.cache
+autom4te.cache
/autoscan.log
/autoscan-*.log
/aclocal.m4
@@ -39,4 +39,3 @@ m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
-autom4te.cache
diff --git a/vendor/gitignore/CraftCMS.gitignore b/vendor/gitignore/CraftCMS.gitignore
index a70d4772c46..0d81b397e35 100644
--- a/vendor/gitignore/CraftCMS.gitignore
+++ b/vendor/gitignore/CraftCMS.gitignore
@@ -1,3 +1,4 @@
-# Craft Storage (cache) [http://buildwithcraft.com/help/craft-storage-gitignore]
+# Craft 2 Storage (https://craftcms.com/support/craft-storage-gitignore)
+# not necessary for Craft 3 (https://github.com/craftcms/craft/issues/26)
/craft/storage/*
-!/craft/storage/logo/* \ No newline at end of file
+!/craft/storage/rebrand
diff --git a/vendor/gitignore/Delphi.gitignore b/vendor/gitignore/Delphi.gitignore
index 19864c6bbef..000ee5f104b 100644
--- a/vendor/gitignore/Delphi.gitignore
+++ b/vendor/gitignore/Delphi.gitignore
@@ -20,7 +20,7 @@
# Deployment Manager configuration file for your project. Added in Delphi XE2.
# Uncomment this if it is not mobile development and you do not use remote debug feature.
#*.deployproj
-#
+#
# C++ object files produced when C/C++ Output file generation is configured.
# Uncomment this if you are not using external objects (zlib library for example).
#*.obj
diff --git a/vendor/gitignore/Eagle.gitignore b/vendor/gitignore/Eagle.gitignore
index 9afc324d6ae..28f0b9715e6 100644
--- a/vendor/gitignore/Eagle.gitignore
+++ b/vendor/gitignore/Eagle.gitignore
@@ -35,7 +35,6 @@ eagle.epf
*.gpi
*.pls
*.ger
-*.gpi
*.xln
*.drd
diff --git a/vendor/gitignore/GWT.gitignore b/vendor/gitignore/GWT.gitignore
index 07704e54bbc..a01e7fcd921 100644
--- a/vendor/gitignore/GWT.gitignore
+++ b/vendor/gitignore/GWT.gitignore
@@ -18,9 +18,6 @@ war/WEB-INF/classes/
#compilation logs
.gwt/
-#caching for already compiled files
-gwt-unitCache/
-
#gwt junit compilation files
www-test/
diff --git a/vendor/gitignore/Global/Backup.gitignore b/vendor/gitignore/Global/Backup.gitignore
new file mode 100644
index 00000000000..825ce52db53
--- /dev/null
+++ b/vendor/gitignore/Global/Backup.gitignore
@@ -0,0 +1,5 @@
+*.bak
+*.gho
+*.ori
+*.orig
+*.tmp
diff --git a/vendor/gitignore/Global/CodeKit.gitignore b/vendor/gitignore/Global/CodeKit.gitignore
index bd9e67fcca2..09b84126cea 100644
--- a/vendor/gitignore/Global/CodeKit.gitignore
+++ b/vendor/gitignore/Global/CodeKit.gitignore
@@ -1,3 +1,4 @@
# General CodeKit files to ignore
config.codekit
+config.codekit3
/min
diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore
index 0eb8a5e8571..a65649a9ed2 100644
--- a/vendor/gitignore/Global/Eclipse.gitignore
+++ b/vendor/gitignore/Global/Eclipse.gitignore
@@ -23,7 +23,7 @@ local.properties
# CDT-specific (C/C++ Development Tooling)
.cproject
-# CDT- autotools
+# CDT- autotools
.autotools
# Java annotation processor (APT)
@@ -47,6 +47,9 @@ local.properties
# Code Recommenders
.recommenders/
+# Annotation Processing
+.apt_generated/
+
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index 4d5117a1d9d..0d95b087f19 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -4,6 +4,7 @@
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
@@ -20,9 +21,16 @@
.idea/**/gradle.xml
.idea/**/libraries
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
# CMake
-cmake-build-debug/
-cmake-build-release/
+cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore
index d87a6bdbeeb..46a83d635ba 100644
--- a/vendor/gitignore/Global/Matlab.gitignore
+++ b/vendor/gitignore/Global/Matlab.gitignore
@@ -7,17 +7,20 @@
# Compiled MEX binaries (all platforms)
*.mex*
-# Packaged app and toolbox files
-*.mlappinstall
-*.mltbx
-
-# Generated helpsearch folders
-helpsearch*/
+# Packaged app and toolbox files
+*.mlappinstall
+*.mltbx
+
+# Generated helpsearch folders
+helpsearch*/
# Simulink code generation folders
slprj/
sccprj/
+# Matlab code generation folders
+codegen/
+
# Simulink autosave extension
*.autosave
diff --git a/vendor/gitignore/Global/Patch.gitignore b/vendor/gitignore/Global/Patch.gitignore
new file mode 100644
index 00000000000..6ffab9ad295
--- /dev/null
+++ b/vendor/gitignore/Global/Patch.gitignore
@@ -0,0 +1,2 @@
+*.orig
+*.rej
diff --git a/vendor/gitignore/Global/SynopsysVCS.gitignore b/vendor/gitignore/Global/SynopsysVCS.gitignore
index eed2432fb78..ad751f6bd75 100644
--- a/vendor/gitignore/Global/SynopsysVCS.gitignore
+++ b/vendor/gitignore/Global/SynopsysVCS.gitignore
@@ -4,8 +4,8 @@
*.evcd
*.fsdb
-# Default name of the simulation executable. A different name can be
-# specified with this switch (the associated daidir database name is
+# Default name of the simulation executable. A different name can be
+# specified with this switch (the associated daidir database name is
# also taken from here): -o <path>/<filename>
simv
@@ -13,7 +13,7 @@ simv
simv.daidir/
simv.db.dir/
-# Infrastructure necessary to co-simulate SystemC models with
+# Infrastructure necessary to co-simulate SystemC models with
# Verilog/VHDL models. An alternate directory may be specified with this
# switch: -Mdir=<directory_path>
csrc/
@@ -22,7 +22,7 @@ csrc/
# used to write all messages from simulation: -l <filename>
*.log
-# Coverage results (generated with urg) and database location. The
+# Coverage results (generated with urg) and database location. The
# following switch can also be used: urg -dir <coverage_directory>.vdb
simv.vdb/
urgReport/
diff --git a/vendor/gitignore/Global/Vim.gitignore b/vendor/gitignore/Global/Vim.gitignore
index 19cfe22f583..741518ffd24 100644
--- a/vendor/gitignore/Global/Vim.gitignore
+++ b/vendor/gitignore/Global/Vim.gitignore
@@ -1,7 +1,8 @@
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
-[._]s[a-v][a-z]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
[._]sw[a-p]
# Session
diff --git a/vendor/gitignore/LabVIEW.gitignore b/vendor/gitignore/LabVIEW.gitignore
index 122450865cf..31619f59814 100644
--- a/vendor/gitignore/LabVIEW.gitignore
+++ b/vendor/gitignore/LabVIEW.gitignore
@@ -14,3 +14,4 @@
# Metadata
*.aliases
*.lvlps
+.cache/
diff --git a/vendor/gitignore/Maven.gitignore b/vendor/gitignore/Maven.gitignore
index 5f2dbe11df9..e8d57d08088 100644
--- a/vendor/gitignore/Maven.gitignore
+++ b/vendor/gitignore/Maven.gitignore
@@ -7,6 +7,4 @@ release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
-
-# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
-!/.mvn/wrapper/maven-wrapper.jar
+.mvn/wrapper/maven-wrapper.jar
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index 4454ba1b5bf..3a4c8581b3a 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -57,9 +57,15 @@ typings/
# dotenv environment variables file
.env
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
# next.js build output
.next
+# nuxt.js build output
+.nuxt
+
# vuepress build output
.vuepress/dist
diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore
index 86de6aa3f5f..a0bd6b453a8 100644
--- a/vendor/gitignore/Objective-C.gitignore
+++ b/vendor/gitignore/Objective-C.gitignore
@@ -35,6 +35,9 @@ xcuserdata/
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
+#
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+# *.xcworkspace
# Carthage
#
diff --git a/vendor/gitignore/Perl6.gitignore b/vendor/gitignore/Perl6.gitignore
new file mode 100644
index 00000000000..7b2c018a562
--- /dev/null
+++ b/vendor/gitignore/Perl6.gitignore
@@ -0,0 +1,7 @@
+# Gitignore for Perl 6 (http://www.perl6.org)
+# As part of https://github.com/github/gitignore
+
+# precompiled files
+.precomp
+lib/.precomp
+
diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore
index 312d1f652c6..b8e04d98e33 100644
--- a/vendor/gitignore/Swift.gitignore
+++ b/vendor/gitignore/Swift.gitignore
@@ -47,6 +47,9 @@ playground.xcworkspace
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
+#
+# Add this line if you want to avoid checking in source code from the Xcode workspace
+# *.xcworkspace
# Carthage
#
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index 3d12d3f90ad..79a66f9ebfa 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -226,6 +226,9 @@ TSWLatexianTemp*
# Texpad
.texpadtmp
+# LyX
+*.lyx~
+
# Kile
*.backup
@@ -241,6 +244,3 @@ TSWLatexianTemp*
# standalone packages
*.sta
-
-# generated if using elsarticle.cls
-*.spl
diff --git a/vendor/gitignore/Typo3.gitignore b/vendor/gitignore/Typo3.gitignore
index cb024fefe99..200c2a2bf79 100644
--- a/vendor/gitignore/Typo3.gitignore
+++ b/vendor/gitignore/Typo3.gitignore
@@ -8,12 +8,15 @@
/typo3conf/temp_CACHED*
/typo3conf/temp_fieldInfo.php
/typo3conf/deprecation_*.log
-/typo3conf/AdditionalConfiguration.php
+/typo3conf/ENABLE_INSTALL_TOOL
+/typo3conf/realurl_autoconf.php
+/FIRST_INSTALL
# Ignore system folders, you should have them symlinked.
# If not comment out the following entries.
/typo3
/typo3_src
/typo3_src-*
+/Packages
/.htaccess
/index.php
# Ignore temp directory.
diff --git a/vendor/gitignore/Umbraco.gitignore b/vendor/gitignore/Umbraco.gitignore
index 10fc2b4d825..cd90af3071a 100644
--- a/vendor/gitignore/Umbraco.gitignore
+++ b/vendor/gitignore/Umbraco.gitignore
@@ -19,7 +19,7 @@
!**/App_Data/[Pp]ackages/*
!**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/*
-# ImageProcessor DiskCache
+# ImageProcessor DiskCache
**/App_Data/cache/
# Ignore the Models Builder models out of date flag
diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore
index 1daca8b50d9..6582eaf9a11 100644
--- a/vendor/gitignore/UnrealEngine.gitignore
+++ b/vendor/gitignore/UnrealEngine.gitignore
@@ -1,9 +1,6 @@
# Visual Studio 2015 user specific files
.vs/
-# Visual Studio 2015 database file
-*.VC.db
-
# Compiled Object files
*.slo
*.lo
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 1e9abf78d69..f431ddc7cf5 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -220,7 +220,7 @@ ClientBin/
*.publishsettings
orleans.codegen.cs
-# Including strong name files can present a security risk
+# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
@@ -316,7 +316,7 @@ __pycache__/
# OpenCover UI analysis results
OpenCover/
-# Azure Stream Analytics local run output
+# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
@@ -325,5 +325,5 @@ ASALocalRun/
# NVidia Nsight GPU debugger configuration file
*.nvuser
-# MFractors (Xamarin productivity tool) working folder
+# MFractors (Xamarin productivity tool) working folder
.mfractor/
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index 0d58a00482a..ee0df7711e7 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -105,11 +105,14 @@ code_quality:
- code_quality
artifacts:
paths: [gl-code-quality-report.json]
+ only:
+ - branches
except:
variables:
- $CODE_QUALITY_DISABLED
license_management:
+ stage: test
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@@ -121,6 +124,8 @@ license_management:
- license_management
artifacts:
paths: [gl-license-management-report.json]
+ only:
+ - branches
except:
variables:
- $LICENSE_MANAGEMENT_DISABLED
@@ -161,6 +166,8 @@ sast:
- sast
artifacts:
paths: [gl-sast-report.json]
+ only:
+ - branches
except:
variables:
- $SAST_DISABLED
@@ -178,6 +185,8 @@ dependency_scanning:
- dependency_scanning
artifacts:
paths: [gl-dependency-scanning-report.json]
+ only:
+ - branches
except:
variables:
- $DEPENDENCY_SCANNING_DISABLED
@@ -195,6 +204,8 @@ container_scanning:
- container_scanning
artifacts:
paths: [gl-container-scanning-report.json]
+ only:
+ - branches
except:
variables:
- $CONTAINER_SCANNING_DISABLED
@@ -365,6 +376,7 @@ production_manual:
kubernetes: active
variables:
- $STAGING_ENABLED
+ - $CANARY_ENABLED
except:
variables:
- $INCREMENTAL_ROLLOUT_ENABLED
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index dada0da0b31..7503160baa0 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -7,7 +7,7 @@
@babel/template,7.0.0-beta.44,MIT
@babel/traverse,7.0.0-beta.44,MIT
@babel/types,7.0.0-beta.44,MIT
-@gitlab-org/gitlab-svgs,1.23.0,SEE LICENSE IN LICENSE
+@gitlab-org/gitlab-svgs,1.25.0,SEE LICENSE IN LICENSE
@sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT
@vue/component-compiler-utils,1.2.1,MIT
@@ -35,7 +35,7 @@ abbrev,1.1.1,ISC
accepts,1.3.4,MIT
ace-rails-ap,4.1.2,MIT
acorn,3.3.0,MIT
-acorn,5.5.3,MIT
+acorn,5.6.2,MIT
acorn-dynamic-import,3.0.0,MIT
acorn-jsx,3.0.1,MIT
actionmailer,4.2.10,MIT
@@ -51,14 +51,12 @@ addressparser,1.0.1,MIT
aes_key_wrap,1.0.1,MIT
after,0.8.2,MIT
agent-base,2.1.1,MIT
-ajv,4.11.8,MIT
ajv,5.5.2,MIT
ajv,6.1.1,MIT
ajv-keywords,2.1.1,MIT
ajv-keywords,3.1.0,MIT
akismet,2.0.0,MIT
align-text,0.1.4,MIT
-allocations,1.0.5,MIT
alphanum-sort,1.0.2,MIT
amdefine,1.0.1,BSD-3-Clause OR MIT
amqplib,0.5.2,MIT
@@ -208,7 +206,7 @@ base64-js,1.2.3,MIT
base64id,1.0.0,MIT
batch,0.6.1,MIT
batch-loader,1.2.1,MIT
-bcrypt,3.1.11,MIT
+bcrypt,3.1.12,MIT
bcrypt-pbkdf,1.0.1,New BSD
bcrypt_pbkdf,1.0.0,MIT
better-assert,1.0.2,MIT
@@ -220,7 +218,6 @@ bitsyntax,0.0.4,Unknown
bl,1.1.2,MIT
blackst0ne-mermaid,7.1.0-fixed,MIT
blob,0.0.4,MIT*
-block-stream,0.0.9,ISC
bluebird,3.5.1,MIT
bn.js,4.11.8,MIT
body-parser,1.18.2,MIT
@@ -269,7 +266,7 @@ camelcase-keys,2.1.0,MIT
caniuse-api,1.6.1,MIT
caniuse-db,1.0.30000649,CC-BY-4.0
capture-stack-trace,1.0.0,MIT
-carrierwave,1.2.1,MIT
+carrierwave,1.2.3,MIT
caseless,0.11.0,Apache 2.0
caseless,0.12.0,Apache 2.0
cause,0.1,MIT
@@ -399,6 +396,7 @@ dashdash,1.14.1,MIT
data-uri-to-buffer,1.2.0,MIT
date-format,1.2.0,MIT
date-now,0.1.4,MIT
+dateformat,3.0.3,MIT
de-indent,1.0.2,MIT
debug,2.2.0,MIT
debug,2.6.8,MIT
@@ -457,7 +455,7 @@ domelementtype,1.3.0,Simplified BSD
domhandler,2.4.1,Simplified BSD
domutils,1.6.2,Simplified BSD
doorkeeper,4.3.2,MIT
-doorkeeper-openid_connect,1.4.0,MIT
+doorkeeper-openid_connect,1.5.0,MIT
dot-prop,4.2.0,MIT
double-ended-queue,2.1.0-0,MIT
dropzone,4.2.0,MIT
@@ -507,7 +505,7 @@ eslint-plugin-html,4.0.3,ISC
eslint-plugin-import,2.12.0,MIT
eslint-plugin-jasmine,2.2.0,MIT
eslint-plugin-promise,3.8.0,ISC
-eslint-plugin-vue,4.0.1,MIT
+eslint-plugin-vue,4.5.0,MIT
eslint-restricted-globals,0.1.1,MIT
eslint-scope,3.7.1,Simplified BSD
eslint-visitor-keys,1.0.0,Apache 2.0
@@ -515,10 +513,9 @@ espree,3.5.4,Simplified BSD
esprima,2.7.3,Simplified BSD
esprima,3.1.3,Simplified BSD
esprima,4.0.0,Simplified BSD
-esquery,1.0.0,New BSD
-esrecurse,4.1.0,Simplified BSD
+esquery,1.0.1,New BSD
+esrecurse,4.2.1,Simplified BSD
estraverse,1.9.3,Simplified BSD
-estraverse,4.1.1,Simplified BSD
estraverse,4.2.0,Simplified BSD
esutils,2.0.2,Simplified BSD
et-orbi,1.0.3,MIT
@@ -607,11 +604,10 @@ fresh,0.5.2,MIT
from,0.1.7,MIT
from2,2.3.0,MIT
fs-access,1.0.1,MIT
+fs-minipass,1.2.5,ISC
fs-write-stream-atomic,1.0.10,ISC
fs.realpath,1.0.0,ISC
-fsevents,1.1.3,MIT
-fstream,1.0.11,ISC
-fstream-ignore,1.0.5,ISC
+fsevents,1.2.4,MIT
ftp,0.3.10,MIT
function-bind,1.1.1,MIT
functional-red-black-tree,1.0.1,MIT
@@ -630,14 +626,14 @@ get_process_mem,0.2.0,MIT
getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.0,MIT
-gitaly-proto,0.100.0,MIT
+gitaly-proto,0.105.0,MIT
github-linguist,5.3.3,MIT
github-markup,1.7.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
-gitlab-gollum-lib,4.2.7.2,MIT
-gitlab-gollum-rugged_adapter,0.4.4,MIT
+gitlab-gollum-lib,4.2.7.5,MIT
+gitlab-gollum-rugged_adapter,0.4.4.1,MIT
gitlab-grit,2.8.2,MIT
-gitlab-markup,1.6.3,MIT
+gitlab-markup,1.6.4,MIT
gitlab_omniauth-ldap,2.0.4,MIT
glob,5.0.15,ISC
glob,7.1.2,ISC
@@ -664,7 +660,7 @@ gpgme,2.0.13,LGPL-2.1+
graceful-fs,4.1.11,ISC
grape,1.0.3,MIT
grape-entity,0.7.1,MIT
-grape-path-helpers,1.0.4,MIT
+grape-path-helpers,1.0.5,MIT
grape_logging,1.7.0,MIT
graphiql-rails,1.4.10,MIT
graphlib,2.1.1,MIT
@@ -674,10 +670,8 @@ gzip-size,4.1.0,MIT
hamlit,2.6.1,MIT
handle-thing,1.2.5,MIT
handlebars,4.0.6,MIT
-har-schema,1.0.5,ISC
har-schema,2.0.0,ISC
har-validator,2.0.6,ISC
-har-validator,4.2.1,ISC
har-validator,5.0.3,ISC
has,1.0.1,MIT
has-ansi,2.0.0,MIT
@@ -712,7 +706,7 @@ hosted-git-info,2.2.0,ISC
hpack.js,2.1.6,MIT
html-comment-regex,1.1.1,MIT
html-entities,1.2.0,MIT
-html-pipeline,2.7.1,MIT
+html-pipeline,2.8.3,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
htmlparser2,3.9.2,MIT
@@ -739,13 +733,14 @@ icalendar,2.4.1,ruby
ice_nine,0.11.2,MIT
iconv-lite,0.4.15,MIT
iconv-lite,0.4.19,MIT
+iconv-lite,0.4.23,MIT
icss-replace-symbols,1.1.0,ISC
icss-utils,2.1.0,ISC
ieee754,1.1.11,New BSD
-ieee754,1.1.8,New BSD
iferr,0.1.5,MIT
ignore,3.3.8,MIT
ignore-by-default,1.0.1,ISC
+ignore-walk,3.0.1,ISC
immediate,3.0.6,MIT
import-lazy,2.1.0,MIT
import-local,1.0.0,MIT
@@ -865,16 +860,14 @@ jsesc,1.3.0,MIT
jsesc,2.5.1,MIT
json,1.8.6,ruby
json-buffer,3.0.0,MIT
-json-jwt,1.9.2,MIT
+json-jwt,1.9.4,MIT
json-parse-better-errors,1.0.2,MIT
json-schema,0.2.3,BSD
json-schema-traverse,0.3.1,MIT
-json-stable-stringify,1.0.1,MIT
json-stable-stringify-without-jsonify,1.0.1,MIT
json-stringify-safe,5.0.1,ISC
json3,3.3.2,MIT
json5,0.5.1,MIT
-jsonify,0.0.0,Public Domain
jsonpointer,4.0.1,MIT
jsprim,1.4.1,MIT
jszip,3.1.3,(MIT OR GPL-3.0)
@@ -992,12 +985,14 @@ minimatch,3.0.4,ISC
minimist,0.0.10,MIT
minimist,0.0.8,MIT
minimist,1.2.0,MIT
+minipass,2.3.3,ISC
+minizlib,1.1.0,MIT
mississippi,2.0.0,Simplified BSD
mixin-deep,1.3.1,MIT
mkdirp,0.5.1,MIT
moment,2.19.2,MIT
monaco-editor,0.13.1,MIT
-monaco-editor-webpack-plugin,1.2.1,MIT
+monaco-editor-webpack-plugin,1.4.0,MIT
mousetrap,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
move-concurrently,1.0.1,ISC
@@ -1012,9 +1007,10 @@ mustermann,1.0.2,MIT
mustermann-grape,1.0.0,MIT
mute-stream,0.0.7,ISC
mysql2,0.4.10,MIT
-nan,2.8.0,MIT
+nan,2.10.0,MIT
nanomatch,1.2.9,MIT
natural-compare,1.4.0,MIT
+needle,2.2.1,MIT
negotiator,0.6.1,MIT
neo-async,2.5.0,MIT
net-ldap,0.16.0,MIT
@@ -1024,7 +1020,7 @@ netrc,0.11.0,MIT
nice-try,1.0.4,MIT
node-forge,0.6.33,New BSD
node-libs-browser,2.1.0,MIT
-node-pre-gyp,0.6.39,New BSD
+node-pre-gyp,0.10.0,New BSD
node-uuid,1.4.8,MIT
nodemailer,2.7.2,MIT
nodemailer-direct-transport,3.3.2,MIT
@@ -1034,7 +1030,8 @@ nodemailer-smtp-pool,2.8.2,MIT
nodemailer-smtp-transport,2.7.2,MIT
nodemailer-wellknown,0.1.10,MIT
nodemon,1.17.3,MIT
-nokogiri,1.8.2,MIT
+nokogiri,1.8.3,MIT
+nokogumbo,1.5.0,Apache 2.0
nopt,1.0.10,MIT
nopt,3.0.6,ISC
nopt,4.0.1,ISC
@@ -1043,6 +1040,8 @@ normalize-path,2.1.1,MIT
normalize-range,0.1.2,MIT
normalize-url,1.9.1,MIT
normalize-url,2.0.1,MIT
+npm-bundled,1.0.3,ISC
+npm-packlist,1.1.10,ISC
npm-run-path,2.0.2,MIT
npmlog,4.1.2,ISC
null-check,1.0.0,MIT
@@ -1076,7 +1075,7 @@ omniauth-oauth,1.1.0,MIT
omniauth-oauth2,1.5.0,MIT
omniauth-oauth2-generic,0.2.2,MIT
omniauth-saml,1.10.0,MIT
-omniauth-shibboleth,1.2.1,MIT
+omniauth-shibboleth,1.3.0,MIT
omniauth-twitter,1.4.0,MIT
omniauth_crowd,2.2.3,MIT
on-finished,2.3.0,MIT
@@ -1137,7 +1136,6 @@ peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT
peek-sidekiq,1.0.3,MIT
-performance-now,0.2.0,MIT
performance-now,2.1.0,MIT
pg,0.18.4,"BSD,ruby,GPL"
pify,2.3.0,MIT
@@ -1194,7 +1192,6 @@ premailer-rails,1.9.7,MIT
prepend-http,1.0.4,MIT
prepend-http,2.0.0,MIT
preserve,0.2.0,MIT
-prettier,1.11.1,MIT
prettier,1.12.1,MIT
prismjs,1.6.0,MIT
private,0.1.8,MIT
@@ -1222,7 +1219,6 @@ q,1.4.1,MIT
q,1.5.0,MIT
qjobs,1.2.0,MIT
qs,6.2.3,New BSD
-qs,6.4.0,New BSD
qs,6.5.1,New BSD
query-string,4.3.2,MIT
query-string,5.1.1,MIT
@@ -1303,11 +1299,9 @@ repeat-string,1.6.1,MIT
repeating,2.0.1,MIT
representable,3.0.4,MIT
request,2.75.0,Apache 2.0
-request,2.81.0,Apache 2.0
request,2.83.0,Apache 2.0
request_store,1.3.1,MIT
requestretry,1.13.0,MIT
-require-all,2.2.0,MIT
require-directory,2.1.1,MIT
require-main-filename,1.0.1,ISC
require-uncached,1.0.3,MIT
@@ -1341,24 +1335,26 @@ ruby_parser,3.9.0,MIT
rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT
-rugged,0.27.1,MIT
+rugged,0.27.2,MIT
run-async,2.3.0,MIT
run-queue,1.0.3,ISC
rx-lite,4.0.8,Apache 2.0
rx-lite-aggregates,4.0.8,Apache 2.0
rxjs,5.5.10,Apache 2.0
safe-buffer,5.1.1,MIT
+safe-buffer,5.1.2,MIT
safe-regex,1.1.0,MIT
safe_yaml,1.0.4,MIT
-sanitize,2.1.0,MIT
+safer-buffer,2.1.2,MIT
+sanitize,4.6.5,MIT
sanitize-html,1.16.3,MIT
sass,3.5.5,MIT
sass-listen,4.0.0,MIT
sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
sax,1.2.2,ISC
+sax,1.2.4,ISC
schema-utils,0.4.5,MIT
-securecompare,1.0.0,MIT
seed-fu,2.3.7,MIT
select,1.1.2,MIT
select-hose,2.0.0,MIT
@@ -1433,7 +1429,7 @@ spdy-transport,2.0.20,MIT
split,0.3.3,MIT
split-string,3.1.0,MIT
sprintf-js,1.0.3,New BSD
-sprockets,3.7.1,MIT
+sprockets,3.7.2,MIT
sprockets-rails,3.2.1,MIT
sql.js,0.4.0,MIT
srcset,1.0.0,MIT
@@ -1479,8 +1475,7 @@ sys-filesystem,1.1.6,Artistic 2.0
table,4.0.2,New BSD
tapable,0.1.10,MIT
tapable,1.0.0,MIT
-tar,2.2.1,ISC
-tar-pack,3.4.1,Simplified BSD
+tar,4.4.4,ISC
temple,0.7.7,MIT
term-size,1.2.0,MIT
test-exclude,4.2.1,ISC
@@ -1535,7 +1530,6 @@ uglify-es,3.3.9,Simplified BSD
uglify-js,2.8.29,Simplified BSD
uglify-to-browserify,1.0.2,MIT
uglifyjs-webpack-plugin,1.2.5,MIT
-uid-number,0.0.6,ISC
ultron,1.1.1,MIT
undefsafe,2.0.2,MIT
underscore,1.7.0,MIT
@@ -1566,7 +1560,6 @@ url-parse,1.1.9,MIT
url-parse-lax,1.0.0,MIT
url-parse-lax,3.0.0,MIT
url-to-options,1.0.1,MIT
-url_safe_base64,0.2.2,MIT
use,2.0.2,MIT
useragent,2.2.1,MIT
util,0.10.3,MIT
@@ -1640,6 +1633,7 @@ xtend,4.0.1,MIT
y18n,3.2.1,ISC
y18n,4.0.0,ISC
yallist,2.1.2,ISC
+yallist,3.0.2,ISC
yargs,11.0.0,MIT
yargs,11.1.0,MIT
yargs,3.10.0,MIT
diff --git a/yarn.lock b/yarn.lock
index 30d49ad276a..9d0eae5f509 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -78,9 +78,9 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"
-"@gitlab-org/gitlab-svgs@^1.24.0":
- version "1.24.0"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.24.0.tgz#3b2b58c5a1d58ce784f486d648bd87cbbb06cedc"
+"@gitlab-org/gitlab-svgs@^1.25.0":
+ version "1.25.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.25.0.tgz#1a82b1be43e1a46e6b0767ef46f26f5fd6bbd101"
"@sindresorhus/is@^0.7.0":
version "0.7.0"
@@ -104,139 +104,140 @@
source-map "^0.5.6"
vue-template-es2015-compiler "^1.6.0"
-"@webassemblyjs/ast@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.10.tgz#7f1e81149ca4e103c9e7cc321ea0dcb83a392512"
+"@webassemblyjs/ast@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25"
dependencies:
- "@webassemblyjs/helper-module-context" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/wast-parser" "1.5.10"
+ "@webassemblyjs/helper-module-context" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/wast-parser" "1.5.13"
debug "^3.1.0"
mamacro "^0.0.3"
-"@webassemblyjs/floating-point-hex-parser@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.10.tgz#ae48705fd58927df62023f114520b8215330ff86"
+"@webassemblyjs/floating-point-hex-parser@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298"
-"@webassemblyjs/helper-api-error@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.10.tgz#0baf9453ce2fd8db58f0fdb4fb2852557c71d5a7"
+"@webassemblyjs/helper-api-error@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59"
-"@webassemblyjs/helper-buffer@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.10.tgz#abee4284161e9cd6ba7619785ca277bfcb8052ce"
+"@webassemblyjs/helper-buffer@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e"
dependencies:
debug "^3.1.0"
-"@webassemblyjs/helper-code-frame@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.10.tgz#4e23c05431665f16322104580af7c06253d4b4e0"
+"@webassemblyjs/helper-code-frame@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58"
dependencies:
- "@webassemblyjs/wast-printer" "1.5.10"
+ "@webassemblyjs/wast-printer" "1.5.13"
-"@webassemblyjs/helper-fsm@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.10.tgz#490bab613ea255a9272b764826d3cc9d15170676"
+"@webassemblyjs/helper-fsm@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924"
-"@webassemblyjs/helper-module-context@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.10.tgz#6fca93585228bf33e6da076d0a1373db1fdd6580"
+"@webassemblyjs/helper-module-context@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5"
dependencies:
+ debug "^3.1.0"
mamacro "^0.0.3"
-"@webassemblyjs/helper-wasm-bytecode@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.10.tgz#90f6da93c7a186bfb2f587de442982ff533c4b44"
+"@webassemblyjs/helper-wasm-bytecode@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747"
-"@webassemblyjs/helper-wasm-section@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.10.tgz#d64292a19f7f357c49719461065efdf7ec975d66"
+"@webassemblyjs/helper-wasm-section@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-buffer" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/wasm-gen" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-buffer" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/wasm-gen" "1.5.13"
debug "^3.1.0"
-"@webassemblyjs/ieee754@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.10.tgz#257cad440dd6c8a339402d31e035ba2e38e9c245"
+"@webassemblyjs/ieee754@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364"
dependencies:
ieee754 "^1.1.11"
-"@webassemblyjs/leb128@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.10.tgz#a8e4fe5f4b16daadb241fcc44d9735e9f27b05a3"
+"@webassemblyjs/leb128@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee"
dependencies:
- leb "^0.3.0"
+ long "4.0.0"
-"@webassemblyjs/utf8@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.10.tgz#0b3b6bc86b7619c5dc7b2789db6665aa35689983"
+"@webassemblyjs/utf8@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469"
-"@webassemblyjs/wasm-edit@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.10.tgz#0fe80f19e57f669eab1caa8c1faf9690b259d5b9"
+"@webassemblyjs/wasm-edit@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-buffer" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/helper-wasm-section" "1.5.10"
- "@webassemblyjs/wasm-gen" "1.5.10"
- "@webassemblyjs/wasm-opt" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
- "@webassemblyjs/wast-printer" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-buffer" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/helper-wasm-section" "1.5.13"
+ "@webassemblyjs/wasm-gen" "1.5.13"
+ "@webassemblyjs/wasm-opt" "1.5.13"
+ "@webassemblyjs/wasm-parser" "1.5.13"
+ "@webassemblyjs/wast-printer" "1.5.13"
debug "^3.1.0"
-"@webassemblyjs/wasm-gen@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.10.tgz#8b29ddd3651259408ae5d5c816a011fb3f3f3584"
+"@webassemblyjs/wasm-gen@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/ieee754" "1.5.10"
- "@webassemblyjs/leb128" "1.5.10"
- "@webassemblyjs/utf8" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/ieee754" "1.5.13"
+ "@webassemblyjs/leb128" "1.5.13"
+ "@webassemblyjs/utf8" "1.5.13"
-"@webassemblyjs/wasm-opt@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.10.tgz#569e45ab1b2bf0a7706cdf6d1b51d1188e9e4c7b"
+"@webassemblyjs/wasm-opt@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-buffer" "1.5.10"
- "@webassemblyjs/wasm-gen" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-buffer" "1.5.13"
+ "@webassemblyjs/wasm-gen" "1.5.13"
+ "@webassemblyjs/wasm-parser" "1.5.13"
debug "^3.1.0"
-"@webassemblyjs/wasm-parser@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.10.tgz#3e1017e49f833f46b840db7cf9d194d4f00037ff"
- dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-api-error" "1.5.10"
- "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
- "@webassemblyjs/ieee754" "1.5.10"
- "@webassemblyjs/leb128" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
-
-"@webassemblyjs/wast-parser@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.10.tgz#1a3235926483c985a00ee8ebca856ffda9544934"
- dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/floating-point-hex-parser" "1.5.10"
- "@webassemblyjs/helper-api-error" "1.5.10"
- "@webassemblyjs/helper-code-frame" "1.5.10"
- "@webassemblyjs/helper-fsm" "1.5.10"
+"@webassemblyjs/wasm-parser@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-api-error" "1.5.13"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.13"
+ "@webassemblyjs/ieee754" "1.5.13"
+ "@webassemblyjs/leb128" "1.5.13"
+ "@webassemblyjs/utf8" "1.5.13"
+
+"@webassemblyjs/wast-parser@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/floating-point-hex-parser" "1.5.13"
+ "@webassemblyjs/helper-api-error" "1.5.13"
+ "@webassemblyjs/helper-code-frame" "1.5.13"
+ "@webassemblyjs/helper-fsm" "1.5.13"
long "^3.2.0"
mamacro "^0.0.3"
-"@webassemblyjs/wast-printer@1.5.10":
- version "1.5.10"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.10.tgz#adb38831ba45efd0a5c7971b666e179b64f68bba"
+"@webassemblyjs/wast-printer@1.5.13":
+ version "1.5.13"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/wast-parser" "1.5.10"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/wast-parser" "1.5.13"
long "^3.2.0"
abbrev@1:
@@ -274,6 +275,10 @@ acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
version "5.6.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
+acorn@^5.6.2:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
+
addressparser@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746"
@@ -282,12 +287,11 @@ after@0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
-agent-base@2:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7"
+agent-base@4, agent-base@^4.1.0, agent-base@^4.2.0, agent-base@~4.2.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
dependencies:
- extend "~3.0.0"
- semver "~5.0.1"
+ es6-promisify "^5.0.0"
ajv-keywords@^2.1.0:
version "2.1.1"
@@ -322,10 +326,6 @@ align-text@^0.1.1, align-text@^0.1.3:
longest "^1.0.1"
repeat-string "^1.5.2"
-alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
-
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -372,13 +372,6 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-anymatch@^1.3.0:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
- dependencies:
- micromatch "^2.1.5"
- normalize-path "^2.0.0"
-
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -409,17 +402,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
-arr-diff@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
- dependencies:
- arr-flatten "^1.0.1"
-
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
-arr-flatten@^1.0.1, arr-flatten@^1.1.0:
+arr-flatten@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
@@ -532,11 +519,11 @@ async@^2.0.0, async@^2.1.4:
dependencies:
lodash "^4.14.0"
-async@~2.1.2:
- version "2.1.5"
- resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
+async@~2.6.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
- lodash "^4.14.0"
+ lodash "^4.17.10"
asynckit@^0.4.0:
version "0.4.0"
@@ -546,17 +533,6 @@ atob@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
-autoprefixer@^6.3.1:
- version "6.7.7"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
- dependencies:
- browserslist "^1.7.6"
- caniuse-db "^1.0.30000634"
- normalize-range "^0.1.2"
- num2fraction "^1.2.2"
- postcss "^5.2.16"
- postcss-value-parser "^3.2.3"
-
autosize@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.0.tgz#7a0599b1ba84d73bd7589b0d9da3870152c69237"
@@ -766,9 +742,9 @@ babel-helpers@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
-babel-loader@^7.1.4:
- version "7.1.4"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.4.tgz#e3463938bd4e6d55d1c174c5485d406a188ed015"
+babel-loader@^7.1.5:
+ version "7.1.5"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68"
dependencies:
find-cache-dir "^1.0.0"
loader-utils "^1.0.2"
@@ -1202,10 +1178,6 @@ backo2@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
-balanced-match@^0.4.2:
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -1374,14 +1346,6 @@ braces@^0.1.2:
dependencies:
expand-range "^0.1.0"
-braces@^1.8.2:
- version "1.8.5"
- resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
- dependencies:
- expand-range "^1.8.1"
- preserve "^0.2.0"
- repeat-element "^1.1.2"
-
braces@^2.3.0, braces@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb"
@@ -1455,13 +1419,6 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
-browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
- version "1.7.7"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
- dependencies:
- caniuse-db "^1.0.30000639"
- electron-to-chromium "^1.2.7"
-
buffer-from@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
@@ -1600,19 +1557,6 @@ camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
-caniuse-api@^1.5.2:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
- dependencies:
- browserslist "^1.3.6"
- caniuse-db "^1.0.30000529"
- lodash.memoize "^4.1.2"
- lodash.uniq "^4.5.0"
-
-caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
- version "1.0.30000649"
- resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a"
-
capture-stack-trace@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
@@ -1654,6 +1598,10 @@ chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+chardet@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029"
+
"charenc@>= 0.0.1":
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -1666,24 +1614,27 @@ check-types@^7.3.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d"
-chokidar@^1.4.1:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+chokidar@^2.0.0, chokidar@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
dependencies:
- anymatch "^1.3.0"
+ anymatch "^2.0.0"
async-each "^1.0.0"
- glob-parent "^2.0.0"
+ braces "^2.3.0"
+ glob-parent "^3.1.0"
inherits "^2.0.1"
is-binary-path "^1.0.0"
- is-glob "^2.0.0"
+ is-glob "^4.0.0"
+ normalize-path "^2.1.1"
path-is-absolute "^1.0.0"
readdirp "^2.0.0"
+ upath "^1.0.0"
optionalDependencies:
fsevents "^1.0.0"
-chokidar@^2.0.0, chokidar@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
+chokidar@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
dependencies:
anymatch "^2.0.0"
async-each "^1.0.0"
@@ -1692,20 +1643,23 @@ chokidar@^2.0.0, chokidar@^2.0.2:
inherits "^2.0.1"
is-binary-path "^1.0.0"
is-glob "^4.0.0"
+ lodash.debounce "^4.0.8"
normalize-path "^2.1.1"
path-is-absolute "^1.0.0"
readdirp "^2.0.0"
- upath "^1.0.0"
+ upath "^1.0.5"
optionalDependencies:
- fsevents "^1.0.0"
+ fsevents "^1.2.2"
chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
-chrome-trace-event@^0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-0.1.2.tgz#90f36885d5345a50621332f0717b595883d5d982"
+chrome-trace-event@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48"
+ dependencies:
+ tslib "^1.9.0"
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
@@ -1718,15 +1672,9 @@ circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
-circular-json@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f"
-
-clap@^1.0.9:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b"
- dependencies:
- chalk "^1.1.3"
+circular-json@^0.5.4:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.5.tgz#64182ef359042d37cd8e767fc9de878b1e9447d3"
class-utils@^0.3.5:
version "0.3.6"
@@ -1785,24 +1733,10 @@ clone-response@1.0.2:
dependencies:
mimic-response "^1.0.0"
-clone@^1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f"
-
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
-co@~3.0.6:
- version "3.0.6"
- resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda"
-
-coa@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3"
- dependencies:
- q "^1.1.2"
-
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@@ -1814,39 +1748,17 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
-color-convert@^1.3.0, color-convert@^1.9.0:
+color-convert@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
dependencies:
color-name "^1.1.1"
-color-name@^1.0.0, color-name@^1.1.1:
+color-name@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
-color-string@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
- dependencies:
- color-name "^1.0.0"
-
-color@^0.11.0:
- version "0.11.4"
- resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
- dependencies:
- clone "^1.0.2"
- color-convert "^1.3.0"
- color-string "^0.3.0"
-
-colormin@^1.0.5:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
- dependencies:
- color "^0.11.0"
- css-color-names "0.0.4"
- has "^1.0.1"
-
-colors@^1.1.0, colors@~1.1.2:
+colors@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@@ -2115,22 +2027,16 @@ crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
-css-color-names@0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
-
-css-loader@^0.28.11:
- version "0.28.11"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7"
+css-loader@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56"
dependencies:
babel-code-frame "^6.26.0"
css-selector-tokenizer "^0.7.0"
- cssnano "^3.10.0"
icss-utils "^2.1.0"
loader-utils "^1.0.2"
lodash.camelcase "^4.3.0"
- object-assign "^4.1.1"
- postcss "^5.0.6"
+ postcss "^6.0.23"
postcss-modules-extract-imports "^1.2.0"
postcss-modules-local-by-default "^1.2.0"
postcss-modules-scope "^1.1.0"
@@ -2150,50 +2056,6 @@ cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
-cssnano@^3.10.0:
- version "3.10.0"
- resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
- dependencies:
- autoprefixer "^6.3.1"
- decamelize "^1.1.2"
- defined "^1.0.0"
- has "^1.0.1"
- object-assign "^4.0.1"
- postcss "^5.0.14"
- postcss-calc "^5.2.0"
- postcss-colormin "^2.1.8"
- postcss-convert-values "^2.3.4"
- postcss-discard-comments "^2.0.4"
- postcss-discard-duplicates "^2.0.1"
- postcss-discard-empty "^2.0.1"
- postcss-discard-overridden "^0.1.1"
- postcss-discard-unused "^2.2.1"
- postcss-filter-plugins "^2.0.0"
- postcss-merge-idents "^2.1.5"
- postcss-merge-longhand "^2.0.1"
- postcss-merge-rules "^2.0.3"
- postcss-minify-font-values "^1.0.2"
- postcss-minify-gradients "^1.0.1"
- postcss-minify-params "^1.0.4"
- postcss-minify-selectors "^2.0.4"
- postcss-normalize-charset "^1.1.0"
- postcss-normalize-url "^3.0.7"
- postcss-ordered-values "^2.1.0"
- postcss-reduce-idents "^2.2.2"
- postcss-reduce-initial "^1.0.0"
- postcss-reduce-transforms "^1.0.3"
- postcss-svgo "^2.1.1"
- postcss-unique-selectors "^2.0.2"
- postcss-value-parser "^3.2.3"
- postcss-zindex "^2.0.1"
-
-csso@~2.3.1:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
- dependencies:
- clap "^1.0.9"
- source-map "^0.5.3"
-
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -2368,18 +2230,12 @@ debug@2.6.8:
dependencies:
ms "2.0.0"
-debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
+debug@3.1.0, debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
-debug@~2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
- dependencies:
- ms "0.7.1"
-
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -2442,11 +2298,7 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
-defined@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
-
-degenerator@~1.0.2:
+degenerator@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
dependencies:
@@ -2493,6 +2345,10 @@ depd@1.1.1, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+
des.js@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
@@ -2522,7 +2378,7 @@ di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
-diff@^3.4.0:
+diff@^3.2.0, diff@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -2654,10 +2510,6 @@ ejs@^2.5.7:
version "2.5.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
-electron-to-chromium@^1.2.7:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e"
-
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@@ -2735,6 +2587,14 @@ enhanced-resolve@^4.0.0:
memory-fs "^0.4.0"
tapable "^1.0.0"
+enhanced-resolve@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
+ dependencies:
+ graceful-fs "^4.1.2"
+ memory-fs "^0.4.0"
+ tapable "^1.0.0"
+
enhanced-resolve@~0.9.0:
version "0.9.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e"
@@ -2787,10 +2647,20 @@ es-to-primitive@^1.1.1:
is-date-object "^1.0.1"
is-symbol "^1.0.1"
+es6-promise@^4.0.3:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
+
es6-promise@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
+es6-promisify@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
+ dependencies:
+ es6-promise "^4.0.3"
+
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -2964,7 +2834,7 @@ espree@^3.5.2:
acorn "^5.5.0"
acorn-jsx "^3.0.0"
-esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1:
+esprima@2.7.x, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
@@ -3061,12 +2931,6 @@ expand-braces@^0.1.1:
array-unique "^0.2.1"
braces "^0.1.2"
-expand-brackets@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
- dependencies:
- is-posix-bracket "^0.1.0"
-
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -3086,12 +2950,6 @@ expand-range@^0.1.0:
is-number "^0.1.1"
repeat-string "^0.2.2"
-expand-range@^1.8.1:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
- dependencies:
- fill-range "^2.1.0"
-
exports-loader@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5"
@@ -3151,7 +3009,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.1.0:
+external-editor@^2.0.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
dependencies:
@@ -3159,11 +3017,13 @@ external-editor@^2.0.4, external-editor@^2.1.0:
iconv-lite "^0.4.17"
tmp "^0.0.33"
-extglob@^0.3.1:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+external-editor@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6"
dependencies:
- is-extglob "^1.0.0"
+ chardet "^0.5.0"
+ iconv-lite "^0.4.22"
+ tmp "^0.0.33"
extglob@^2.0.4:
version "2.0.4"
@@ -3238,10 +3098,6 @@ file-uri-to-path@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
-filename-regex@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
-
fileset@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
@@ -3253,16 +3109,6 @@ filesize@^3.5.11:
version "3.6.0"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa"
-fill-range@^2.1.0:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^1.1.3"
- repeat-element "^1.1.2"
- repeat-string "^1.5.2"
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -3318,10 +3164,6 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
-flatten@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
-
flush-write-stream@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
@@ -3341,16 +3183,10 @@ follow-redirects@^1.2.5:
dependencies:
debug "^3.1.0"
-for-in@^1.0.1, for-in@^1.0.2:
+for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
-for-own@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
- dependencies:
- for-in "^1.0.1"
-
foreach@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
@@ -3367,15 +3203,7 @@ form-data@~2.0.0:
combined-stream "^1.0.5"
mime-types "^2.1.11"
-form-data@~2.1.1:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.5"
- mime-types "^2.1.12"
-
-form-data@~2.3.1:
+form-data@~2.3.0, form-data@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
@@ -3433,7 +3261,7 @@ fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
-fsevents@^1.0.0:
+fsevents@^1.0.0, fsevents@^1.2.2:
version "1.2.4"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
dependencies:
@@ -3494,9 +3322,9 @@ get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
-get-uri@2:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59"
+get-uri@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.2.tgz#5c795e71326f6ca1286f2fc82575cd2bab2af578"
dependencies:
data-uri-to-buffer "1"
debug "2"
@@ -3515,19 +3343,6 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-glob-base@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
- dependencies:
- glob-parent "^2.0.0"
- is-glob "^2.0.0"
-
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- dependencies:
- is-glob "^2.0.0"
-
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -3850,10 +3665,6 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1"
wbuf "^1.1.0"
-html-comment-regex@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
-
html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
@@ -3886,13 +3697,21 @@ http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
-http-proxy-agent@1:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a"
+http-errors@1.6.3:
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
+ depd "~1.1.2"
+ inherits "2.0.3"
+ setprototypeof "1.1.0"
+ statuses ">= 1.4.0 < 2"
+
+http-proxy-agent@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
+ dependencies:
+ agent-base "4"
+ debug "3.1.0"
http-proxy-middleware@~0.18.0:
version "0.18.0"
@@ -3941,13 +3760,12 @@ https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
-https-proxy-agent@1:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
+https-proxy-agent@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
+ agent-base "^4.1.0"
+ debug "^3.1.0"
iconv-lite@0.4.15:
version "0.4.15"
@@ -3957,7 +3775,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.4:
+iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
dependencies:
@@ -4035,9 +3853,9 @@ indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
-inflection@~1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f"
+inflection@~1.12.0:
+ version "1.12.0"
+ resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416"
inflection@~1.3.0:
version "1.3.8"
@@ -4081,20 +3899,20 @@ inquirer@^3.0.6:
strip-ansi "^4.0.0"
through "^2.3.6"
-inquirer@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726"
+inquirer@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.0.0.tgz#e8c20303ddc15bbfc2c12a6213710ccd9e1413d8"
dependencies:
ansi-escapes "^3.0.0"
chalk "^2.0.0"
cli-cursor "^2.1.0"
cli-width "^2.0.0"
- external-editor "^2.1.0"
+ external-editor "^3.0.0"
figures "^2.0.0"
lodash "^4.3.0"
mute-stream "0.0.7"
run-async "^2.2.0"
- rxjs "^5.5.2"
+ rxjs "^6.1.0"
string-width "^2.1.0"
strip-ansi "^4.0.0"
through "^2.3.6"
@@ -4126,10 +3944,6 @@ invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
-ip@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590"
-
ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -4138,10 +3952,6 @@ ipaddr.js@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
-is-absolute-url@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
-
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@@ -4210,16 +4020,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2:
is-data-descriptor "^1.0.0"
kind-of "^6.0.2"
-is-dotfile@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
-
-is-equal-shallow@^0.1.3:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
- dependencies:
- is-primitive "^2.0.0"
-
is-extendable@^0.1.0, is-extendable@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -4230,10 +4030,6 @@ is-extendable@^1.0.1:
dependencies:
is-plain-object "^2.0.4"
-is-extglob@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
-
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -4254,12 +4050,6 @@ is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
-is-glob@^2.0.0, is-glob@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
- dependencies:
- is-extglob "^1.0.0"
-
is-glob@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
@@ -4301,12 +4091,6 @@ is-number@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- dependencies:
- kind-of "^3.0.2"
-
is-number@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -4357,14 +4141,6 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
-is-posix-bracket@^0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
-
-is-primitive@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
-
is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
@@ -4397,12 +4173,6 @@ is-stream@^1.0.0, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
-is-svg@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
- dependencies:
- html-comment-regex "^1.1.0"
-
is-symbol@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
@@ -4554,6 +4324,12 @@ jasmine-core@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.9.0.tgz#bfbb56defcd30789adec5a3fbba8504233289c72"
+jasmine-diff@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/jasmine-diff/-/jasmine-diff-0.1.3.tgz#93ccc2dcc41028c5ddd4606558074839f2deeaa8"
+ dependencies:
+ diff "^3.2.0"
+
jasmine-jquery@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jasmine-jquery/-/jasmine-jquery-2.1.1.tgz#d4095e646944a26763235769ab018d9f30f0d47b"
@@ -4576,10 +4352,6 @@ jquery.waitforimages@^2.2.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
-js-base64@^2.1.9:
- version "2.1.9"
- resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
-
js-cookie@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526"
@@ -4595,13 +4367,6 @@ js-yaml@3.x, js-yaml@^3.7.0, js-yaml@^3.9.1:
argparse "^1.0.7"
esprima "^4.0.0"
-js-yaml@~3.7.0:
- version "3.7.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
- dependencies:
- argparse "^1.0.7"
- esprima "^2.6.0"
-
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -4691,9 +4456,9 @@ karma-coverage-istanbul-reporter@^1.4.2:
istanbul-api "^1.1.14"
minimatch "^3.0.4"
-karma-jasmine@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.1.tgz#6fe840e75a11600c9d91e84b33c458e1c46a3529"
+karma-jasmine@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.2.tgz#394f2b25ffb4a644b9ada6f22d443e2fd08886c3"
karma-mocha-reporter@^2.2.5:
version "2.2.5"
@@ -4709,24 +4474,24 @@ karma-sourcemap-loader@^0.3.7:
dependencies:
graceful-fs "^4.1.2"
-karma-webpack@3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-3.0.0.tgz#bf009c5b73c667c11c015717e9e520f581317c44"
+karma-webpack@^4.0.0-beta.0:
+ version "4.0.0-beta.0"
+ resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-4.0.0-beta.0.tgz#2b386df6c364f588f896ffbdae57c2e51513d1ba"
dependencies:
async "^2.0.0"
babel-runtime "^6.0.0"
loader-utils "^1.0.0"
lodash "^4.0.0"
source-map "^0.5.6"
- webpack-dev-middleware "^2.0.6"
+ webpack-dev-middleware "^3.0.1"
-karma@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.2.tgz#4d2db9402850a66551fa784b0164fb0824ed8c4b"
+karma@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.4.tgz#b399785f57e9bab1d3c4384db33fef4dec8ae349"
dependencies:
bluebird "^3.3.0"
body-parser "^1.16.1"
- chokidar "^1.4.1"
+ chokidar "^2.0.3"
colors "^1.1.0"
combine-lists "^1.0.0"
connect "^3.6.0"
@@ -4739,7 +4504,7 @@ karma@^2.0.2:
http-proxy "^1.13.0"
isbinaryfile "^3.0.0"
lodash "^4.17.4"
- log4js "^2.3.9"
+ log4js "^2.5.3"
mime "^1.3.4"
minimatch "^3.0.2"
optimist "^0.6.1"
@@ -4810,10 +4575,6 @@ lcid@^1.0.0:
dependencies:
invert-kv "^1.0.0"
-leb@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3"
-
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -4889,6 +4650,10 @@ lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+
lodash.escaperegexp@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
@@ -4897,10 +4662,6 @@ lodash.kebabcase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
-lodash.memoize@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
-
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
@@ -4909,10 +4670,6 @@ lodash.snakecase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
-lodash.uniq@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
-
lodash.upperfirst@4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
@@ -4921,7 +4678,7 @@ lodash@4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@@ -4931,21 +4688,21 @@ log-symbols@^2.1.0:
dependencies:
chalk "^2.0.1"
-log4js@^2.3.9:
- version "2.5.3"
- resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1"
+log4js@^2.5.3:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.11.0.tgz#bf3902eff65c6923d9ce9cfbd2db54160e34005a"
dependencies:
- circular-json "^0.5.1"
+ circular-json "^0.5.4"
date-format "^1.2.0"
debug "^3.1.0"
- semver "^5.3.0"
- streamroller "^0.7.0"
+ semver "^5.5.0"
+ streamroller "0.7.0"
optionalDependencies:
amqplib "^0.5.2"
axios "^0.15.3"
hipchat-notifier "^1.1.0"
loggly "^1.1.0"
- mailgun-js "^0.7.0"
+ mailgun-js "^0.18.0"
nodemailer "^2.5.0"
redis "^2.7.1"
slack-node "~0.2.0"
@@ -4966,6 +4723,10 @@ loglevelnext@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e"
+long@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
+
long@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
@@ -5002,14 +4763,6 @@ lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2:
pseudomap "^1.0.2"
yallist "^2.1.2"
-lru-cache@~2.6.5:
- version "2.6.5"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
-
-macaddress@^0.2.8:
- version "0.2.8"
- resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
-
mailcomposer@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4"
@@ -5017,18 +4770,18 @@ mailcomposer@4.0.1:
buildmail "4.0.1"
libmime "3.0.0"
-mailgun-js@^0.7.0:
- version "0.7.15"
- resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb"
+mailgun-js@^0.18.0:
+ version "0.18.1"
+ resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.18.1.tgz#ee39aa18d7bb598a5ce9ede84afb681defc8a6b0"
dependencies:
- async "~2.1.2"
- debug "~2.2.0"
- form-data "~2.1.1"
- inflection "~1.10.0"
+ async "~2.6.0"
+ debug "~3.1.0"
+ form-data "~2.3.0"
+ inflection "~1.12.0"
is-stream "^1.1.0"
path-proxy "~1.0.0"
- proxy-agent "~2.0.0"
- q "~1.4.0"
+ promisify-call "^2.0.2"
+ proxy-agent "~3.0.0"
tsscmp "~1.0.0"
make-dir@^1.0.0:
@@ -5067,10 +4820,6 @@ match-at@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
-math-expression-evaluator@^1.2.14:
- version "1.2.16"
- resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
-
md5.js@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d"
@@ -5128,24 +4877,6 @@ methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
-micromatch@^2.1.5:
- version "2.3.11"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
- dependencies:
- arr-diff "^2.0.0"
- array-unique "^0.2.1"
- braces "^1.8.2"
- expand-brackets "^0.1.4"
- extglob "^0.3.1"
- filename-regex "^2.0.0"
- is-extglob "^1.0.0"
- is-glob "^2.0.1"
- kind-of "^3.0.2"
- normalize-path "^2.0.1"
- object.omit "^2.0.0"
- parse-glob "^3.0.4"
- regex-cache "^0.4.2"
-
micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@@ -5262,7 +4993,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
-mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
+mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
@@ -5272,9 +5003,9 @@ moment@2.x, moment@^2.18.1:
version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
-monaco-editor-webpack-plugin@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.2.1.tgz#577ed091420f422bb8f0ff3a8899dd82344da56d"
+monaco-editor-webpack-plugin@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.4.0.tgz#7324258ab3695464cfe3bc12edb2e8c55b80d92f"
monaco-editor@0.13.1:
version "0.13.1"
@@ -5295,10 +5026,6 @@ move-concurrently@^1.0.1:
rimraf "^2.5.4"
run-queue "^1.0.3"
-ms@0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
-
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -5359,7 +5086,7 @@ neo-async@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f"
-netmask@~1.0.4:
+netmask@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
@@ -5467,9 +5194,9 @@ nodemailer@^2.5.0:
nodemailer-smtp-transport "2.7.2"
socks "1.1.9"
-nodemon@^1.17.3:
- version "1.17.3"
- resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.17.3.tgz#3b0bbc2ee05ccb43b1aef15ba05c63c7bc9b8530"
+nodemon@^1.18.2:
+ version "1.18.2"
+ resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.2.tgz#36b89c790da70c4f270e2cc0718723131bc04abb"
dependencies:
chokidar "^2.0.2"
debug "^3.1.0"
@@ -5510,16 +5237,12 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
-normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
+normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies:
remove-trailing-separator "^1.0.1"
-normalize-range@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
-
normalize-url@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
@@ -5528,15 +5251,6 @@ normalize-url@2.0.1:
query-string "^5.0.1"
sort-keys "^2.0.0"
-normalize-url@^1.4.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
- dependencies:
- object-assign "^4.0.1"
- prepend-http "^1.0.0"
- query-string "^4.1.0"
- sort-keys "^1.0.0"
-
npm-bundled@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308"
@@ -5567,10 +5281,6 @@ null-check@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
-num2fraction@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
-
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -5579,7 +5289,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
-object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -5605,13 +5315,6 @@ object-visit@^1.0.0:
dependencies:
isobject "^3.0.0"
-object.omit@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
- dependencies:
- for-own "^0.1.4"
- is-extendable "^0.1.1"
-
object.pick@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
@@ -5743,29 +5446,28 @@ p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
-pac-proxy-agent@1:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
+pac-proxy-agent@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz#90d9f6730ab0f4d2607dcdcd4d3d641aa26c3896"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
- get-uri "2"
- http-proxy-agent "1"
- https-proxy-agent "1"
- pac-resolver "~2.0.0"
- raw-body "2"
- socks-proxy-agent "2"
-
-pac-resolver@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd"
+ agent-base "^4.2.0"
+ debug "^3.1.0"
+ get-uri "^2.0.0"
+ http-proxy-agent "^2.1.0"
+ https-proxy-agent "^2.2.1"
+ pac-resolver "^3.0.0"
+ raw-body "^2.2.0"
+ socks-proxy-agent "^3.0.0"
+
+pac-resolver@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26"
dependencies:
- co "~3.0.6"
- degenerator "~1.0.2"
- ip "1.0.1"
- netmask "~1.0.4"
- thunkify "~2.1.1"
+ co "^4.6.0"
+ degenerator "^1.0.4"
+ ip "^1.1.5"
+ netmask "^1.0.6"
+ thunkify "^2.1.2"
package-json@^4.0.0:
version "4.0.1"
@@ -5798,15 +5500,6 @@ parse-asn1@^5.0.0:
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
-parse-glob@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
- dependencies:
- glob-base "^0.3.0"
- is-dotfile "^1.0.0"
- is-extglob "^1.0.0"
- is-glob "^2.0.0"
-
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
@@ -5967,128 +5660,6 @@ posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
-postcss-calc@^5.2.0:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
- dependencies:
- postcss "^5.0.2"
- postcss-message-helpers "^2.0.0"
- reduce-css-calc "^1.2.6"
-
-postcss-colormin@^2.1.8:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
- dependencies:
- colormin "^1.0.5"
- postcss "^5.0.13"
- postcss-value-parser "^3.2.3"
-
-postcss-convert-values@^2.3.4:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
- dependencies:
- postcss "^5.0.11"
- postcss-value-parser "^3.1.2"
-
-postcss-discard-comments@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
- dependencies:
- postcss "^5.0.14"
-
-postcss-discard-duplicates@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
- dependencies:
- postcss "^5.0.4"
-
-postcss-discard-empty@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
- dependencies:
- postcss "^5.0.14"
-
-postcss-discard-overridden@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
- dependencies:
- postcss "^5.0.16"
-
-postcss-discard-unused@^2.2.1:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
- dependencies:
- postcss "^5.0.14"
- uniqs "^2.0.0"
-
-postcss-filter-plugins@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
- dependencies:
- postcss "^5.0.4"
- uniqid "^4.0.0"
-
-postcss-merge-idents@^2.1.5:
- version "2.1.7"
- resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
- dependencies:
- has "^1.0.1"
- postcss "^5.0.10"
- postcss-value-parser "^3.1.1"
-
-postcss-merge-longhand@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
- dependencies:
- postcss "^5.0.4"
-
-postcss-merge-rules@^2.0.3:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
- dependencies:
- browserslist "^1.5.2"
- caniuse-api "^1.5.2"
- postcss "^5.0.4"
- postcss-selector-parser "^2.2.2"
- vendors "^1.0.0"
-
-postcss-message-helpers@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
-
-postcss-minify-font-values@^1.0.2:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
- dependencies:
- object-assign "^4.0.1"
- postcss "^5.0.4"
- postcss-value-parser "^3.0.2"
-
-postcss-minify-gradients@^1.0.1:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
- dependencies:
- postcss "^5.0.12"
- postcss-value-parser "^3.3.0"
-
-postcss-minify-params@^1.0.4:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
- dependencies:
- alphanum-sort "^1.0.1"
- postcss "^5.0.2"
- postcss-value-parser "^3.0.2"
- uniqs "^2.0.0"
-
-postcss-minify-selectors@^2.0.4:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
- dependencies:
- alphanum-sort "^1.0.2"
- has "^1.0.1"
- postcss "^5.0.14"
- postcss-selector-parser "^2.0.0"
-
postcss-modules-extract-imports@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
@@ -6116,57 +5687,6 @@ postcss-modules-values@^1.3.0:
icss-replace-symbols "^1.1.0"
postcss "^6.0.1"
-postcss-normalize-charset@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
- dependencies:
- postcss "^5.0.5"
-
-postcss-normalize-url@^3.0.7:
- version "3.0.8"
- resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
- dependencies:
- is-absolute-url "^2.0.0"
- normalize-url "^1.4.0"
- postcss "^5.0.14"
- postcss-value-parser "^3.2.3"
-
-postcss-ordered-values@^2.1.0:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
- dependencies:
- postcss "^5.0.4"
- postcss-value-parser "^3.0.1"
-
-postcss-reduce-idents@^2.2.2:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
- dependencies:
- postcss "^5.0.4"
- postcss-value-parser "^3.0.2"
-
-postcss-reduce-initial@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
- dependencies:
- postcss "^5.0.4"
-
-postcss-reduce-transforms@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
- dependencies:
- has "^1.0.1"
- postcss "^5.0.8"
- postcss-value-parser "^3.0.1"
-
-postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
- dependencies:
- flatten "^1.0.2"
- indexes-of "^1.0.1"
- uniq "^1.0.1"
-
postcss-selector-parser@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
@@ -6175,44 +5695,10 @@ postcss-selector-parser@^3.1.1:
indexes-of "^1.0.1"
uniq "^1.0.1"
-postcss-svgo@^2.1.1:
- version "2.1.6"
- resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
- dependencies:
- is-svg "^2.0.0"
- postcss "^5.0.14"
- postcss-value-parser "^3.2.3"
- svgo "^0.7.0"
-
-postcss-unique-selectors@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
- dependencies:
- alphanum-sort "^1.0.1"
- postcss "^5.0.4"
- uniqs "^2.0.0"
-
-postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
+postcss-value-parser@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
-postcss-zindex@^2.0.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
- dependencies:
- has "^1.0.1"
- postcss "^5.0.4"
- uniqs "^2.0.0"
-
-postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
- version "5.2.16"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.16.tgz#732b3100000f9ff8379a48a53839ed097376ad57"
- dependencies:
- chalk "^1.1.3"
- js-base64 "^2.1.9"
- source-map "^0.5.6"
- supports-color "^3.2.3"
-
postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20:
version "6.0.22"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3"
@@ -6221,11 +5707,19 @@ postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20:
source-map "^0.6.1"
supports-color "^5.4.0"
+postcss@^6.0.23:
+ version "6.0.23"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+ dependencies:
+ chalk "^2.4.1"
+ source-map "^0.6.1"
+ supports-color "^5.4.0"
+
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
-prepend-http@^1.0.0, prepend-http@^1.0.1:
+prepend-http@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
@@ -6233,10 +5727,6 @@ prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
-
prettier@1.12.1, prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
@@ -6271,6 +5761,12 @@ promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+promisify-call@^2.0.2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/promisify-call/-/promisify-call-2.0.4.tgz#d48c2d45652ccccd52801ddecbd533a6d4bd5fba"
+ dependencies:
+ with-callback "^1.0.2"
+
proxy-addr@~2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@@ -6278,18 +5774,22 @@ proxy-addr@~2.0.2:
forwarded "~0.1.2"
ipaddr.js "1.6.0"
-proxy-agent@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499"
+proxy-agent@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.1.tgz#4fb7b61b1476d0fe8e3a3384d90e2460bbded3f9"
dependencies:
- agent-base "2"
- debug "2"
- extend "3"
- http-proxy-agent "1"
- https-proxy-agent "1"
- lru-cache "~2.6.5"
- pac-proxy-agent "1"
- socks-proxy-agent "2"
+ agent-base "^4.2.0"
+ debug "^3.1.0"
+ http-proxy-agent "^2.1.0"
+ https-proxy-agent "^2.2.1"
+ lru-cache "^4.1.2"
+ pac-proxy-agent "^2.0.1"
+ proxy-from-env "^1.0.0"
+ socks-proxy-agent "^4.0.1"
+
+proxy-from-env@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
prr@~0.0.0:
version "0.0.0"
@@ -6348,14 +5848,6 @@ punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
-q@^1.1.2:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
-
-q@~1.4.0:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
-
qjobs@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
@@ -6368,13 +5860,6 @@ qs@~6.2.0:
version "6.2.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
-query-string@^4.1.0:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.2.tgz#ec0fd765f58a50031a3968c2431386f8947a5cdd"
- dependencies:
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
query-string@^5.0.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
@@ -6399,13 +5884,6 @@ querystringify@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
-randomatic@^1.1.3:
- version "1.1.7"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
- dependencies:
- is-number "^3.0.0"
- kind-of "^4.0.0"
-
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
@@ -6433,7 +5911,7 @@ raven-js@^3.22.1:
version "3.22.1"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
-raw-body@2, raw-body@2.3.2:
+raw-body@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
dependencies:
@@ -6442,6 +5920,15 @@ raw-body@2, raw-body@2.3.2:
iconv-lite "0.4.19"
unpipe "1.0.0"
+raw-body@^2.2.0:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
+ dependencies:
+ bytes "3.0.0"
+ http-errors "1.6.3"
+ iconv-lite "0.4.23"
+ unpipe "1.0.0"
+
raw-loader@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
@@ -6561,20 +6048,6 @@ redis@^2.7.1:
redis-commands "^1.2.0"
redis-parser "^2.6.0"
-reduce-css-calc@^1.2.6:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
- dependencies:
- balanced-match "^0.4.2"
- math-expression-evaluator "^1.2.14"
- reduce-function-call "^1.0.1"
-
-reduce-function-call@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
- dependencies:
- balanced-match "^0.4.2"
-
regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
@@ -6591,12 +6064,6 @@ regenerator-transform@^0.10.0:
babel-types "^6.19.0"
private "^0.1.6"
-regex-cache@^0.4.2:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
- dependencies:
- is-equal-shallow "^0.1.3"
-
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@@ -6832,11 +6299,11 @@ rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
-rxjs@^5.5.2:
- version "5.5.10"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045"
+rxjs@^6.1.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1"
dependencies:
- symbol-observable "1.0.1"
+ tslib "^1.9.0"
safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
@@ -6872,10 +6339,6 @@ sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
-sax@~1.2.1:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
-
schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.3, schema-utils@^0.4.4, schema-utils@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
@@ -6911,10 +6374,6 @@ semver-diff@^2.0.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
-semver@~5.0.1:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
-
send@0.16.1:
version "0.16.1"
resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3"
@@ -7050,6 +6509,10 @@ smart-buffer@^1.0.13, smart-buffer@^1.0.4:
version "1.1.15"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
+smart-buffer@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3"
+
smtp-connection@2.12.0:
version "2.12.0"
resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1"
@@ -7155,13 +6618,19 @@ sockjs@0.3.19:
faye-websocket "^0.10.0"
uuid "^3.0.1"
-socks-proxy-agent@2:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3"
+socks-proxy-agent@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz#2eae7cf8e2a82d34565761539a7f9718c5617659"
dependencies:
- agent-base "2"
- extend "3"
- socks "~1.1.5"
+ agent-base "^4.1.0"
+ socks "^1.1.10"
+
+socks-proxy-agent@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473"
+ dependencies:
+ agent-base "~4.2.0"
+ socks "~2.2.0"
socks@1.1.9:
version "1.1.9"
@@ -7170,18 +6639,19 @@ socks@1.1.9:
ip "^1.1.2"
smart-buffer "^1.0.4"
-socks@~1.1.5:
+socks@^1.1.10:
version "1.1.10"
resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
dependencies:
ip "^1.1.4"
smart-buffer "^1.0.13"
-sort-keys@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
+socks@~2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.1.tgz#68ad678b3642fbc5d99c64c165bc561eab0215f9"
dependencies:
- is-plain-obj "^1.0.0"
+ ip "^1.1.5"
+ smart-buffer "^4.0.1"
sort-keys@^2.0.0:
version "2.0.0"
@@ -7340,6 +6810,10 @@ static-extend@^0.1.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+"statuses@>= 1.4.0 < 2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+
statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
@@ -7382,7 +6856,7 @@ stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
-streamroller@^0.7.0:
+streamroller@0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
dependencies:
@@ -7477,7 +6951,7 @@ supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3:
+supports-color@^3.1.0, supports-color@^3.1.2:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
@@ -7493,22 +6967,6 @@ svg4everybody@2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d"
-svgo@^0.7.0:
- version "0.7.2"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
- dependencies:
- coa "~1.0.1"
- colors "~1.1.2"
- csso "~2.3.1"
- js-yaml "~3.7.0"
- mkdirp "~0.5.1"
- sax "~1.2.1"
- whet.extend "~0.9.9"
-
-symbol-observable@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
-
table@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
@@ -7583,7 +7041,7 @@ through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
-thunkify@~2.1.1:
+thunkify@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
@@ -7691,6 +7149,10 @@ tryit@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+tslib@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
+
tsscmp@~1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97"
@@ -7794,16 +7256,6 @@ uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
-uniqid@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
- dependencies:
- macaddress "^0.2.8"
-
-uniqs@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
-
unique-filename@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
@@ -7841,6 +7293,10 @@ upath@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.5.tgz#02cab9ecebe95bbec6d5fc2566325725ab6d1a73"
+upath@^1.0.5:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd"
+
update-notifier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451"
@@ -7859,10 +7315,6 @@ urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
-url-join@^2.0.2:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728"
-
url-join@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.0.tgz#4d3340e807d3773bda9991f8305acdcc2a665d2a"
@@ -7968,10 +7420,6 @@ vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
-vendors@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
-
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
@@ -8009,9 +7457,9 @@ 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"
-vue-loader@^15.2.0:
- version "15.2.0"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.2.0.tgz#5a8138e490a1040942d2f10ae68fa72b5a923364"
+vue-loader@^15.2.4:
+ version "15.2.4"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.2.4.tgz#a7b923123d3cf87230a8ff54a1c16d31a6c5dbb4"
dependencies:
"@vue/component-compiler-utils" "^1.2.1"
hash-sum "^1.0.2"
@@ -8073,9 +7521,9 @@ wbuf@^1.1.0, wbuf@^1.7.2:
dependencies:
minimalistic-assert "^1.0.0"
-webpack-bundle-analyzer@^2.11.1:
- version "2.11.1"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.11.1.tgz#b9fbfb6a32c0a8c1c3237223e90890796b950ab9"
+webpack-bundle-analyzer@^2.13.1:
+ version "2.13.1"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz#07d2176c6e86c3cdce4c23e56fae2a7b6b4ad526"
dependencies:
acorn "^5.3.0"
bfj-node4 "^5.2.0"
@@ -8090,23 +7538,23 @@ webpack-bundle-analyzer@^2.11.1:
opener "^1.4.3"
ws "^4.0.0"
-webpack-cli@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.2.tgz#e48c5662aff8ed5aac3db5f82f51d7f32e50459e"
+webpack-cli@^3.0.8:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.8.tgz#90eddcf04a4bfc31aa8c0edc4c76785bc4f1ccd9"
dependencies:
chalk "^2.4.1"
cross-spawn "^6.0.5"
enhanced-resolve "^4.0.0"
global-modules-path "^2.1.0"
import-local "^1.0.0"
- inquirer "^5.2.0"
+ inquirer "^6.0.0"
interpret "^1.1.0"
loader-utils "^1.1.0"
supports-color "^5.4.0"
v8-compile-cache "^2.0.0"
yargs "^11.1.0"
-webpack-dev-middleware@3.1.3:
+webpack-dev-middleware@3.1.3, webpack-dev-middleware@^3.0.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz#8b32aa43da9ae79368c1bf1183f2b6cf5e1f39ed"
dependencies:
@@ -8118,18 +7566,6 @@ webpack-dev-middleware@3.1.3:
url-join "^4.0.0"
webpack-log "^1.0.1"
-webpack-dev-middleware@^2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz#a51692801e8310844ef3e3790e1eacfe52326fd4"
- dependencies:
- loud-rejection "^1.6.0"
- memory-fs "~0.4.1"
- mime "^2.1.0"
- path-is-absolute "^1.0.0"
- range-parser "^1.0.3"
- url-join "^2.0.2"
- webpack-log "^1.0.1"
-
webpack-dev-server@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.4.tgz#9a08d13c4addd1e3b6d8ace116e86715094ad5b4"
@@ -8183,21 +7619,21 @@ webpack-stats-plugin@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.2.1.tgz#1f5bac13fc25d62cbb5fd0ff646757dc802b8595"
-webpack@^4.11.1:
- version "4.11.1"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.11.1.tgz#1aa0b936f7ae93a52cf38d2ad0d0f46dcf3c2723"
+webpack@^4.16.0:
+ version "4.16.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.0.tgz#660dae90890e55b8ed17c6f9d17bebb01dab5b4c"
dependencies:
- "@webassemblyjs/ast" "1.5.10"
- "@webassemblyjs/helper-module-context" "1.5.10"
- "@webassemblyjs/wasm-edit" "1.5.10"
- "@webassemblyjs/wasm-opt" "1.5.10"
- "@webassemblyjs/wasm-parser" "1.5.10"
- acorn "^5.0.0"
+ "@webassemblyjs/ast" "1.5.13"
+ "@webassemblyjs/helper-module-context" "1.5.13"
+ "@webassemblyjs/wasm-edit" "1.5.13"
+ "@webassemblyjs/wasm-opt" "1.5.13"
+ "@webassemblyjs/wasm-parser" "1.5.13"
+ acorn "^5.6.2"
acorn-dynamic-import "^3.0.0"
ajv "^6.1.0"
ajv-keywords "^3.1.0"
- chrome-trace-event "^0.1.1"
- enhanced-resolve "^4.0.0"
+ chrome-trace-event "^1.0.0"
+ enhanced-resolve "^4.1.0"
eslint-scope "^3.7.1"
json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
@@ -8227,10 +7663,6 @@ when@^3.7.7:
version "3.7.8"
resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82"
-whet.extend@~0.9.9:
- version "0.9.9"
- resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
-
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@@ -8257,6 +7689,10 @@ window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+with-callback@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/with-callback/-/with-callback-1.0.2.tgz#a09629b9a920028d721404fb435bdcff5c91bc21"
+
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"