summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml40
-rw-r--r--.gitlab/merge_request_templates/Database changes.md22
-rw-r--r--.gitlab/merge_request_templates/Documentation.md14
-rw-r--r--CHANGELOG.md22
-rw-r--r--app/assets/javascripts/diffs/components/app.vue5
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue12
-rw-r--r--app/assets/javascripts/diffs/components/diff_table_cell.vue12
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue8
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue13
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue16
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue6
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue18
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue14
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue8
-rw-r--r--app/assets/javascripts/diffs/store/getters.js4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/button.vue1
-rw-r--r--app/assets/javascripts/monitoring/stores/monitoring_store.js9
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue162
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/help_popover.vue48
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/issues_list.vue99
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue33
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/report_issues.vue72
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/report_link.vue29
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/report_section.vue192
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/summary_row.vue66
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss5
-rw-r--r--app/assets/stylesheets/framework/gfm.scss8
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/clusters.scss48
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss1
-rw-r--r--app/controllers/projects/wikis_controller.rb7
-rw-r--r--app/helpers/projects_helper.rb14
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb11
-rw-r--r--app/models/project_wiki.rb7
-rw-r--r--app/models/wiki_page.rb2
-rw-r--r--app/services/application_settings/base_service.rb2
-rw-r--r--app/services/application_settings/update_service.rb2
-rw-r--r--app/services/applications/create_service.rb2
-rw-r--r--app/services/auth/container_registry_authentication_service.rb2
-rw-r--r--app/services/badges/base_service.rb2
-rw-r--r--app/services/badges/build_service.rb2
-rw-r--r--app/services/badges/create_service.rb2
-rw-r--r--app/services/badges/update_service.rb2
-rw-r--r--app/services/boards/base_service.rb2
-rw-r--r--app/services/boards/create_service.rb2
-rw-r--r--app/services/boards/issues/create_service.rb2
-rw-r--r--app/services/boards/issues/list_service.rb2
-rw-r--r--app/services/boards/issues/move_service.rb2
-rw-r--r--app/services/boards/list_service.rb2
-rw-r--r--app/services/boards/lists/create_service.rb2
-rw-r--r--app/services/boards/lists/destroy_service.rb2
-rw-r--r--app/services/boards/lists/generate_service.rb2
-rw-r--r--app/services/boards/lists/list_service.rb2
-rw-r--r--app/services/boards/lists/move_service.rb2
-rw-r--r--app/services/chat_names/authorize_user_service.rb2
-rw-r--r--app/services/chat_names/find_user_service.rb2
-rw-r--r--app/services/ci/create_pipeline_schedule_service.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/ci/ensure_stage_service.rb2
-rw-r--r--app/services/ci/extract_sections_from_build_trace_service.rb2
-rw-r--r--app/services/ci/fetch_kubernetes_token_service.rb2
-rw-r--r--app/services/ci/pipeline_trigger_service.rb2
-rw-r--r--app/services/ci/play_build_service.rb2
-rw-r--r--app/services/ci/process_pipeline_service.rb2
-rw-r--r--app/services/ci/register_job_service.rb2
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/services/ci/retry_pipeline_service.rb2
-rw-r--r--app/services/ci/stop_environments_service.rb2
-rw-r--r--app/services/ci/update_build_queue_service.rb2
-rw-r--r--app/services/ci/update_runner_service.rb2
-rw-r--r--app/services/clusters/applications/base_helm_service.rb2
-rw-r--r--app/services/clusters/applications/check_ingress_ip_address_service.rb2
-rw-r--r--app/services/clusters/applications/check_installation_progress_service.rb2
-rw-r--r--app/services/clusters/applications/install_service.rb2
-rw-r--r--app/services/clusters/applications/schedule_installation_service.rb2
-rw-r--r--app/services/clusters/create_service.rb2
-rw-r--r--app/services/clusters/gcp/fetch_operation_service.rb2
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb2
-rw-r--r--app/services/clusters/gcp/provision_service.rb2
-rw-r--r--app/services/clusters/gcp/verify_provision_status_service.rb2
-rw-r--r--app/services/clusters/update_service.rb2
-rw-r--r--app/services/commits/change_service.rb2
-rw-r--r--app/services/commits/cherry_pick_service.rb2
-rw-r--r--app/services/commits/create_service.rb2
-rw-r--r--app/services/commits/revert_service.rb2
-rw-r--r--app/services/concerns/exclusive_lease_guard.rb2
-rw-r--r--app/services/concerns/issues/resolve_discussions.rb2
-rw-r--r--app/services/concerns/update_visibility_level.rb2
-rw-r--r--app/services/concerns/users/new_user_notifier.rb2
-rw-r--r--app/services/concerns/users/participable_service.rb2
-rw-r--r--app/services/deploy_keys/create_service.rb2
-rw-r--r--app/services/deploy_tokens/create_service.rb2
-rw-r--r--app/services/discussions/base_service.rb2
-rw-r--r--app/services/discussions/resolve_service.rb2
-rw-r--r--app/services/discussions/update_diff_position_service.rb2
-rw-r--r--app/services/emails/base_service.rb2
-rw-r--r--app/services/emails/confirm_service.rb2
-rw-r--r--app/services/emails/create_service.rb2
-rw-r--r--app/services/emails/destroy_service.rb2
-rw-r--r--app/services/events/render_service.rb2
-rw-r--r--app/services/files/base_service.rb2
-rw-r--r--app/services/files/create_dir_service.rb2
-rw-r--r--app/services/files/create_service.rb2
-rw-r--r--app/services/files/delete_service.rb2
-rw-r--r--app/services/files/multi_service.rb2
-rw-r--r--app/services/files/update_service.rb2
-rw-r--r--app/services/gpg_keys/create_service.rb2
-rw-r--r--app/services/groups/base_service.rb2
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/groups/destroy_service.rb2
-rw-r--r--app/services/groups/nested_create_service.rb2
-rw-r--r--app/services/groups/transfer_service.rb2
-rw-r--r--app/services/groups/update_service.rb2
-rw-r--r--app/services/issuable/bulk_update_service.rb2
-rw-r--r--app/services/issuable/common_system_notes_service.rb2
-rw-r--r--app/services/issuable/destroy_service.rb2
-rw-r--r--app/services/issues/base_service.rb2
-rw-r--r--app/services/issues/build_service.rb8
-rw-r--r--app/services/issues/close_service.rb2
-rw-r--r--app/services/issues/create_service.rb2
-rw-r--r--app/services/issues/duplicate_service.rb2
-rw-r--r--app/services/issues/fetch_referenced_merge_requests_service.rb2
-rw-r--r--app/services/issues/move_service.rb2
-rw-r--r--app/services/issues/reopen_service.rb2
-rw-r--r--app/services/issues/update_service.rb2
-rw-r--r--app/services/keys/base_service.rb2
-rw-r--r--app/services/keys/create_service.rb2
-rw-r--r--app/services/keys/destroy_service.rb2
-rw-r--r--app/services/keys/last_used_service.rb2
-rw-r--r--app/services/labels/base_service.rb2
-rw-r--r--app/services/labels/create_service.rb2
-rw-r--r--app/services/labels/find_or_create_service.rb2
-rw-r--r--app/services/labels/promote_service.rb2
-rw-r--r--app/services/labels/transfer_service.rb2
-rw-r--r--app/services/labels/update_service.rb2
-rw-r--r--app/services/lfs/file_transformer.rb2
-rw-r--r--app/services/lfs/lock_file_service.rb2
-rw-r--r--app/services/lfs/locks_finder_service.rb2
-rw-r--r--app/services/lfs/unlock_file_service.rb2
-rw-r--r--app/services/mattermost/create_team_service.rb2
-rw-r--r--app/services/members/approve_access_request_service.rb2
-rw-r--r--app/services/members/base_service.rb2
-rw-r--r--app/services/members/create_service.rb2
-rw-r--r--app/services/members/destroy_service.rb2
-rw-r--r--app/services/members/request_access_service.rb2
-rw-r--r--app/services/members/update_service.rb2
-rw-r--r--app/services/merge_requests/add_todo_when_build_fails_service.rb2
-rw-r--r--app/services/merge_requests/assign_issues_service.rb2
-rw-r--r--app/services/merge_requests/base_service.rb2
-rw-r--r--app/services/merge_requests/build_service.rb11
-rw-r--r--app/services/merge_requests/close_service.rb2
-rw-r--r--app/services/merge_requests/conflicts/base_service.rb2
-rw-r--r--app/services/merge_requests/conflicts/list_service.rb2
-rw-r--r--app/services/merge_requests/conflicts/resolve_service.rb2
-rw-r--r--app/services/merge_requests/create_from_issue_service.rb2
-rw-r--r--app/services/merge_requests/create_service.rb2
-rw-r--r--app/services/merge_requests/delete_non_latest_diffs_service.rb2
-rw-r--r--app/services/merge_requests/ff_merge_service.rb2
-rw-r--r--app/services/merge_requests/get_urls_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/merge_requests/merge_when_pipeline_succeeds_service.rb2
-rw-r--r--app/services/merge_requests/post_merge_service.rb2
-rw-r--r--app/services/merge_requests/rebase_service.rb2
-rw-r--r--app/services/merge_requests/refresh_service.rb2
-rw-r--r--app/services/merge_requests/reload_diffs_service.rb2
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/services/merge_requests/resolved_discussion_notification_service.rb2
-rw-r--r--app/services/merge_requests/squash_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/merge_requests/working_copy_base_service.rb2
-rw-r--r--app/services/milestones/base_service.rb2
-rw-r--r--app/services/milestones/close_service.rb2
-rw-r--r--app/services/milestones/create_service.rb2
-rw-r--r--app/services/milestones/destroy_service.rb2
-rw-r--r--app/services/milestones/promote_service.rb2
-rw-r--r--app/services/milestones/reopen_service.rb2
-rw-r--r--app/services/milestones/update_service.rb2
-rw-r--r--app/services/notes/build_service.rb2
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/notes/destroy_service.rb2
-rw-r--r--app/services/notes/post_process_service.rb2
-rw-r--r--app/services/notes/quick_actions_service.rb2
-rw-r--r--app/services/notes/render_service.rb2
-rw-r--r--app/services/notes/resolve_service.rb2
-rw-r--r--app/services/notes/update_service.rb2
-rw-r--r--app/services/projects/after_import_service.rb2
-rw-r--r--app/services/projects/autocomplete_service.rb2
-rw-r--r--app/services/projects/base_move_relations_service.rb2
-rw-r--r--app/services/projects/batch_count_service.rb2
-rw-r--r--app/services/projects/batch_forks_count_service.rb2
-rw-r--r--app/services/projects/batch_open_issues_count_service.rb2
-rw-r--r--app/services/projects/count_service.rb2
-rw-r--r--app/services/projects/create_from_template_service.rb2
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb2
-rw-r--r--app/services/projects/download_service.rb2
-rw-r--r--app/services/projects/enable_deploy_key_service.rb2
-rw-r--r--app/services/projects/fork_service.rb2
-rw-r--r--app/services/projects/forks_count_service.rb2
-rw-r--r--app/services/projects/gitlab_projects_import_service.rb2
-rw-r--r--app/services/projects/group_links/create_service.rb2
-rw-r--r--app/services/projects/group_links/destroy_service.rb2
-rw-r--r--app/services/projects/hashed_storage/migrate_attachments_service.rb2
-rw-r--r--app/services/projects/hashed_storage/migrate_repository_service.rb2
-rw-r--r--app/services/projects/hashed_storage_migration_service.rb2
-rw-r--r--app/services/projects/housekeeping_service.rb2
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_link_list_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_import_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_link_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_list_service.rb2
-rw-r--r--app/services/projects/move_access_service.rb2
-rw-r--r--app/services/projects/move_deploy_keys_projects_service.rb2
-rw-r--r--app/services/projects/move_forks_service.rb2
-rw-r--r--app/services/projects/move_lfs_objects_projects_service.rb2
-rw-r--r--app/services/projects/move_notification_settings_service.rb2
-rw-r--r--app/services/projects/move_project_authorizations_service.rb2
-rw-r--r--app/services/projects/move_project_group_links_service.rb2
-rw-r--r--app/services/projects/move_project_members_service.rb2
-rw-r--r--app/services/projects/move_users_star_projects_service.rb2
-rw-r--r--app/services/projects/open_issues_count_service.rb2
-rw-r--r--app/services/projects/open_merge_requests_count_service.rb2
-rw-r--r--app/services/projects/overwrite_project_service.rb2
-rw-r--r--app/services/projects/participants_service.rb2
-rw-r--r--app/services/projects/propagate_service_template.rb2
-rw-r--r--app/services/projects/transfer_service.rb2
-rw-r--r--app/services/projects/unlink_fork_service.rb2
-rw-r--r--app/services/projects/update_pages_configuration_service.rb2
-rw-r--r--app/services/projects/update_pages_service.rb2
-rw-r--r--app/services/projects/update_remote_mirror_service.rb2
-rw-r--r--app/services/projects/update_service.rb2
-rw-r--r--app/views/admin/identities/edit.html.haml3
-rw-r--r--app/views/admin/identities/index.html.haml2
-rw-r--r--app/views/admin/identities/new.html.haml3
-rw-r--r--app/views/admin/impersonation_tokens/index.html.haml2
-rw-r--r--app/views/admin/users/keys.html.haml2
-rw-r--r--app/views/admin/users/projects.html.haml2
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml7
-rw-r--r--app/views/projects/_home_panel.html.haml3
-rw-r--r--app/views/projects/clusters/_gcp_signup_offer_banner.html.haml14
-rw-r--r--app/views/projects/commit/_change.html.haml2
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml8
-rw-r--r--changelogs/custom_wiki_sidebar.yml5
-rw-r--r--changelogs/unreleased/47419-Fix-breadcrumbs.yml5
-rw-r--r--changelogs/unreleased/48804-redesign-gcp-banner.yml5
-rw-r--r--changelogs/unreleased/add-merge-request-header-branch-details-right-margin.yml5
-rw-r--r--changelogs/unreleased/close-revert-and-cherry-pick-modal-on-escape-keypress.yml5
-rw-r--r--changelogs/unreleased/feature-gb-email-delivery-metrics.yml5
-rw-r--r--changelogs/unreleased/features-show-project-id-on-home-panel.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-apps-services-inner.yml5
-rw-r--r--changelogs/unreleased/osw-fallback-to-collection-when-no-diff-refs.yml5
-rw-r--r--changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-49133.yml5
-rw-r--r--changelogs/unreleased/update-specific-runners-help-url.yml5
-rw-r--r--config/initializers/action_mailer_hooks.rb12
-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--danger/changelog/Dangerfile14
-rw-r--r--danger/specs/Dangerfile14
-rw-r--r--doc/api/pipelines.md4
-rw-r--r--doc/api/users.md30
-rw-r--r--doc/development/sql.md42
-rw-r--r--doc/user/gitlab_com/index.md3
-rw-r--r--doc/user/project/import/manifest.md3
-rw-r--r--doc/user/project/integrations/webhooks.md28
-rw-r--r--doc/user/project/wiki/index.md7
-rw-r--r--lib/additional_email_headers_interceptor.rb6
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb22
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb18
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb12
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb8
-rw-r--r--lib/disable_email_interceptor.rb7
-rw-r--r--lib/email_template_interceptor.rb11
-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/git/commit.rb64
-rw-r--r--lib/gitlab/git/repository.rb164
-rw-r--r--lib/gitlab/git/repository_mirroring.rb2
-rw-r--r--lib/gitlab/hook_data/base_builder.rb38
-rw-r--r--lib/gitlab/hook_data/issuable_builder.rb8
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb9
-rw-r--r--lib/gitlab/hook_data/merge_request_builder.rb9
-rw-r--r--lib/gitlab/hook_data/note_builder.rb43
-rw-r--r--lib/gitlab/hook_data/wiki_page_builder.rb15
-rw-r--r--lib/gitlab/import_export/file_importer.rb3
-rw-r--r--lib/gitlab/regex.rb26
-rw-r--r--locale/gitlab.pot27
-rw-r--r--qa/qa.rb10
-rw-r--r--qa/qa/factory/resource/file.rb34
-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/project/show.rb8
-rw-r--r--qa/qa/page/view.rb4
-rw-r--r--qa/qa/specs/features/project/file_spec.rb54
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb17
-rw-r--r--spec/features/admin/admin_users_spec.rb38
-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)22
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb46
-rw-r--r--spec/features/user_sees_revert_modal_spec.rb25
-rw-r--r--spec/helpers/projects_helper_spec.rb27
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js2
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js19
-rw-r--r--spec/javascripts/monitoring/mock_data.js17
-rw-r--r--spec/javascripts/monitoring/monitoring_store_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js45
-rw-r--r--spec/javascripts/vue_shared/components/reports/report_issues_spec.js0
-rw-r--r--spec/javascripts/vue_shared/components/reports/report_link_spec.js71
-rw-r--r--spec/javascripts/vue_shared/components/reports/report_section_spec.js174
-rw-r--r--spec/javascripts/vue_shared/components/reports/summary_row_spec.js37
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb22
-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/git/commit_spec.rb18
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb45
-rw-r--r--spec/lib/gitlab/hook_data/base_builder_spec.rb64
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb7
-rw-r--r--spec/mailers/notify_spec.rb6
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb5
-rw-r--r--spec/models/merge_request_diff_spec.rb8
-rw-r--r--spec/models/project_wiki_spec.rb16
-rw-r--r--spec/models/wiki_page_spec.rb10
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb3
-rw-r--r--spec/views/projects/_home_panel.html.haml_spec.rb121
342 files changed, 2688 insertions, 751 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e7b28e540a5..afe9da08495 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -350,25 +350,6 @@ retrieve-tests-metadata:
- wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH
- '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}'
-danger-review:
- image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest
- stage: prepare
- 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:
- variables:
- - $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
- script:
- - git version
- - danger --fail-on-errors=true
-
update-tests-metadata:
<<: *tests-metadata-state
<<: *only-canonical-masters
@@ -457,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/CHANGELOG.md b/CHANGELOG.md
index e90f599ced1..e1a6a014c57 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.0.4 (2018-07-17)
+
+### Security (1 change)
+
+- Fix symlink vulnerability in project import.
+
+
## 11.0.3 (2018-07-05)
### Fixed (14 changes, 1 of them is from the community)
@@ -295,6 +302,14 @@ entry.
- Workhorse to send raw diff and patch for commits.
+## 10.8.6 (2018-07-17)
+
+### Security (2 changes)
+
+- Fix symlink vulnerability in project import.
+- Merge branch 'fix-mr-widget-border' into 'master'.
+
+
## 10.8.5 (2018-06-21)
### Security (5 changes)
@@ -524,6 +539,13 @@ entry.
- Gitaly handles repository forks by default.
+## 10.7.7 (2018-07-17)
+
+### Security (1 change)
+
+- Fix symlink vulnerability in project import.
+
+
## 10.7.6 (2018-06-21)
### Security (6 changes)
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 1d1415fe6ca..7cc4e6a2c3a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -85,6 +85,9 @@ export default {
}
return __('Show latest version');
},
+ canCurrentUserFork() {
+ return this.currentUser.canFork === true && this.currentUser.canCreateMergeRequest;
+ },
},
watch: {
diffViewType() {
@@ -192,7 +195,7 @@ export default {
v-for="file in diffFiles"
:key="file.newPath"
:file="file"
- :current-user="currentUser"
+ :can-current-user-fork="canCurrentUserFork"
/>
</div>
<no-changes v-else />
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 944084f05c9..7e7058d8d08 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -18,8 +18,8 @@ export default {
type: Object,
required: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
},
@@ -87,7 +87,7 @@ 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"
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index c5abd0a9568..c494d3bcd3e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -39,8 +39,8 @@ export default {
required: false,
default: true,
},
- currentUser: {
- type: Object,
+ canCurrentUserFork: {
+ type: Boolean,
required: true,
},
},
@@ -228,7 +228,7 @@ export default {
<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_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index db380e68bd1..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() {
@@ -68,13 +65,14 @@ 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,
});
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index bd02b45a63c..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: {
@@ -120,14 +124,14 @@ export default {
: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 1e8f2eecd76..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: {
@@ -58,10 +54,9 @@ export default {
/>
<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 8e4715c9862..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: {
@@ -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,7 +92,8 @@ 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"
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 9c1359f7c89..9fd19b74cd7 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -60,15 +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
v-if="shouldRenderCommentRow(line)"
- :diff-file="diffFile"
- :diff-lines="normalizedDiffLines"
+ :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 1e20792b647..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: {
@@ -91,10 +87,9 @@ export default {
<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,10 +107,9 @@ export default {
<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 b76fc63205b..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: {
@@ -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"
@@ -123,7 +128,8 @@ export default {
>
</td>
<diff-table-cell
- :diff-file="diffFile"
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
:line="line"
:line-type="newLineType"
:line-position="linePositionRight"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 216865474a6..32528c9e7ab 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -93,17 +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
v-if="shouldRenderCommentRow(line)"
- :key="line.left.lineCode || line.right.lineCode"
+ :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/getters.js b/app/assets/javascripts/diffs/store/getters.js
index f89acb73ed8..855de79adf8 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -57,4 +57,8 @@ export const getDiffFileDiscussions = (state, getters, rootState, rootGetters) =
) || [];
// 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/ide/components/new_dropdown/button.vue b/app/assets/javascripts/ide/components/new_dropdown/button.vue
index 7682b34ce4d..ff114e47741 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/button.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/button.vue
@@ -38,6 +38,7 @@ export default {
<button
:aria-label="label"
type="button"
+ class="btn-blank"
@click.stop.prevent="clicked"
>
<icon
diff --git a/app/assets/javascripts/monitoring/stores/monitoring_store.js b/app/assets/javascripts/monitoring/stores/monitoring_store.js
index 748b8cb6e6e..176f7d9eef2 100644
--- a/app/assets/javascripts/monitoring/stores/monitoring_store.js
+++ b/app/assets/javascripts/monitoring/stores/monitoring_store.js
@@ -1,7 +1,10 @@
import _ from 'underscore';
function sortMetrics(metrics) {
- return _.chain(metrics).sortBy('title').sortBy('weight').value();
+ return _.chain(metrics)
+ .sortBy('title')
+ .sortBy('weight')
+ .value();
}
function normalizeMetrics(metrics) {
@@ -39,7 +42,9 @@ export default class MonitoringStore {
}
storeEnvironmentsData(environmentsData = []) {
- this.environmentsData = environmentsData;
+ this.environmentsData = environmentsData.filter(
+ environment => !!environment.latest.last_deployment,
+ );
}
getMetricsCount() {
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/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/vue_shared/components/reports/help_popover.vue b/app/assets/javascripts/vue_shared/components/reports/help_popover.vue
new file mode 100644
index 00000000000..c5faa29fd2a
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/help_popover.vue
@@ -0,0 +1,48 @@
+<script>
+import $ from 'jquery';
+import Icon from '~/vue_shared/components/icon.vue';
+import { inserted } from '~/feature_highlight/feature_highlight_helper';
+import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover';
+
+export default {
+ name: 'ReportsHelpPopover',
+ components: {
+ Icon,
+ },
+ props: {
+ options: {
+ type: Object,
+ required: true,
+ },
+ },
+ mounted() {
+ const $el = $(this.$el);
+
+ $el
+ .popover({
+ html: true,
+ trigger: 'focus',
+ container: 'body',
+ placement: 'top',
+ template:
+ '<div class="popover" role="tooltip"><div class="arrow"></div><p class="popover-header"></p><div class="popover-body"></div></div>',
+ ...this.options,
+ })
+ .on('mouseenter', mouseenter)
+ .on('mouseleave', debouncedMouseleave(300))
+ .on('inserted.bs.popover', inserted)
+ .on('show.bs.popover', () => {
+ window.addEventListener('scroll', togglePopover.bind($el, false), { once: true });
+ });
+ },
+};
+</script>
+<template>
+ <button
+ type="button"
+ class="btn btn-blank btn-transparent btn-help"
+ tabindex="0"
+ >
+ <icon name="question" />
+ </button>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue b/app/assets/javascripts/vue_shared/components/reports/issues_list.vue
new file mode 100644
index 00000000000..e1e03e39ee0
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/issues_list.vue
@@ -0,0 +1,99 @@
+<script>
+import IssuesBlock from './report_issues.vue';
+
+/**
+ * Renders block of issues
+ */
+
+export default {
+ components: {
+ IssuesBlock,
+ },
+ props: {
+ unresolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ resolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ neutralIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ allIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ type: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isFullReportVisible: false,
+ };
+ },
+ computed: {
+ unresolvedIssuesStatus() {
+ return this.type === 'license' ? 'neutral' : 'failed';
+ },
+ },
+ methods: {
+ openFullReport() {
+ this.isFullReportVisible = true;
+ },
+ },
+};
+</script>
+<template>
+ <div class="report-block-container">
+
+ <issues-block
+ v-if="unresolvedIssues.length"
+ :type="type"
+ :status="unresolvedIssuesStatus"
+ :issues="unresolvedIssues"
+ class="js-mr-code-new-issues"
+ />
+
+ <issues-block
+ v-if="isFullReportVisible"
+ :type="type"
+ :issues="allIssues"
+ class="js-mr-code-all-issues"
+ status="failed"
+ />
+
+ <issues-block
+ v-if="neutralIssues.length"
+ :type="type"
+ :issues="neutralIssues"
+ class="js-mr-code-non-issues"
+ status="neutral"
+ />
+
+ <issues-block
+ v-if="resolvedIssues.length"
+ :type="type"
+ :issues="resolvedIssues"
+ class="js-mr-code-resolved-issues"
+ status="success"
+ />
+
+ <button
+ v-if="allIssues.length && !isFullReportVisible"
+ type="button"
+ class="btn-link btn-blank prepend-left-10 js-expand-full-list break-link"
+ @click="openFullReport"
+ >
+ {{ s__("ciReport|Show complete code vulnerabilities report") }}
+ </button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue b/app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue
new file mode 100644
index 00000000000..4f81cee2a38
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue
@@ -0,0 +1,33 @@
+<script>
+import { mapActions } from 'vuex';
+
+export default {
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ ...mapActions(['openModal']),
+ handleIssueClick() {
+ const { issue, status, openModal } = this;
+ openModal({ issue, status });
+ },
+ },
+};
+</script>
+<template>
+ <button
+ type="button"
+ class="btn-link btn-blank text-left break-link vulnerability-name-button"
+ @click="handleIssueClick()"
+ >
+ {{ issue.title }}
+ </button>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue b/app/assets/javascripts/vue_shared/components/reports/report_issues.vue
new file mode 100644
index 00000000000..ecffb02a3a0
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/report_issues.vue
@@ -0,0 +1,72 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ name: 'ReportIssues',
+ components: {
+ Icon,
+ },
+ props: {
+ issues: {
+ type: Array,
+ required: true,
+ },
+ type: {
+ type: String,
+ required: true,
+ },
+ // failed || success
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ iconName() {
+ if (this.isStatusFailed) {
+ return 'status_failed_borderless';
+ } else if (this.isStatusSuccess) {
+ return 'status_success_borderless';
+ }
+
+ return 'status_created_borderless';
+ },
+ isStatusFailed() {
+ return this.status === 'failed';
+ },
+ isStatusSuccess() {
+ return this.status === 'success';
+ },
+ isStatusNeutral() {
+ return this.status === 'neutral';
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <ul class="report-block-list">
+ <li
+ v-for="(issue, index) in issues"
+ :class="{ 'is-dismissed': issue.isDismissed }"
+ :key="index"
+ class="report-block-list-issue"
+ >
+ <div
+ :class="{
+ failed: isStatusFailed,
+ success: isStatusSuccess,
+ neutral: isStatusNeutral,
+ }"
+ class="report-block-list-icon append-right-5"
+ >
+ <icon
+ :name="iconName"
+ :size="32"
+ />
+ </div>
+
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_link.vue b/app/assets/javascripts/vue_shared/components/reports/report_link.vue
new file mode 100644
index 00000000000..74d68f9f439
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/report_link.vue
@@ -0,0 +1,29 @@
+<script>
+export default {
+ name: 'ReportIssueLink',
+ props: {
+ issue: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="report-block-list-issue-description-link">
+ in
+
+ <a
+ v-if="issue.urlPath"
+ :href="issue.urlPath"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="break-link"
+ >
+ {{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
+ </a>
+ <template v-else>
+ {{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_section.vue b/app/assets/javascripts/vue_shared/components/reports/report_section.vue
new file mode 100644
index 00000000000..d383ed99a0c
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/report_section.vue
@@ -0,0 +1,192 @@
+<script>
+import { __ } from '~/locale';
+import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import IssuesList from './issues_list.vue';
+import Popover from './help_popover.vue';
+
+const LOADING = 'LOADING';
+const ERROR = 'ERROR';
+const SUCCESS = 'SUCCESS';
+
+export default {
+ name: 'ReportSection',
+ components: {
+ IssuesList,
+ StatusIcon,
+ Popover,
+ },
+ props: {
+ alwaysOpen: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ type: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ status: {
+ type: String,
+ required: true,
+ },
+ loadingText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ errorText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ successText: {
+ type: String,
+ required: true,
+ },
+ unresolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ resolvedIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ neutralIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ allIssues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ infoText: {
+ type: [String, Boolean],
+ required: false,
+ default: false,
+ },
+ hasIssues: {
+ type: Boolean,
+ required: true,
+ },
+ popoverOptions: {
+ type: Object,
+ default: () => ({}),
+ required: false,
+ },
+ },
+
+ data() {
+ return {
+ isCollapsed: true,
+ };
+ },
+
+ computed: {
+ collapseText() {
+ return this.isCollapsed ? __('Expand') : __('Collapse');
+ },
+ isLoading() {
+ return this.status === LOADING;
+ },
+ loadingFailed() {
+ return this.status === ERROR;
+ },
+ isSuccess() {
+ return this.status === SUCCESS;
+ },
+ isCollapsible() {
+ return !this.alwaysOpen && this.hasIssues;
+ },
+ isExpanded() {
+ return this.alwaysOpen || !this.isCollapsed;
+ },
+ statusIconName() {
+ if (this.isLoading) {
+ return 'loading';
+ }
+ if (this.loadingFailed || this.unresolvedIssues.length || this.neutralIssues.length) {
+ return 'warning';
+ }
+ return 'success';
+ },
+ headerText() {
+ if (this.isLoading) {
+ return this.loadingText;
+ }
+
+ if (this.isSuccess) {
+ return this.successText;
+ }
+
+ if (this.loadingFailed) {
+ return this.errorText;
+ }
+
+ return '';
+ },
+ hasPopover() {
+ return Object.keys(this.popoverOptions).length > 0;
+ },
+ },
+ methods: {
+ toggleCollapsed() {
+ this.isCollapsed = !this.isCollapsed;
+ },
+ },
+};
+</script>
+<template>
+ <section class="media-section">
+ <div
+ class="media"
+ >
+ <status-icon
+ :status="statusIconName"
+ />
+ <div
+ class="media-body space-children d-flex"
+ >
+ <span
+ class="js-code-text code-text"
+ >
+ {{ headerText }}
+
+ <popover
+ v-if="hasPopover"
+ :options="popoverOptions"
+ class="prepend-left-5"
+ />
+ </span>
+
+ <button
+ v-if="isCollapsible"
+ type="button"
+ class="js-collapse-btn btn bt-default float-right btn-sm"
+ @click="toggleCollapsed"
+ >
+ {{ collapseText }}
+ </button>
+ </div>
+ </div>
+
+ <div
+ v-if="hasIssues"
+ v-show="isExpanded"
+ class="js-report-section-container"
+ >
+ <slot name="body">
+ <issues-list
+ :unresolved-issues="unresolvedIssues"
+ :resolved-issues="resolvedIssues"
+ :all-issues="allIssues"
+ :type="type"
+ />
+ </slot>
+ </div>
+ </section>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/reports/summary_row.vue b/app/assets/javascripts/vue_shared/components/reports/summary_row.vue
new file mode 100644
index 00000000000..997bad960e2
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/reports/summary_row.vue
@@ -0,0 +1,66 @@
+<script>
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Popover from './help_popover.vue';
+
+/**
+ * Renders the summary row for each report
+ *
+ * Used both in MR widget and Pipeline's view for:
+ * - Unit tests reports
+ * - Security reports
+ */
+
+export default {
+ name: 'ReportSummaryRow',
+ components: {
+ CiIcon,
+ LoadingIcon,
+ Popover,
+ },
+ props: {
+ summary: {
+ type: String,
+ required: true,
+ },
+ statusIcon: {
+ type: String,
+ required: true,
+ },
+ popoverOptions: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ iconStatus() {
+ return {
+ group: this.statusIcon,
+ icon: `status_${this.statusIcon}`,
+ };
+ },
+ },
+};
+</script>
+<template>
+ <div class="report-block-list-issue report-block-list-issue-parent">
+ <div class="report-block-list-icon append-right-10 prepend-left-5">
+ <loading-icon
+ v-if="statusIcon === 'loading'"
+ css-class="report-block-list-loading-icon"
+ />
+ <ci-icon
+ v-else
+ :status="iconStatus"
+ />
+ </div>
+
+ <div class="report-block-list-issue-description">
+ <div class="report-block-list-issue-description-text">
+ {{ summary }}
+ </div>
+
+ <popover :options="popoverOptions" />
+ </div>
+ </div>
+</template>
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/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/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 6cfa09b56a7..6c2fdbe0608 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -297,7 +297,6 @@ $performance-bar-height: 35px;
$flash-height: 52px;
$context-header-height: 60px;
$breadcrumb-min-height: 48px;
-$gcp-signup-offer-icon-max-width: 125px;
/*
* Common component specific colors
diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss
index 56beb7718a4..0f22fe21143 100644
--- a/app/assets/stylesheets/pages/clusters.scss
+++ b/app/assets/stylesheets/pages/clusters.scss
@@ -32,49 +32,23 @@
}
.gcp-signup-offer {
- background-color: $blue-50;
- border: 1px solid $blue-300;
- border-radius: $border-radius-default;
+ border-left-color: $blue-500;
- // TODO: To be superceded by cssLab
- &.alert {
- padding: 24px 16px;
-
- &-dismissable {
- padding-right: 32px;
-
- .close {
- top: -8px;
- right: -16px;
- color: $blue-500;
- opacity: 1;
- }
- }
- }
-
- .gcp-logo {
- margin-bottom: $gl-padding;
- text-align: center;
- }
-
- img {
- max-width: $gcp-signup-offer-icon-max-width;
+ svg {
+ fill: $blue-500;
+ vertical-align: middle;
}
- a:not(.btn) {
- color: $gl-link-color;
- font-weight: normal;
- text-decoration: none;
- }
+ .gcp-signup-offer--content {
+ display: flex;
- @include media-breakpoint-up(sm) {
- > div {
- display: flex;
- align-items: center;
+ h4 {
+ font-size: 16px;
+ line-height: 24px;
}
- .gcp-logo {
- margin: 0;
+ .gcp-signup-offer--icon {
+ align-self: flex-start;
}
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 5835b8b8c9b..c8349a4ef79 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -551,6 +551,7 @@
@include media-breakpoint-up(lg) {
.branch-actions {
align-self: center;
+ margin-left: $gl-padding;
}
}
}
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index c01066c688a..9dc0c31be49 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -116,7 +116,12 @@ 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)
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/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 3d72c447b4b..a073bbfad20 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -182,7 +182,7 @@ class MergeRequestDiff < ActiveRecord::Base
end
def diffs(diff_options = nil)
- if without_files? && comparison = diff_refs.compare_in(project)
+ if without_files? && comparison = diff_refs&.compare_in(project)
# It should fetch the repository when diffs are cleaned by the system.
# We don't keep these for storage overload purposes.
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/37639
diff --git a/app/models/note.rb b/app/models/note.rb
index abc40d9016e..fe3507adcb3 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -202,7 +202,7 @@ class Note < ActiveRecord::Base
end
def hook_attrs
- attributes
+ Gitlab::HookData::NoteBuilder.new(self).build
end
def for_commit?
diff --git a/app/models/project.rb b/app/models/project.rb
index e29bca365a4..ae0a13b17dc 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2171,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_wiki.rb b/app/models/project_wiki.rb
index 9ae2fb0013a..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.
@@ -98,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
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 4b49edb01a5..55243136140 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -60,7 +60,7 @@ class WikiPage
attr_accessor :attributes
def hook_attrs
- attributes
+ Gitlab::HookData::WikiPageBuilder.new(self).build
end
def initialize(wiki, page = nil, persisted = false)
diff --git a/app/services/application_settings/base_service.rb b/app/services/application_settings/base_service.rb
index 2bcc7d7c08b..ebe067536ca 100644
--- a/app/services/application_settings/base_service.rb
+++ b/app/services/application_settings/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettings
class BaseService < ::BaseService
def initialize(application_setting, user, params = {})
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 7bcb8f49d0d..19cf34e2ac4 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApplicationSettings
class UpdateService < ApplicationSettings::BaseService
attr_reader :params, :application_setting
diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb
index 94a434b95dd..7db90c0b3c6 100644
--- a/app/services/applications/create_service.rb
+++ b/app/services/applications/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Applications
class CreateService
def initialize(current_user, params)
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index f28cddb2af3..81857d0cb4c 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Auth
class ContainerRegistryAuthenticationService < BaseService
AUDIENCE = 'container_registry'.freeze
diff --git a/app/services/badges/base_service.rb b/app/services/badges/base_service.rb
index 4f87426bd38..45fc9ac4373 100644
--- a/app/services/badges/base_service.rb
+++ b/app/services/badges/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class BaseService
protected
diff --git a/app/services/badges/build_service.rb b/app/services/badges/build_service.rb
index 6267e571838..e5ede1586b6 100644
--- a/app/services/badges/build_service.rb
+++ b/app/services/badges/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class BuildService < Badges::BaseService
# returns the created badge
diff --git a/app/services/badges/create_service.rb b/app/services/badges/create_service.rb
index aafb87f7dcd..4a55a00daeb 100644
--- a/app/services/badges/create_service.rb
+++ b/app/services/badges/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class CreateService < Badges::BaseService
# returns the created badge
diff --git a/app/services/badges/update_service.rb b/app/services/badges/update_service.rb
index 495a4a2c99d..a653b7903dd 100644
--- a/app/services/badges/update_service.rb
+++ b/app/services/badges/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Badges
class UpdateService < Badges::BaseService
# returns the updated badge
diff --git a/app/services/boards/base_service.rb b/app/services/boards/base_service.rb
index 72822ffffa1..205db47888e 100644
--- a/app/services/boards/base_service.rb
+++ b/app/services/boards/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class BaseService < ::BaseService
# Parent can either a group or a project
diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb
index bd0bb387662..4caf5ffa3cb 100644
--- a/app/services/boards/create_service.rb
+++ b/app/services/boards/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class CreateService < Boards::BaseService
def execute
diff --git a/app/services/boards/issues/create_service.rb b/app/services/boards/issues/create_service.rb
index 3025029755c..bd045e18b8d 100644
--- a/app/services/boards/issues/create_service.rb
+++ b/app/services/boards/issues/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class CreateService < Boards::BaseService
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index b1dbe73cdf7..50c11be0d15 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class ListService < Boards::BaseService
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index ee3112c7571..6fd8a23b2a1 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Issues
class MoveService < Boards::BaseService
diff --git a/app/services/boards/list_service.rb b/app/services/boards/list_service.rb
index 9269b8d2620..edd1cc7c2e1 100644
--- a/app/services/boards/list_service.rb
+++ b/app/services/boards/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
class ListService < Boards::BaseService
def execute
diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb
index 6fd9885d4f3..48d2d5abaec 100644
--- a/app/services/boards/lists/create_service.rb
+++ b/app/services/boards/lists/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class CreateService < Boards::BaseService
diff --git a/app/services/boards/lists/destroy_service.rb b/app/services/boards/lists/destroy_service.rb
index d75c5fd3dc6..e12d4f46e19 100644
--- a/app/services/boards/lists/destroy_service.rb
+++ b/app/services/boards/lists/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class DestroyService < Boards::BaseService
diff --git a/app/services/boards/lists/generate_service.rb b/app/services/boards/lists/generate_service.rb
index 05d4ab5dbcc..4fbf1026019 100644
--- a/app/services/boards/lists/generate_service.rb
+++ b/app/services/boards/lists/generate_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class GenerateService < Boards::BaseService
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index e57c95294af..e10eb52e041 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class ListService < Boards::BaseService
diff --git a/app/services/boards/lists/move_service.rb b/app/services/boards/lists/move_service.rb
index 7d0730e8332..27a36051662 100644
--- a/app/services/boards/lists/move_service.rb
+++ b/app/services/boards/lists/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Boards
module Lists
class MoveService < Boards::BaseService
diff --git a/app/services/chat_names/authorize_user_service.rb b/app/services/chat_names/authorize_user_service.rb
index 7256466c9e8..78b53cb3637 100644
--- a/app/services/chat_names/authorize_user_service.rb
+++ b/app/services/chat_names/authorize_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatNames
class AuthorizeUserService
include Gitlab::Routing
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index d458b814183..854b191c45c 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatNames
class FindUserService
def initialize(service, params)
diff --git a/app/services/ci/create_pipeline_schedule_service.rb b/app/services/ci/create_pipeline_schedule_service.rb
index cd40deb6187..0d5f50c26a1 100644
--- a/app/services/ci/create_pipeline_schedule_service.rb
+++ b/app/services/ci/create_pipeline_schedule_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class CreatePipelineScheduleService < BaseService
def execute
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 17a53b6a8fd..85df8bcff8c 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline
diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb
index b8c7be2d350..3d0e39d1b9f 100644
--- a/app/services/ci/ensure_stage_service.rb
+++ b/app/services/ci/ensure_stage_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
##
# We call this service everytime we persist a CI/CD job.
diff --git a/app/services/ci/extract_sections_from_build_trace_service.rb b/app/services/ci/extract_sections_from_build_trace_service.rb
index 75f9e0f897d..693f6d55be3 100644
--- a/app/services/ci/extract_sections_from_build_trace_service.rb
+++ b/app/services/ci/extract_sections_from_build_trace_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ExtractSectionsFromBuildTraceService < BaseService
def execute(build)
diff --git a/app/services/ci/fetch_kubernetes_token_service.rb b/app/services/ci/fetch_kubernetes_token_service.rb
index bca883ec0a0..15eda56cac6 100644
--- a/app/services/ci/fetch_kubernetes_token_service.rb
+++ b/app/services/ci/fetch_kubernetes_token_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# TODO:
# Almost components in this class were copied from app/models/project_services/kubernetes_service.rb
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 85533a1cbdb..f54574b026b 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PipelineTriggerService < BaseService
include Gitlab::Utils::StrongMemoize
diff --git a/app/services/ci/play_build_service.rb b/app/services/ci/play_build_service.rb
index e24f48c2d16..eb0b070657d 100644
--- a/app/services/ci/play_build_service.rb
+++ b/app/services/ci/play_build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class PlayBuildService < ::BaseService
def execute(build)
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index 55af193d717..cda9bbff3b4 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class ProcessPipelineService < BaseService
attr_reader :pipeline
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 6eb1c4f52de..f7ccec3a700 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
# This class responsible for assigning
# proper pending build to runner on runner API request
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 6128b2a8fbb..6ceb59e4780 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RetryBuildService < ::BaseService
CLONE_ACCESSORS = %i[pipeline project ref tag options commands name
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
index c5a43869990..42a13367a99 100644
--- a/app/services/ci/retry_pipeline_service.rb
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class RetryPipelineService < ::BaseService
include Gitlab::OptimisticLocking
diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb
index 439746e82bd..973ae5ce5aa 100644
--- a/app/services/ci/stop_environments_service.rb
+++ b/app/services/ci/stop_environments_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class StopEnvironmentsService < BaseService
attr_reader :ref
diff --git a/app/services/ci/update_build_queue_service.rb b/app/services/ci/update_build_queue_service.rb
index 41b1c144c3e..9c589d910eb 100644
--- a/app/services/ci/update_build_queue_service.rb
+++ b/app/services/ci/update_build_queue_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class UpdateBuildQueueService
def execute(build)
diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb
index 450ee7da1c9..e4117a51fe6 100644
--- a/app/services/ci/update_runner_service.rb
+++ b/app/services/ci/update_runner_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class UpdateRunnerService
attr_reader :runner
diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb
index cba1b920f7c..270a8eb24f4 100644
--- a/app/services/clusters/applications/base_helm_service.rb
+++ b/app/services/clusters/applications/base_helm_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class BaseHelmService
diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb
index e572b1e5d99..f32e73e8b1c 100644
--- a/app/services/clusters/applications/check_ingress_ip_address_service.rb
+++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class CheckIngressIpAddressService < BaseHelmService
diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb
index 90393e951a4..4640c5a2d4b 100644
--- a/app/services/clusters/applications/check_installation_progress_service.rb
+++ b/app/services/clusters/applications/check_installation_progress_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class CheckInstallationProgressService < BaseHelmService
diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb
index 7ec3a9baa6e..7e3c0e77a83 100644
--- a/app/services/clusters/applications/install_service.rb
+++ b/app/services/clusters/applications/install_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class InstallService < BaseHelmService
diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb
index 9c5461e85e1..4ead4f619c8 100644
--- a/app/services/clusters/applications/schedule_installation_service.rb
+++ b/app/services/clusters/applications/schedule_installation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Applications
class ScheduleInstallationService < ::BaseService
diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb
index 418888e3293..e3e0cfa462c 100644
--- a/app/services/clusters/create_service.rb
+++ b/app/services/clusters/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class CreateService < BaseService
attr_reader :access_token
diff --git a/app/services/clusters/gcp/fetch_operation_service.rb b/app/services/clusters/gcp/fetch_operation_service.rb
index a4cd3ca5c11..02c96a1e286 100644
--- a/app/services/clusters/gcp/fetch_operation_service.rb
+++ b/app/services/clusters/gcp/fetch_operation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class FetchOperationService
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index 84944e95542..264419501dc 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class FinalizeCreationService
diff --git a/app/services/clusters/gcp/provision_service.rb b/app/services/clusters/gcp/provision_service.rb
index 8beea5a8cfb..ab1bf9c64f6 100644
--- a/app/services/clusters/gcp/provision_service.rb
+++ b/app/services/clusters/gcp/provision_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class ProvisionService
diff --git a/app/services/clusters/gcp/verify_provision_status_service.rb b/app/services/clusters/gcp/verify_provision_status_service.rb
index 7cc4324677e..b24246f5c4b 100644
--- a/app/services/clusters/gcp/verify_provision_status_service.rb
+++ b/app/services/clusters/gcp/verify_provision_status_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
module Gcp
class VerifyProvisionStatusService
diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb
index 989218e32a2..98fdeec4fb1 100644
--- a/app/services/clusters/update_service.rb
+++ b/app/services/clusters/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Clusters
class UpdateService < BaseService
def execute(cluster)
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index 1ce6ab36cbf..2fbd442fc2e 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class ChangeService < Commits::CreateService
def initialize(*args)
diff --git a/app/services/commits/cherry_pick_service.rb b/app/services/commits/cherry_pick_service.rb
index 320e229560d..4c5b15b2f95 100644
--- a/app/services/commits/cherry_pick_service.rb
+++ b/app/services/commits/cherry_pick_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class CherryPickService < ChangeService
def create_commit!
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index 4d0578becbe..3ce9acc833c 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class CreateService < ::BaseService
ValidationError = Class.new(StandardError)
diff --git a/app/services/commits/revert_service.rb b/app/services/commits/revert_service.rb
index dc27399e047..dddb8b24eac 100644
--- a/app/services/commits/revert_service.rb
+++ b/app/services/commits/revert_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Commits
class RevertService < ChangeService
def create_commit!
diff --git a/app/services/concerns/exclusive_lease_guard.rb b/app/services/concerns/exclusive_lease_guard.rb
index f45436370c1..f102e00d150 100644
--- a/app/services/concerns/exclusive_lease_guard.rb
+++ b/app/services/concerns/exclusive_lease_guard.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
#
# Concern that helps with getting an exclusive lease for running a block
# of code.
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 455f761ca9b..1563ed965df 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
module ResolveDiscussions
include Gitlab::Utils::StrongMemoize
diff --git a/app/services/concerns/update_visibility_level.rb b/app/services/concerns/update_visibility_level.rb
index 536fcc6acce..b7a161f5089 100644
--- a/app/services/concerns/update_visibility_level.rb
+++ b/app/services/concerns/update_visibility_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module UpdateVisibilityLevel
def valid_visibility_level_change?(target, new_visibility)
# check that user is allowed to set specified visibility_level
diff --git a/app/services/concerns/users/new_user_notifier.rb b/app/services/concerns/users/new_user_notifier.rb
index 231693ce7a9..11547e4a5b6 100644
--- a/app/services/concerns/users/new_user_notifier.rb
+++ b/app/services/concerns/users/new_user_notifier.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
module NewUserNotifier
def notify_new_user(user, reset_token)
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index bf60b96938d..5b408bd96c7 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Users
module ParticipableService
extend ActiveSupport::Concern
diff --git a/app/services/deploy_keys/create_service.rb b/app/services/deploy_keys/create_service.rb
index 16de3d08df2..a25e73666f8 100644
--- a/app/services/deploy_keys/create_service.rb
+++ b/app/services/deploy_keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployKeys
class CreateService < Keys::BaseService
def execute
diff --git a/app/services/deploy_tokens/create_service.rb b/app/services/deploy_tokens/create_service.rb
index 52f545947af..dc0122002e9 100644
--- a/app/services/deploy_tokens/create_service.rb
+++ b/app/services/deploy_tokens/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeployTokens
class CreateService < BaseService
def execute
diff --git a/app/services/discussions/base_service.rb b/app/services/discussions/base_service.rb
index e4dfe6e71bb..86b8310f0a6 100644
--- a/app/services/discussions/base_service.rb
+++ b/app/services/discussions/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class BaseService < ::BaseService
end
diff --git a/app/services/discussions/resolve_service.rb b/app/services/discussions/resolve_service.rb
index 0437195f588..816cd45b07a 100644
--- a/app/services/discussions/resolve_service.rb
+++ b/app/services/discussions/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class ResolveService < Discussions::BaseService
def execute(one_or_more_discussions)
diff --git a/app/services/discussions/update_diff_position_service.rb b/app/services/discussions/update_diff_position_service.rb
index 746f209e20f..c61437fb2e3 100644
--- a/app/services/discussions/update_diff_position_service.rb
+++ b/app/services/discussions/update_diff_position_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Discussions
class UpdateDiffPositionService < BaseService
def execute(discussion)
diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb
index 5bbceeb3b3f..ba7b689a9af 100644
--- a/app/services/emails/base_service.rb
+++ b/app/services/emails/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class BaseService
def initialize(current_user, params = {})
diff --git a/app/services/emails/confirm_service.rb b/app/services/emails/confirm_service.rb
index b5301bf2b82..38204e011dd 100644
--- a/app/services/emails/confirm_service.rb
+++ b/app/services/emails/confirm_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class ConfirmService < ::Emails::BaseService
def execute(email)
diff --git a/app/services/emails/create_service.rb b/app/services/emails/create_service.rb
index 94a841af7c3..acf575e24e5 100644
--- a/app/services/emails/create_service.rb
+++ b/app/services/emails/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class CreateService < ::Emails::BaseService
def execute(extra_params = {})
diff --git a/app/services/emails/destroy_service.rb b/app/services/emails/destroy_service.rb
index 1ed131fe326..9ca1a03e172 100644
--- a/app/services/emails/destroy_service.rb
+++ b/app/services/emails/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Emails
class DestroyService < ::Emails::BaseService
def execute(email)
diff --git a/app/services/events/render_service.rb b/app/services/events/render_service.rb
index bb72d7685dd..50429683902 100644
--- a/app/services/events/render_service.rb
+++ b/app/services/events/render_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Events
class RenderService < BaseRenderer
def execute(events, atom_request: false)
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 8d4b9f14780..025f093a428 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class BaseService < Commits::CreateService
FileChangedError = Class.new(StandardError)
diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb
index 8ecac6115bd..362b80071ba 100644
--- a/app/services/files/create_dir_service.rb
+++ b/app/services/files/create_dir_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class CreateDirService < Files::BaseService
def create_commit!
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index a954564946b..fd5442a6c28 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class CreateService < Files::BaseService
def create_commit!
diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb
index 32a57484d4e..0ec1f79d396 100644
--- a/app/services/files/delete_service.rb
+++ b/app/services/files/delete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class DeleteService < Files::BaseService
def create_commit!
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 13a1dee4173..08088f8c592 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class MultiService < Files::BaseService
UPDATE_FILE_ACTIONS = %w(update move delete).freeze
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 1902d1cea72..2b3e96e6c53 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Files
class UpdateService < Files::BaseService
def create_commit!
diff --git a/app/services/gpg_keys/create_service.rb b/app/services/gpg_keys/create_service.rb
index e822a89c4d3..e41444b2a82 100644
--- a/app/services/gpg_keys/create_service.rb
+++ b/app/services/gpg_keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GpgKeys
class CreateService < Keys::BaseService
def execute
diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb
index a8fa098246a..8c8acce5ca5 100644
--- a/app/services/groups/base_service.rb
+++ b/app/services/groups/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class BaseService < ::BaseService
attr_accessor :group, :current_user, :params
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 70e50aa0f12..24d8400c625 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class CreateService < Groups::BaseService
def initialize(user, params = {})
diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb
index 58e88688dfa..c4554ce45fb 100644
--- a/app/services/groups/destroy_service.rb
+++ b/app/services/groups/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class DestroyService < Groups::BaseService
def async_execute
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index c2dfbac5414..50d34d8cb91 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class NestedCreateService < Groups::BaseService
attr_reader :group_path, :visibility_level
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index e591c820cff..ea7576077f3 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class TransferService < Groups::BaseService
ERROR_MESSAGES = {
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index 08e3efb96e3..436a6b18cb1 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class UpdateService < Groups::BaseService
include UpdateVisibilityLevel
diff --git a/app/services/issuable/bulk_update_service.rb b/app/services/issuable/bulk_update_service.rb
index 5d42a89fced..051d5ba881d 100644
--- a/app/services/issuable/bulk_update_service.rb
+++ b/app/services/issuable/bulk_update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class BulkUpdateService < IssuableBaseService
def execute(type)
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index 3da21bd8b8f..028b350ca07 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class CommonSystemNotesService < ::BaseService
attr_reader :issuable
diff --git a/app/services/issuable/destroy_service.rb b/app/services/issuable/destroy_service.rb
index 0b1a33518c6..4c64655a622 100644
--- a/app/services/issuable/destroy_service.rb
+++ b/app/services/issuable/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issuable
class DestroyService < IssuableBaseService
def execute(issuable)
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index cbfef175af0..25389a946bb 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class BaseService < ::IssuableBaseService
def hook_data(issue, action, old_associations: {})
diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb
index 3a4f7b159f1..52b45f1b2ce 100644
--- a/app/services/issues/build_service.rb
+++ b/app/services/issues/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class BuildService < Issues::BaseService
include ResolveDiscussions
@@ -44,14 +46,14 @@ module Issues
other_note_count = discussion.notes.size - 1
- discussion_info = "- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "
- discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
+ discussion_info = ["- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "]
+ discussion_info << "(+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note_to_resolve.note).call
spaces = ' ' * 4
quote = note_without_block_quotes.lines.map { |line| "#{spaces}> #{line}" }.join
- [discussion_info, quote].join("\n\n")
+ [discussion_info.join(' '), quote].join("\n\n")
end
def issue_params
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index 4a99367c575..e5cc12e6082 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class CloseService < Issues::BaseService
# Closes the supplied issue if the current user is able to do so.
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 0307634c0b6..5793a15e1bc 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class CreateService < Issues::BaseService
include SpamCheckService
diff --git a/app/services/issues/duplicate_service.rb b/app/services/issues/duplicate_service.rb
index 5c0854e664d..9b22f5e7914 100644
--- a/app/services/issues/duplicate_service.rb
+++ b/app/services/issues/duplicate_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class DuplicateService < Issues::BaseService
def execute(duplicate_issue, canonical_issue)
diff --git a/app/services/issues/fetch_referenced_merge_requests_service.rb b/app/services/issues/fetch_referenced_merge_requests_service.rb
index 39c8ded9df4..5e84f3c81c9 100644
--- a/app/services/issues/fetch_referenced_merge_requests_service.rb
+++ b/app/services/issues/fetch_referenced_merge_requests_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class FetchReferencedMergeRequestsService < Issues::BaseService
def execute(issue)
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 6e5c29a5c40..841bce9949e 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class MoveService < Issues::BaseService
MoveError = Class.new(StandardError)
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 02224f3357a..3bd53f9ccdc 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class ReopenService < Issues::BaseService
def execute(issue)
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 1000e1842b6..c02dddf67b2 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Issues
class UpdateService < Issues::BaseService
include SpamCheckService
diff --git a/app/services/keys/base_service.rb b/app/services/keys/base_service.rb
index df8e82f5f60..113e22b01ce 100644
--- a/app/services/keys/base_service.rb
+++ b/app/services/keys/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class BaseService
attr_accessor :user, :params
diff --git a/app/services/keys/create_service.rb b/app/services/keys/create_service.rb
index e2e5a6c46c5..d9fa69a88d7 100644
--- a/app/services/keys/create_service.rb
+++ b/app/services/keys/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class CreateService < ::Keys::BaseService
def execute
diff --git a/app/services/keys/destroy_service.rb b/app/services/keys/destroy_service.rb
index 785cfa3a1d8..e2ae4047941 100644
--- a/app/services/keys/destroy_service.rb
+++ b/app/services/keys/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class DestroyService < ::Keys::BaseService
def execute(key)
diff --git a/app/services/keys/last_used_service.rb b/app/services/keys/last_used_service.rb
index dbd79f7da55..daef544bac0 100644
--- a/app/services/keys/last_used_service.rb
+++ b/app/services/keys/last_used_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Keys
class LastUsedService
TIMEOUT = 1.day.to_i
diff --git a/app/services/labels/base_service.rb b/app/services/labels/base_service.rb
index 91d72a57b4e..ead7f2ea607 100644
--- a/app/services/labels/base_service.rb
+++ b/app/services/labels/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class BaseService < ::BaseService
COLOR_NAME_TO_HEX = {
diff --git a/app/services/labels/create_service.rb b/app/services/labels/create_service.rb
index 6c399c92377..fe34be41ac1 100644
--- a/app/services/labels/create_service.rb
+++ b/app/services/labels/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class CreateService < Labels::BaseService
def initialize(params = {})
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index a72da3c637f..e4486764a4d 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class FindOrCreateService
def initialize(current_user, parent, params = {})
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index 74a85e5c9f0..c0463052821 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class PromoteService < BaseService
BATCH_SIZE = 1000
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 9b7486cf53b..1bd8d9fc325 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Labels::TransferService class
#
# User for recreate the missing group labels at project level
diff --git a/app/services/labels/update_service.rb b/app/services/labels/update_service.rb
index 28dcabf9541..c3a720a1c66 100644
--- a/app/services/labels/update_service.rb
+++ b/app/services/labels/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Labels
class UpdateService < Labels::BaseService
def initialize(params = {})
diff --git a/app/services/lfs/file_transformer.rb b/app/services/lfs/file_transformer.rb
index 69281ee3137..c8eccb8e6cd 100644
--- a/app/services/lfs/file_transformer.rb
+++ b/app/services/lfs/file_transformer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
# Usage: Calling `new_file` check to see if a file should be in LFS and
# return a transformed result with `content` and `encoding` to commit.
diff --git a/app/services/lfs/lock_file_service.rb b/app/services/lfs/lock_file_service.rb
index bbe10f84ef4..78434909d68 100644
--- a/app/services/lfs/lock_file_service.rb
+++ b/app/services/lfs/lock_file_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
class LockFileService < BaseService
def execute
diff --git a/app/services/lfs/locks_finder_service.rb b/app/services/lfs/locks_finder_service.rb
index 13c6cc6f81c..d52cf0e3cc4 100644
--- a/app/services/lfs/locks_finder_service.rb
+++ b/app/services/lfs/locks_finder_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
class LocksFinderService < BaseService
def execute
diff --git a/app/services/lfs/unlock_file_service.rb b/app/services/lfs/unlock_file_service.rb
index 7e3edf21d54..4d1443bf772 100644
--- a/app/services/lfs/unlock_file_service.rb
+++ b/app/services/lfs/unlock_file_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Lfs
class UnlockFileService < BaseService
def execute
diff --git a/app/services/mattermost/create_team_service.rb b/app/services/mattermost/create_team_service.rb
index e3206810f3a..afcd6439a14 100644
--- a/app/services/mattermost/create_team_service.rb
+++ b/app/services/mattermost/create_team_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Mattermost
class CreateTeamService < ::BaseService
def initialize(group, current_user)
diff --git a/app/services/members/approve_access_request_service.rb b/app/services/members/approve_access_request_service.rb
index 6be08b590bc..52b890d1821 100644
--- a/app/services/members/approve_access_request_service.rb
+++ b/app/services/members/approve_access_request_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class ApproveAccessRequestService < Members::BaseService
def execute(access_requester, skip_authorization: false, skip_log_audit_event: false)
diff --git a/app/services/members/base_service.rb b/app/services/members/base_service.rb
index 74556fb20cf..8248f1441d7 100644
--- a/app/services/members/base_service.rb
+++ b/app/services/members/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class BaseService < ::BaseService
# current_user - The user that performs the action
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
index bc6a9405aac..714b8586737 100644
--- a/app/services/members/create_service.rb
+++ b/app/services/members/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class CreateService < Members::BaseService
DEFAULT_LIMIT = 100
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index 5b51e1982f1..aca0ba66646 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class DestroyService < Members::BaseService
def execute(member, skip_authorization: false)
diff --git a/app/services/members/request_access_service.rb b/app/services/members/request_access_service.rb
index 24293b30005..b9b0550e290 100644
--- a/app/services/members/request_access_service.rb
+++ b/app/services/members/request_access_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class RequestAccessService < Members::BaseService
def execute(source)
diff --git a/app/services/members/update_service.rb b/app/services/members/update_service.rb
index cb19cf01dd7..1f5618dae53 100644
--- a/app/services/members/update_service.rb
+++ b/app/services/members/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Members
class UpdateService < Members::BaseService
# returns the updated member
diff --git a/app/services/merge_requests/add_todo_when_build_fails_service.rb b/app/services/merge_requests/add_todo_when_build_fails_service.rb
index 6805b2f7d1c..79c43b8e7d5 100644
--- a/app/services/merge_requests/add_todo_when_build_fails_service.rb
+++ b/app/services/merge_requests/add_todo_when_build_fails_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class AddTodoWhenBuildFailsService < MergeRequests::BaseService
# Adds a todo to the parent merge_request when a CI build fails
diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb
index 8c6c4841020..e9107b9998e 100644
--- a/app/services/merge_requests/assign_issues_service.rb
+++ b/app/services/merge_requests/assign_issues_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class AssignIssuesService < BaseService
def assignable_issues
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 4c420b38258..e6dd0e12a3a 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class BaseService < ::IssuableBaseService
def create_note(merge_request, state = merge_request.state)
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index a98bbdf74dd..bc988eb2a26 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class BuildService < MergeRequests::BaseService
include Gitlab::Utils::StrongMemoize
@@ -140,7 +142,8 @@ module MergeRequests
closes_issue = "Closes #{issue.to_reference}"
if description.present?
- merge_request.description += closes_issue.prepend("\n\n")
+ descr_parts = [merge_request.description, closes_issue]
+ merge_request.description = descr_parts.join("\n\n")
else
merge_request.description = closes_issue
end
@@ -164,9 +167,11 @@ module MergeRequests
return if merge_request.title.present?
if issue_iid.present?
- merge_request.title = "Resolve #{issue.to_reference}"
+ title_parts = ["Resolve #{issue.to_reference}"]
branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize
- merge_request.title += " \"#{branch_title}\"" if branch_title.present?
+
+ title_parts << "\"#{branch_title}\"" if branch_title.present?
+ merge_request.title = title_parts.join(' ')
end
end
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index db701c1145d..04527bb9713 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class CloseService < MergeRequests::BaseService
def execute(merge_request, commit = nil)
diff --git a/app/services/merge_requests/conflicts/base_service.rb b/app/services/merge_requests/conflicts/base_service.rb
index b50875347d9..402f6c4e4c0 100644
--- a/app/services/merge_requests/conflicts/base_service.rb
+++ b/app/services/merge_requests/conflicts/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
module Conflicts
class BaseService
diff --git a/app/services/merge_requests/conflicts/list_service.rb b/app/services/merge_requests/conflicts/list_service.rb
index 72cbc49adb2..c6b3a6a1a69 100644
--- a/app/services/merge_requests/conflicts/list_service.rb
+++ b/app/services/merge_requests/conflicts/list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
module Conflicts
class ListService < MergeRequests::Conflicts::BaseService
diff --git a/app/services/merge_requests/conflicts/resolve_service.rb b/app/services/merge_requests/conflicts/resolve_service.rb
index 27cafd2d7d9..b9f734310be 100644
--- a/app/services/merge_requests/conflicts/resolve_service.rb
+++ b/app/services/merge_requests/conflicts/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
module Conflicts
class ResolveService < MergeRequests::Conflicts::BaseService
diff --git a/app/services/merge_requests/create_from_issue_service.rb b/app/services/merge_requests/create_from_issue_service.rb
index 3407b312700..fd91dc4acd0 100644
--- a/app/services/merge_requests/create_from_issue_service.rb
+++ b/app/services/merge_requests/create_from_issue_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class CreateFromIssueService < MergeRequests::CreateService
def initialize(project, user, params)
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index fe1ac70781e..c36a2ecbfe3 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class CreateService < MergeRequests::BaseService
def execute
diff --git a/app/services/merge_requests/delete_non_latest_diffs_service.rb b/app/services/merge_requests/delete_non_latest_diffs_service.rb
index 40079b21189..2a8ea316921 100644
--- a/app/services/merge_requests/delete_non_latest_diffs_service.rb
+++ b/app/services/merge_requests/delete_non_latest_diffs_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class DeleteNonLatestDiffsService
BATCH_SIZE = 10
diff --git a/app/services/merge_requests/ff_merge_service.rb b/app/services/merge_requests/ff_merge_service.rb
index bffc09c34f0..479e0fe6699 100644
--- a/app/services/merge_requests/ff_merge_service.rb
+++ b/app/services/merge_requests/ff_merge_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
# MergeService class
#
diff --git a/app/services/merge_requests/get_urls_service.rb b/app/services/merge_requests/get_urls_service.rb
index 668a1741736..7c88c9abb41 100644
--- a/app/services/merge_requests/get_urls_service.rb
+++ b/app/services/merge_requests/get_urls_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class GetUrlsService < BaseService
attr_reader :project
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 3d587f97906..fb44f809c41 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
# MergeService class
#
diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
index 9a4e6eb2e88..973e5b64e88 100644
--- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class MergeWhenPipelineSucceedsService < MergeRequests::BaseService
# Marks the passed `merge_request` to be merged when the pipeline succeeds or
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index 7606d68ff29..3d2aea4e9b6 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
# PostMergeService class
#
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index c741e913860..31b3ebf311e 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class RebaseService < MergeRequests::WorkingCopyBaseService
REBASE_ERROR = 'Rebase failed. Please rebase locally'.freeze
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 0127d781686..48da796505f 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class RefreshService < MergeRequests::BaseService
def execute(oldrev, newrev, ref)
diff --git a/app/services/merge_requests/reload_diffs_service.rb b/app/services/merge_requests/reload_diffs_service.rb
index 2ec7b403903..8d85dc9eb5f 100644
--- a/app/services/merge_requests/reload_diffs_service.rb
+++ b/app/services/merge_requests/reload_diffs_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class ReloadDiffsService
def initialize(merge_request, current_user)
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index 8f1c95ac1b7..f2fc13ad028 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class ReopenService < MergeRequests::BaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/resolved_discussion_notification_service.rb b/app/services/merge_requests/resolved_discussion_notification_service.rb
index 66a0cbc81d4..03ded1512f9 100644
--- a/app/services/merge_requests/resolved_discussion_notification_service.rb
+++ b/app/services/merge_requests/resolved_discussion_notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class ResolvedDiscussionNotificationService < MergeRequests::BaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/squash_service.rb b/app/services/merge_requests/squash_service.rb
index a40fb2786bd..a439a380255 100644
--- a/app/services/merge_requests/squash_service.rb
+++ b/app/services/merge_requests/squash_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class SquashService < MergeRequests::WorkingCopyBaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 7350725e223..b112edbce7f 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class UpdateService < MergeRequests::BaseService
def execute(merge_request)
diff --git a/app/services/merge_requests/working_copy_base_service.rb b/app/services/merge_requests/working_copy_base_service.rb
index 186e05bf966..2d2be1f4c25 100644
--- a/app/services/merge_requests/working_copy_base_service.rb
+++ b/app/services/merge_requests/working_copy_base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequests
class WorkingCopyBaseService < MergeRequests::BaseService
attr_reader :merge_request
diff --git a/app/services/milestones/base_service.rb b/app/services/milestones/base_service.rb
index cce0863d611..f30194c0bfe 100644
--- a/app/services/milestones/base_service.rb
+++ b/app/services/milestones/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class BaseService < ::BaseService
# Parent can either a group or a project
diff --git a/app/services/milestones/close_service.rb b/app/services/milestones/close_service.rb
index 5b06c4b601d..a252f5c144e 100644
--- a/app/services/milestones/close_service.rb
+++ b/app/services/milestones/close_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class CloseService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/milestones/create_service.rb b/app/services/milestones/create_service.rb
index ed2e833d833..6c3edd2e147 100644
--- a/app/services/milestones/create_service.rb
+++ b/app/services/milestones/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class CreateService < Milestones::BaseService
def execute
diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb
index b18651476a8..15c04525075 100644
--- a/app/services/milestones/destroy_service.rb
+++ b/app/services/milestones/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class DestroyService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/milestones/promote_service.rb b/app/services/milestones/promote_service.rb
index 2187f26d1ed..37aa6d3a9bc 100644
--- a/app/services/milestones/promote_service.rb
+++ b/app/services/milestones/promote_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class PromoteService < Milestones::BaseService
PromoteMilestoneError = Class.new(StandardError)
diff --git a/app/services/milestones/reopen_service.rb b/app/services/milestones/reopen_service.rb
index 3efb33157c5..125a3ec1367 100644
--- a/app/services/milestones/reopen_service.rb
+++ b/app/services/milestones/reopen_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class ReopenService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/milestones/update_service.rb b/app/services/milestones/update_service.rb
index 74edbf9b41d..81b20943bab 100644
--- a/app/services/milestones/update_service.rb
+++ b/app/services/milestones/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Milestones
class UpdateService < Milestones::BaseService
def execute(milestone)
diff --git a/app/services/notes/build_service.rb b/app/services/notes/build_service.rb
index 77e7b8a5ea7..df5fe65de3c 100644
--- a/app/services/notes/build_service.rb
+++ b/app/services/notes/build_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class BuildService < ::BaseService
def execute
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 9ea28733f5f..049e6c5a871 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class CreateService < ::BaseService
def execute
diff --git a/app/services/notes/destroy_service.rb b/app/services/notes/destroy_service.rb
index fb78420d324..64e9accd97f 100644
--- a/app/services/notes/destroy_service.rb
+++ b/app/services/notes/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class DestroyService < BaseService
def execute(note)
diff --git a/app/services/notes/post_process_service.rb b/app/services/notes/post_process_service.rb
index 199b8028dbc..48722cc2a79 100644
--- a/app/services/notes/post_process_service.rb
+++ b/app/services/notes/post_process_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class PostProcessService
attr_accessor :note
diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb
index 0a33d5f3f3d..7280449bb1c 100644
--- a/app/services/notes/quick_actions_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class QuickActionsService < BaseService
UPDATE_SERVICES = {
diff --git a/app/services/notes/render_service.rb b/app/services/notes/render_service.rb
index efc9d6da2aa..0e1a55ae2ff 100644
--- a/app/services/notes/render_service.rb
+++ b/app/services/notes/render_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class RenderService < BaseRenderer
# Renders a collection of Note instances.
diff --git a/app/services/notes/resolve_service.rb b/app/services/notes/resolve_service.rb
index 0db8ee809a9..cf24795f050 100644
--- a/app/services/notes/resolve_service.rb
+++ b/app/services/notes/resolve_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class ResolveService < ::BaseService
def execute(note)
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index e16ef398184..35db409eb27 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Notes
class UpdateService < BaseService
def execute(note)
diff --git a/app/services/projects/after_import_service.rb b/app/services/projects/after_import_service.rb
index 3047268b2d1..bbdde4408d2 100644
--- a/app/services/projects/after_import_service.rb
+++ b/app/services/projects/after_import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class AfterImportService
RESERVED_REF_PREFIXES = Repository::RESERVED_REFS_NAMES.map { |n| File.join('refs', n, '/') }
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 9d0eaaf3152..10eb2cea4a2 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class AutocompleteService < BaseService
def issues
diff --git a/app/services/projects/base_move_relations_service.rb b/app/services/projects/base_move_relations_service.rb
index e8fd3ef57e5..78cc2869b72 100644
--- a/app/services/projects/base_move_relations_service.rb
+++ b/app/services/projects/base_move_relations_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class BaseMoveRelationsService < BaseService
attr_reader :source_project
diff --git a/app/services/projects/batch_count_service.rb b/app/services/projects/batch_count_service.rb
index 178ebc5a143..aec3b32da89 100644
--- a/app/services/projects/batch_count_service.rb
+++ b/app/services/projects/batch_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for getting and caching the number of elements of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids.
diff --git a/app/services/projects/batch_forks_count_service.rb b/app/services/projects/batch_forks_count_service.rb
index e61fe6c86b2..9bf369df999 100644
--- a/app/services/projects/batch_forks_count_service.rb
+++ b/app/services/projects/batch_forks_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for getting and caching the number of forks of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids
diff --git a/app/services/projects/batch_open_issues_count_service.rb b/app/services/projects/batch_open_issues_count_service.rb
index 3b0ade2419b..d375fcf9dbd 100644
--- a/app/services/projects/batch_open_issues_count_service.rb
+++ b/app/services/projects/batch_open_issues_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Service class for getting and caching the number of issues of several projects
# Warning: do not user this service with a really large set of projects
# because the service use maps to retrieve the project ids
diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb
index 4c8e000928f..3cee80c7bbc 100644
--- a/app/services/projects/count_service.rb
+++ b/app/services/projects/count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Base class for the various service classes that count project data (e.g.
# issues or forks).
diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb
index 29b133cc466..f5c48e56880 100644
--- a/app/services/projects/create_from_template_service.rb
+++ b/app/services/projects/create_from_template_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class CreateFromTemplateService < BaseService
def initialize(user, params)
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 85491089d8e..02a3a3eb096 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class CreateService < BaseService
def initialize(user, params)
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 87173cc79ec..46a8a5e4d98 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class DestroyService < BaseService
include Gitlab::ShellAdapter
diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb
index 604747e39d0..dd297c9ba43 100644
--- a/app/services/projects/download_service.rb
+++ b/app/services/projects/download_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class DownloadService < BaseService
WHITELIST = [
diff --git a/app/services/projects/enable_deploy_key_service.rb b/app/services/projects/enable_deploy_key_service.rb
index 121385afca3..b7c172028e9 100644
--- a/app/services/projects/enable_deploy_key_service.rb
+++ b/app/services/projects/enable_deploy_key_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class EnableDeployKeyService < BaseService
def execute
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index a8aafa9fb4f..33ad2120a75 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class ForkService < BaseService
def execute(fork_to_project = nil)
diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb
index dc6eb19affd..b570c6d4754 100644
--- a/app/services/projects/forks_count_service.rb
+++ b/app/services/projects/forks_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Service class for getting and caching the number of forks of a project.
class ForksCountService < Projects::CountService
diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb
index a16268f4fd2..bc6e9caebb8 100644
--- a/app/services/projects/gitlab_projects_import_service.rb
+++ b/app/services/projects/gitlab_projects_import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service is an adapter used to for the GitLab Import feature, and
# creating a project from a template.
# The latter will under the hood just import an archive supplied by GitLab.
diff --git a/app/services/projects/group_links/create_service.rb b/app/services/projects/group_links/create_service.rb
index 35624577024..1392775f805 100644
--- a/app/services/projects/group_links/create_service.rb
+++ b/app/services/projects/group_links/create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module GroupLinks
class CreateService < BaseService
diff --git a/app/services/projects/group_links/destroy_service.rb b/app/services/projects/group_links/destroy_service.rb
index e3a20b4c1e4..8aefad048ce 100644
--- a/app/services/projects/group_links/destroy_service.rb
+++ b/app/services/projects/group_links/destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module GroupLinks
class DestroyService < BaseService
diff --git a/app/services/projects/hashed_storage/migrate_attachments_service.rb b/app/services/projects/hashed_storage/migrate_attachments_service.rb
index bc897d891d5..649c916a593 100644
--- a/app/services/projects/hashed_storage/migrate_attachments_service.rb
+++ b/app/services/projects/hashed_storage/migrate_attachments_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module HashedStorage
AttachmentMigrationError = Class.new(StandardError)
diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb
index 68c1af2396b..70f00b7fdeb 100644
--- a/app/services/projects/hashed_storage/migrate_repository_service.rb
+++ b/app/services/projects/hashed_storage/migrate_repository_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module HashedStorage
class MigrateRepositoryService < BaseService
diff --git a/app/services/projects/hashed_storage_migration_service.rb b/app/services/projects/hashed_storage_migration_service.rb
index 662702c1db5..1828c99a65e 100644
--- a/app/services/projects/hashed_storage_migration_service.rb
+++ b/app/services/projects/hashed_storage_migration_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class HashedStorageMigrationService < BaseService
attr_reader :logger
diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb
index 120d57a188d..2f6dc4207dd 100644
--- a/app/services/projects/housekeeping_service.rb
+++ b/app/services/projects/housekeeping_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Projects::HousekeepingService class
#
# Used for git housekeeping
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 7bf0b90b491..e3491282a8a 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
module ImportExport
class ExportService < BaseService
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 1781a01cbd4..60f400edfce 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class ImportService < BaseService
include Gitlab::ShellAdapter
diff --git a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
index d9fb74b090e..a837ea82e38 100644
--- a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service lists the download link from a remote source based on the
# oids provided
module Projects
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index 618c30b971f..7d4fa4e08df 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service downloads and links lfs objects from a remote URL
module Projects
module LfsPointers
diff --git a/app/services/projects/lfs_pointers/lfs_import_service.rb b/app/services/projects/lfs_pointers/lfs_import_service.rb
index b6b0dec142f..97ce681a911 100644
--- a/app/services/projects/lfs_pointers/lfs_import_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_import_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service manages the whole worflow of discovering the Lfs files in a
# repository, linking them to the project and downloading (and linking) the non
# existent ones.
diff --git a/app/services/projects/lfs_pointers/lfs_link_service.rb b/app/services/projects/lfs_pointers/lfs_link_service.rb
index d20bdf86c58..a2eba8e124e 100644
--- a/app/services/projects/lfs_pointers/lfs_link_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_link_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Given a list of oids, this services links the existent Lfs Objects to the project
module Projects
module LfsPointers
diff --git a/app/services/projects/lfs_pointers/lfs_list_service.rb b/app/services/projects/lfs_pointers/lfs_list_service.rb
index b770982cbc0..22160017f4f 100644
--- a/app/services/projects/lfs_pointers/lfs_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This service list all existent Lfs objects in a repository
module Projects
module LfsPointers
diff --git a/app/services/projects/move_access_service.rb b/app/services/projects/move_access_service.rb
index 3af3a22d486..8e2c3ad2f69 100644
--- a/app/services/projects/move_access_service.rb
+++ b/app/services/projects/move_access_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveAccessService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_deploy_keys_projects_service.rb b/app/services/projects/move_deploy_keys_projects_service.rb
index dde420655b0..40a22837eaf 100644
--- a/app/services/projects/move_deploy_keys_projects_service.rb
+++ b/app/services/projects/move_deploy_keys_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveDeployKeysProjectsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_forks_service.rb b/app/services/projects/move_forks_service.rb
index d2901ea1457..076a7a50aa9 100644
--- a/app/services/projects/move_forks_service.rb
+++ b/app/services/projects/move_forks_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveForksService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_lfs_objects_projects_service.rb b/app/services/projects/move_lfs_objects_projects_service.rb
index 298da5f1a82..a5099519594 100644
--- a/app/services/projects/move_lfs_objects_projects_service.rb
+++ b/app/services/projects/move_lfs_objects_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveLfsObjectsProjectsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_notification_settings_service.rb b/app/services/projects/move_notification_settings_service.rb
index f7be461a5da..746605d56f1 100644
--- a/app/services/projects/move_notification_settings_service.rb
+++ b/app/services/projects/move_notification_settings_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveNotificationSettingsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/move_project_authorizations_service.rb b/app/services/projects/move_project_authorizations_service.rb
index 5ef12fc49e5..60f2af88e99 100644
--- a/app/services/projects/move_project_authorizations_service.rb
+++ b/app/services/projects/move_project_authorizations_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
diff --git a/app/services/projects/move_project_group_links_service.rb b/app/services/projects/move_project_group_links_service.rb
index dbeffd7dae9..d9038030f7e 100644
--- a/app/services/projects/move_project_group_links_service.rb
+++ b/app/services/projects/move_project_group_links_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
diff --git a/app/services/projects/move_project_members_service.rb b/app/services/projects/move_project_members_service.rb
index 22a5f0a3fe6..bb0c0d10242 100644
--- a/app/services/projects/move_project_members_service.rb
+++ b/app/services/projects/move_project_members_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# NOTE: This service cannot be used directly because it is part of a
# a bigger process. Instead, use the service MoveAccessService which moves
# project memberships, project group links, authorizations and refreshes
diff --git a/app/services/projects/move_users_star_projects_service.rb b/app/services/projects/move_users_star_projects_service.rb
index 079fd5b9685..20121d429e2 100644
--- a/app/services/projects/move_users_star_projects_service.rb
+++ b/app/services/projects/move_users_star_projects_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class MoveUsersStarProjectsService < BaseMoveRelationsService
def execute(source_project, remove_remaining_elements: true)
diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb
index 78b1477186a..5d6620c3c54 100644
--- a/app/services/projects/open_issues_count_service.rb
+++ b/app/services/projects/open_issues_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Service class for counting and caching the number of open issues of a
# project.
diff --git a/app/services/projects/open_merge_requests_count_service.rb b/app/services/projects/open_merge_requests_count_service.rb
index 77e6448fd5e..76ec13952ab 100644
--- a/app/services/projects/open_merge_requests_count_service.rb
+++ b/app/services/projects/open_merge_requests_count_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
# Service class for counting and caching the number of open merge requests of
# a project.
diff --git a/app/services/projects/overwrite_project_service.rb b/app/services/projects/overwrite_project_service.rb
index ce94f147aa9..696e1b665b2 100644
--- a/app/services/projects/overwrite_project_service.rb
+++ b/app/services/projects/overwrite_project_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class OverwriteProjectService < BaseService
def execute(source_project)
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index 21741913385..7080f388e53 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class ParticipantsService < BaseService
include Users::ParticipableService
diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb
index a8ef2108492..fdfa91801ab 100644
--- a/app/services/projects/propagate_service_template.rb
+++ b/app/services/projects/propagate_service_template.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class PropagateServiceTemplate
BATCH_SIZE = 100
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 61acdd58021..a4a66330546 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Projects::TransferService class
#
# Used for transfer project to another namespace
diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb
index 842fe4e09c4..2c0d91fe34f 100644
--- a/app/services/projects/unlink_fork_service.rb
+++ b/app/services/projects/unlink_fork_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UnlinkForkService < BaseService
def execute
diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb
index 25017c5cbe3..efbd4c7b323 100644
--- a/app/services/projects/update_pages_configuration_service.rb
+++ b/app/services/projects/update_pages_configuration_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdatePagesConfigurationService < BaseService
attr_reader :project
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index 1d8caec9c6f..eb2478be3cf 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdatePagesService < BaseService
InvalidStateError = Class.new(StandardError)
diff --git a/app/services/projects/update_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb
index 8183a2f26d7..4651f7c4f8f 100644
--- a/app/services/projects/update_remote_mirror_service.rb
+++ b/app/services/projects/update_remote_mirror_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdateRemoteMirrorService < BaseService
attr_reader :errors
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index f4fbaacc08b..d3dc11435fe 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class UpdateService < BaseService
include UpdateVisibilityLevel
diff --git a/app/views/admin/identities/edit.html.haml b/app/views/admin/identities/edit.html.haml
index 1ad6ce969cb..fa09138c502 100644
--- a/app/views/admin/identities/edit.html.haml
+++ b/app/views/admin/identities/edit.html.haml
@@ -1,3 +1,6 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- add_to_breadcrumbs @user.name, admin_user_identities_path(@user)
+- breadcrumb_title "Edit Identity"
- page_title _("Edit"), @identity.provider, _("Identities"), @user.name, _("Users")
%h3.page-title
= _('Edit identity for %{user_name}') % { user_name: @user.name }
diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index 59373ee6752..df3df159947 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title _("Identities"), @user.name, _("Users")
= render 'admin/users/head'
diff --git a/app/views/admin/identities/new.html.haml b/app/views/admin/identities/new.html.haml
index ee743b0fd3c..c28d22625b5 100644
--- a/app/views/admin/identities/new.html.haml
+++ b/app/views/admin/identities/new.html.haml
@@ -1,3 +1,6 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- add_to_breadcrumbs @user.name, admin_user_identities_path(@user)
+- breadcrumb_title "New Identity"
- page_title _("New Identity")
%h3.page-title= _('New identity')
%hr
diff --git a/app/views/admin/impersonation_tokens/index.html.haml b/app/views/admin/impersonation_tokens/index.html.haml
index 1378dde52ab..9e490713ef3 100644
--- a/app/views/admin/impersonation_tokens/index.html.haml
+++ b/app/views/admin/impersonation_tokens/index.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title "Impersonation Tokens", @user.name, "Users"
= render 'admin/users/head'
diff --git a/app/views/admin/users/keys.html.haml b/app/views/admin/users/keys.html.haml
index 0f644121e62..103bbb3b063 100644
--- a/app/views/admin/users/keys.html.haml
+++ b/app/views/admin/users/keys.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title "SSH Keys", @user.name, "Users"
= render 'admin/users/head'
= render 'profiles/keys/key_table', admin: true
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index cf50d45f755..3d39c1da408 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -1,3 +1,5 @@
+- add_to_breadcrumbs "Users", admin_users_path
+- breadcrumb_title @user.name
- page_title "Groups and projects", @user.name, "Users"
= render 'admin/users/head'
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 3ae9ce6c11f..13f96b9747c 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -1,16 +1,17 @@
-- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
+- link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank'
.append-bottom-10
%h4= _("Setup a #{type} Runner manually")
%ol
%li
- = _("Install a Runner compatible with GitLab CI")
- = (_("(check out the %{link} for information on how to install it).") % { link: link }).html_safe
+ = link.html_safe
%li
= _("Specify the following URL during the Runner setup:")
%code#coordinator_address= root_url(only_path: false)
+ = clipboard_button(target: '#coordinator_address', title: _("Copy URL to clipboard"), class: "btn-transparent btn-clipboard")
%li
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
+ = clipboard_button(target: '#registration_token', title: _("Copy token to clipboard"), class: "btn-transparent btn-clipboard")
%li
= _("Start the Runner!")
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 89940512bc6..74ab8cf8250 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -12,6 +12,9 @@
.project-home-desc
- if @project.description.present?
= markdown_field(@project, :description)
+ - if can?(current_user, :read_project, @project)
+ .text-secondary.prepend-top-8
+ = s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
- if @project.forked?
%p
diff --git a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml b/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
index 9298d93663d..73b11d509d3 100644
--- a/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/projects/clusters/_gcp_signup_offer_banner.html.haml
@@ -1,12 +1,12 @@
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
-.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
+.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
%button.close{ type: "button", data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } &times;
- %div
- .col-sm-2.gcp-logo
- = image_tag 'illustrations/logos/google-cloud-platform_logo.svg'
- .col-sm-10
- %h4= s_('ClusterIntegration|Redeem up to $500 in free credit for Google Cloud Platform')
+ .gcp-signup-offer--content
+ .gcp-signup-offer--icon.append-right-8
+ = sprite_icon("information", size: 16)
+ .gcp-signup-offer--copy
+ %h4= s_('ClusterIntegration|Did you know?')
%p= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
- %a.btn.btn-info{ href: 'https://goo.gl/AaJzRW', target: '_blank', rel: 'noopener noreferrer' }
+ %a.btn.btn-default{ href: 'https://goo.gl/AaJzRW', target: '_blank', rel: 'noopener noreferrer' }
Apply for credit
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/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/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/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/47419-Fix-breadcrumbs.yml b/changelogs/unreleased/47419-Fix-breadcrumbs.yml
new file mode 100644
index 00000000000..1a7f8196683
--- /dev/null
+++ b/changelogs/unreleased/47419-Fix-breadcrumbs.yml
@@ -0,0 +1,5 @@
+---
+title: Fix breadcrumbs in Admin/User interface.
+merge_request: 19608
+author: Robin Naundorf
+type: fixed
diff --git a/changelogs/unreleased/48804-redesign-gcp-banner.yml b/changelogs/unreleased/48804-redesign-gcp-banner.yml
new file mode 100644
index 00000000000..729f959badc
--- /dev/null
+++ b/changelogs/unreleased/48804-redesign-gcp-banner.yml
@@ -0,0 +1,5 @@
+---
+title: Redesign GCP offer banner
+merge_request:
+author:
+type: changed
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/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/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/features-show-project-id-on-home-panel.yml b/changelogs/unreleased/features-show-project-id-on-home-panel.yml
new file mode 100644
index 00000000000..f592be07a52
--- /dev/null
+++ b/changelogs/unreleased/features-show-project-id-on-home-panel.yml
@@ -0,0 +1,5 @@
+---
+title: Show Project ID on project home panel
+merge_request: 20305
+author: Tuğçe Nur Taş
+type: added
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml
new file mode 100644
index 00000000000..ea962cf8edc
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-apps-services-inner-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable more frozen string in app/services/**/*.rb
+merge_request: 20677
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
new file mode 100644
index 00000000000..16b8ec3908f
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-apps-services-inner.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/services/**/*.rb
+merge_request: 20656
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/osw-fallback-to-collection-when-no-diff-refs.yml b/changelogs/unreleased/osw-fallback-to-collection-when-no-diff-refs.yml
new file mode 100644
index 00000000000..71a2d94fc55
--- /dev/null
+++ b/changelogs/unreleased/osw-fallback-to-collection-when-no-diff-refs.yml
@@ -0,0 +1,5 @@
+---
+title: Render MR page when diffs cannot be fetched from the database or the git repository
+merge_request: 20680
+author:
+type: fixed
diff --git a/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
new file mode 100644
index 00000000000..7bfe1b5778f
--- /dev/null
+++ b/changelogs/unreleased/satishperala-gitlab-ce-20720_webhooks_full_image_url.yml
@@ -0,0 +1,5 @@
+---
+title: Include full image URL in webhooks for uploaded images
+merge_request: 18109
+author: Satish Perala
+type: changed
diff --git a/changelogs/unreleased/sh-fix-issue-49133.yml b/changelogs/unreleased/sh-fix-issue-49133.yml
new file mode 100644
index 00000000000..847220d88b2
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-49133.yml
@@ -0,0 +1,5 @@
+---
+title: Fix symlink vulnerability in project import
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/update-specific-runners-help-url.yml b/changelogs/unreleased/update-specific-runners-help-url.yml
new file mode 100644
index 00000000000..0ccbc3b2d65
--- /dev/null
+++ b/changelogs/unreleased/update-specific-runners-help-url.yml
@@ -0,0 +1,5 @@
+---
+title: Update specific runners help URL
+merge_request: 20213
+author: George Tsiolis
+type: other
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/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/danger/changelog/Dangerfile b/danger/changelog/Dangerfile
index 0374de24520..a1f94dc6004 100644
--- a/danger/changelog/Dangerfile
+++ b/danger/changelog/Dangerfile
@@ -2,15 +2,13 @@
require 'yaml'
-NO_CHANGELOG_LABELS = %w[backstage QA test].freeze
+NO_CHANGELOG_LABELS = %w[backstage Documentation QA test].freeze
SEE_DOC = "See [the documentation](https://docs.gitlab.com/ce/development/changelog.html).".freeze
-MISSING_CHANGELOG_MESSAGE = <<~MSG.freeze
-**[CHANGELOG missing](https://docs.gitlab.com/ce/development/changelog.html).**
-
+CREATE_CHANGELOG_MESSAGE = <<~MSG.freeze
You can create one with:
```
-bin/changelog -m %<mr_iid>s
+bin/changelog -m %<mr_iid>s "%<mr_title>s"
```
If your merge request doesn't warrant a CHANGELOG entry,
@@ -56,13 +54,15 @@ 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 an entry with `bin/changelog -m #{gitlab.mr_json["iid"]}` instead."
+ 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 format(MISSING_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], labels: presented_no_changelog_labels)
+ 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/specs/Dangerfile b/danger/specs/Dangerfile
index 934ea0beadb..97188df8785 100644
--- a/danger/specs/Dangerfile
+++ b/danger/specs/Dangerfile
@@ -1,12 +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 the ~backstage label in that case.
+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(/spec/).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
- warn NO_NEW_SPEC_MESSAGE, sticky: false
+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/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/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/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/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index d054561d5f3..20886faf418 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -64,7 +64,8 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
## Repository size limit
-The maximum size your Git repository is allowed to be including LFS.
+The maximum size your Git repository is allowed to be, including LFS. If you are near
+or over the size limit, you can [reduce your repository size with Git](../project/repository/reducing_the_repo_size_using_git.md).
| Setting | GitLab.com | Default |
| ----------- | ----------------- | ------------- |
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
index 812ecf05faf..06171f11e12 100644
--- a/doc/user/project/import/manifest.md
+++ b/doc/user/project/import/manifest.md
@@ -1,7 +1,8 @@
# 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.
+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:**
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 8c09927e2df..8e486318980 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -10,6 +10,13 @@ Starting from GitLab 8.5:
Starting from GitLab 11.1, the logs of web hooks are automatically removed after
one month.
+>**Note**
+Starting from GitLab 11.2:
+- The `description` field for issues, merge requests, comments, and wiki pages
+ is rewritten so that simple Markdown image references (like
+ `![](/uploads/...)`) have their target URL changed to an absolute URL. See
+ [image URL rewriting](#image-url-rewriting) for more details.
+
Project webhooks allow you to trigger a URL if for example new code is pushed or
a new issue is created. You can configure webhooks to listen for specific events
like pushes, issues or merge requests. GitLab will send a POST request with data
@@ -1125,6 +1132,27 @@ X-Gitlab-Event: Build Hook
}
```
+## Image URL rewriting
+
+From GitLab 11.2, simple image references are rewritten to use an absolute URL
+in webhooks. So if an image, merge request, comment, or wiki page has this in
+its description:
+
+```markdown
+![image](/uploads/$sha/image.png)
+```
+
+It will appear in the webhook body as the below (assuming that GitLab is
+installed at gitlab.example.com):
+
+```markdown
+![image](https://gitlab.example.com/uploads/$sha/image.png)
+```
+
+This will not rewrite URLs that already are pointing to HTTP, HTTPS, or
+protocol-relative URLs. It will also not rewrite image URLs using advanced
+Markdown features, like link labels.
+
## Testing webhooks
You can trigger the webhook manually. Sample data from the project will be used.Sample data will take from the project.
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/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/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index fbfcd72c916..7108e828c6d 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -2,27 +2,7 @@ module Banzai
module Filter
class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
REGEX = %r{
- (?<code>
- # Code blocks:
- # ```
- # Anything, including `>>>` blocks which are ignored by this filter
- # ```
-
- ^```
- .+?
- \n```\ *$
- )
- |
- (?<html>
- # HTML block:
- # <tag>
- # Anything, including `>>>` blocks which are ignored by this filter
- # </tag>
-
- ^<[^>]+?>\ *\n
- .+?
- \n<\/[^>]+?>\ *$
- )
+ #{::Gitlab::Regex.markdown_code_or_html_blocks}
|
(?:
# Blockquote:
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 0d9b874ef85..5dab80dd3eb 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -24,6 +24,17 @@ module Banzai
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
+ *reference_filters,
+
+ Filter::TaskListFilter,
+ Filter::InlineDiffFilter,
+
+ Filter::SetDirectionFilter
+ ]
+ end
+
+ def self.reference_filters
+ [
Filter::UserReferenceFilter,
Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter,
@@ -32,12 +43,7 @@ module Banzai
Filter::CommitRangeReferenceFilter,
Filter::CommitReferenceFilter,
Filter::LabelReferenceFilter,
- Filter::MilestoneReferenceFilter,
-
- Filter::TaskListFilter,
- Filter::InlineDiffFilter,
-
- Filter::SetDirectionFilter
+ Filter::MilestoneReferenceFilter
]
end
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index dcd52bc03c7..0b2e584ef16 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -2,11 +2,17 @@ module Banzai
module Pipeline
class PostProcessPipeline < BasePipeline
def self.filters
- FilterArray[
+ @filters ||= FilterArray[
+ *internal_link_filters,
+ Filter::AbsoluteLinkFilter
+ ]
+ end
+
+ def self.internal_link_filters
+ [
Filter::RedactorFilter,
Filter::RelativeLinkFilter,
- Filter::IssuableStateFilter,
- Filter::AbsoluteLinkFilter
+ Filter::IssuableStateFilter
]
end
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
index 1929099931b..cd5a6c8875c 100644
--- a/lib/banzai/pipeline/single_line_pipeline.rb
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -10,13 +10,19 @@ module Banzai
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
+ *reference_filters
+ ]
+ end
+
+ def self.reference_filters
+ [
Filter::UserReferenceFilter,
Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter,
Filter::MergeRequestReferenceFilter,
Filter::SnippetReferenceFilter,
Filter::CommitRangeReferenceFilter,
- Filter::CommitReferenceFilter,
+ Filter::CommitReferenceFilter
]
end
end
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/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/git/commit.rb b/lib/gitlab/git/commit.rb
index 4e2d817d12c..5b264868af0 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -1,4 +1,4 @@
-# Gitlab::Git::Commit is a wrapper around native Rugged::Commit object
+# Gitlab::Git::Commit is a wrapper around Gitaly::GitCommit
module Gitlab
module Git
class Commit
@@ -55,7 +55,6 @@ module Gitlab
# A rugged reference?
commit_id = Gitlab::Git::Ref.dereference_object(commit_id)
- return decorate(repo, commit_id) if commit_id.is_a?(Rugged::Commit)
# Some weird thing?
return nil unless commit_id.is_a?(String)
@@ -68,9 +67,7 @@ module Gitlab
end
decorate(repo, commit) if commit
- rescue Rugged::ReferenceError, Rugged::InvalidError, Rugged::ObjectError,
- Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository,
- Rugged::OdbError, Rugged::TreeError, ArgumentError
+ rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, ArgumentError
nil
end
@@ -142,20 +139,6 @@ module Gitlab
Gitlab::Git::Commit.new(repository, commit, ref)
end
- # Returns the `Rugged` sorting type constant for one or more given
- # sort types. Valid keys are `:none`, `:topo`, and `:date`, or an array
- # containing more than one of them. `:date` uses a combination of date and
- # topological sorting to closer mimic git's native ordering.
- def rugged_sort_type(sort_type)
- @rugged_sort_types ||= {
- none: Rugged::SORT_NONE,
- topo: Rugged::SORT_TOPO,
- date: Rugged::SORT_DATE | Rugged::SORT_TOPO
- }
-
- @rugged_sort_types.fetch(sort_type, Rugged::SORT_NONE)
- end
-
def shas_with_signatures(repository, shas)
Gitlab::GitalyClient::CommitService.new(repository).filter_shas_with_signatures(shas)
end
@@ -223,8 +206,6 @@ module Gitlab
case raw_commit
when Hash
init_from_hash(raw_commit)
- when Rugged::Commit
- init_from_rugged(raw_commit)
when Gitaly::GitCommit
init_from_gitaly(raw_commit)
else
@@ -265,23 +246,6 @@ module Gitlab
@repository.gitaly_commit_client.diff_from_parent(self, options)
end
- # Not to be called directly, but right now its used for tests and in old
- # migrations
- def rugged_diff_from_parent(options = {})
- options ||= {}
- break_rewrites = options[:break_rewrites]
- actual_options = Gitlab::Git::Diff.filter_diff_options(options)
-
- diff = if rugged_commit.parents.empty?
- rugged_commit.diff(actual_options.merge(reverse: true))
- else
- rugged_commit.parents[0].diff(rugged_commit, actual_options)
- end
-
- diff.find_similar!(break_rewrites: break_rewrites)
- diff
- end
-
def deltas
@deltas ||= begin
deltas = @repository.gitaly_commit_client.commit_deltas(self)
@@ -352,14 +316,6 @@ module Gitlab
encode! @committer_email
end
- def rugged_commit
- @rugged_commit ||= if raw_commit.is_a?(Rugged::Commit)
- raw_commit
- else
- @repository.rev_parse_target(id)
- end
- end
-
def merge_commit?
parent_ids.size > 1
end
@@ -405,22 +361,6 @@ module Gitlab
end
end
- def init_from_rugged(commit)
- author = commit.author
- committer = commit.committer
-
- @raw_commit = commit
- @id = commit.oid
- @message = commit.message
- @authored_date = author[:time]
- @committed_date = committer[:time]
- @author_name = author[:name]
- @author_email = author[:email]
- @committer_name = committer[:name]
- @committer_email = committer[:email]
- @parent_ids = commit.parents.map(&:oid)
- end
-
def init_from_gitaly(commit)
@raw_commit = commit
@id = commit.id
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 3c23b588f77..19c79b6f7b7 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -252,14 +252,6 @@ module Gitlab
end
end
- def batch_existence(object_ids, existing: true)
- filter_method = existing ? :select : :reject
-
- object_ids.public_send(filter_method) do |oid| # rubocop:disable GitlabSecurity/PublicSend
- rugged.exists?(oid)
- end
- end
-
# Returns an Array of branch and tag names
def ref_names
branch_names + tag_names
@@ -409,13 +401,6 @@ module Gitlab
end
end
- # Return the object that +revspec+ points to. If +revspec+ is an
- # annotated tag, then return the tag's target instead.
- def rev_parse_target(revspec)
- obj = rugged.rev_parse(revspec)
- Ref.dereference_object(obj)
- end
-
# Counts the amount of commits between `from` and `to`.
def count_commits_between(from, to, options = {})
count_commits(from: from, to: to, **options)
@@ -443,12 +428,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
@@ -464,12 +445,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)
@@ -848,12 +825,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
@@ -1144,33 +1117,6 @@ module Gitlab
run_git!(args, lazy_block: block)
end
- def with_worktree(worktree_path, branch, sparse_checkout_files: nil, env:)
- base_args = %w(worktree add --detach)
-
- # Note that we _don't_ want to test for `.present?` here: If the caller
- # passes an non nil empty value it means it still wants sparse checkout
- # but just isn't interested in any file, perhaps because it wants to
- # checkout files in by a changeset but that changeset only adds files.
- if sparse_checkout_files
- # Create worktree without checking out
- run_git!(base_args + ['--no-checkout', worktree_path], env: env)
- worktree_git_path = run_git!(%w(rev-parse --git-dir), chdir: worktree_path).chomp
-
- configure_sparse_checkout(worktree_git_path, sparse_checkout_files)
-
- # After sparse checkout configuration, checkout `branch` in worktree
- run_git!(%W(checkout --detach #{branch}), chdir: worktree_path, env: env)
- else
- # Create worktree and checkout `branch` in it
- run_git!(base_args + [worktree_path, branch], env: env)
- end
-
- yield
- ensure
- FileUtils.rm_rf(worktree_path) if File.exist?(worktree_path)
- FileUtils.rm_rf(worktree_git_path) if worktree_git_path && File.exist?(worktree_git_path)
- end
-
def checksum
# The exists? RPC is much cheaper, so we perform this request first
raise NoRepository, "Repository does not exists" unless exists?
@@ -1188,37 +1134,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
@@ -1247,38 +1162,6 @@ module Gitlab
end
end
- # Adding a worktree means checking out the repository. For large repos,
- # this can be very expensive, so set up sparse checkout for the worktree
- # to only check out the files we're interested in.
- def configure_sparse_checkout(worktree_git_path, files)
- run_git!(%w(config core.sparseCheckout true))
-
- return if files.empty?
-
- worktree_info_path = File.join(worktree_git_path, 'info')
- FileUtils.mkdir_p(worktree_info_path)
- File.write(File.join(worktree_info_path, 'sparse-checkout'), files)
- end
-
- def rugged_fetch_source_branch(source_repository, source_branch, local_ref)
- with_repo_branch_commit(source_repository, source_branch) do |commit|
- if commit
- write_ref(local_ref, commit.sha)
- true
- else
- false
- end
- end
- end
-
- def worktree_path(prefix, id)
- id = id.to_s
- raise ArgumentError, "worktree id can't be empty" unless id.present?
- raise ArgumentError, "worktree id can't contain slashes " if id.include?("/")
-
- File.join(path, 'gitlab-worktree', "#{prefix}-#{id}")
- end
-
def git_env_for_user(user)
{
'GIT_COMMITTER_NAME' => user.name,
@@ -1289,20 +1172,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}" }
@@ -1353,17 +1222,6 @@ module Gitlab
Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
end
- # Return the Rugged patches for the diff between +from+ and +to+.
- def diff_patches(from, to, options = {}, *paths)
- options ||= {}
- break_rewrites = options[:break_rewrites]
- actual_options = Gitlab::Git::Diff.filter_diff_options(options.merge(paths: paths))
-
- diff = rugged.diff(from, to, actual_options)
- diff.find_similar!(break_rewrites: break_rewrites)
- diff.each_patch
- end
-
def sort_branches(branches, sort_by)
case sort_by
when 'name'
@@ -1448,19 +1306,9 @@ module Gitlab
raise CommandError, @gitlab_projects.output
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
-
- def sha_from_ref(ref)
- rev_parse_target(ref).oid
- end
end
end
end
diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb
index e35ea5762eb..9faa62be28e 100644
--- a/lib/gitlab/git/repository_mirroring.rb
+++ b/lib/gitlab/git/repository_mirroring.rb
@@ -50,7 +50,7 @@ module Gitlab
name = ref.name.sub(%r{\Arefs/remotes/#{remote_name}/}, '')
begin
- target_commit = Gitlab::Git::Commit.find(self, ref.target)
+ target_commit = Gitlab::Git::Commit.find(self, ref.target.oid)
branches << Gitlab::Git::Branch.new(self, name, ref.target, target_commit)
rescue Rugged::ReferenceError
# Omit invalid branch
diff --git a/lib/gitlab/hook_data/base_builder.rb b/lib/gitlab/hook_data/base_builder.rb
new file mode 100644
index 00000000000..4ffca356b29
--- /dev/null
+++ b/lib/gitlab/hook_data/base_builder.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module HookData
+ class BaseBuilder
+ attr_accessor :object
+
+ MARKDOWN_SIMPLE_IMAGE = %r{
+ #{::Gitlab::Regex.markdown_code_or_html_blocks}
+ |
+ (?<image>
+ !
+ \[(?<title>[^\n]*?)\]
+ \((?<url>(?!(https?://|//))[^\n]+?)\)
+ )
+ }mx.freeze
+
+ def initialize(object)
+ @object = object
+ end
+
+ private
+
+ def absolute_image_urls(markdown_text)
+ return markdown_text unless markdown_text.present?
+
+ markdown_text.gsub(MARKDOWN_SIMPLE_IMAGE) do
+ if $~[:image]
+ url = $~[:url]
+ url = "/#{url}" unless url.start_with?('/')
+
+ "![#{$~[:title]}](#{Gitlab.config.gitlab.url}#{url})"
+ else
+ $~[0]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/issuable_builder.rb b/lib/gitlab/hook_data/issuable_builder.rb
index 6ab36676127..f2eda398b8f 100644
--- a/lib/gitlab/hook_data/issuable_builder.rb
+++ b/lib/gitlab/hook_data/issuable_builder.rb
@@ -1,13 +1,9 @@
module Gitlab
module HookData
- class IssuableBuilder
+ class IssuableBuilder < BaseBuilder
CHANGES_KEYS = %i[previous current].freeze
- attr_accessor :issuable
-
- def initialize(issuable)
- @issuable = issuable
- end
+ alias_method :issuable, :object
def build(user: nil, changes: {})
hook_data = {
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index f9b1a3caf5e..0d71c748dc6 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -1,6 +1,6 @@
module Gitlab
module HookData
- class IssueBuilder
+ class IssueBuilder < BaseBuilder
SAFE_HOOK_ATTRIBUTES = %i[
assignee_id
author_id
@@ -30,14 +30,11 @@ module Gitlab
total_time_spent
].freeze
- attr_accessor :issue
-
- def initialize(issue)
- @issue = issue
- end
+ alias_method :issue, :object
def build
attrs = {
+ description: absolute_image_urls(issue.description),
url: Gitlab::UrlBuilder.build(issue),
total_time_spent: issue.total_time_spent,
human_total_time_spent: issue.human_total_time_spent,
diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb
index aff786864f2..dfbed0597ed 100644
--- a/lib/gitlab/hook_data/merge_request_builder.rb
+++ b/lib/gitlab/hook_data/merge_request_builder.rb
@@ -1,6 +1,6 @@
module Gitlab
module HookData
- class MergeRequestBuilder
+ class MergeRequestBuilder < BaseBuilder
SAFE_HOOK_ATTRIBUTES = %i[
assignee_id
author_id
@@ -35,14 +35,11 @@ module Gitlab
total_time_spent
].freeze
- attr_accessor :merge_request
-
- def initialize(merge_request)
- @merge_request = merge_request
- end
+ alias_method :merge_request, :object
def build
attrs = {
+ description: absolute_image_urls(merge_request.description),
url: Gitlab::UrlBuilder.build(merge_request),
source: merge_request.source_project.try(:hook_attrs),
target: merge_request.target_project.hook_attrs,
diff --git a/lib/gitlab/hook_data/note_builder.rb b/lib/gitlab/hook_data/note_builder.rb
new file mode 100644
index 00000000000..81873e345d5
--- /dev/null
+++ b/lib/gitlab/hook_data/note_builder.rb
@@ -0,0 +1,43 @@
+module Gitlab
+ module HookData
+ class NoteBuilder < BaseBuilder
+ SAFE_HOOK_ATTRIBUTES = %i[
+ attachment
+ author_id
+ change_position
+ commit_id
+ created_at
+ discussion_id
+ id
+ line_code
+ note
+ noteable_id
+ noteable_type
+ original_position
+ position
+ project_id
+ resolved_at
+ resolved_by_id
+ resolved_by_push
+ st_diff
+ system
+ type
+ updated_at
+ updated_by_id
+ ].freeze
+
+ alias_method :note, :object
+
+ def build
+ note
+ .attributes
+ .with_indifferent_access
+ .slice(*SAFE_HOOK_ATTRIBUTES)
+ .merge(
+ description: absolute_image_urls(note.note),
+ url: Gitlab::UrlBuilder.build(note)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/wiki_page_builder.rb b/lib/gitlab/hook_data/wiki_page_builder.rb
new file mode 100644
index 00000000000..59c94a61cf2
--- /dev/null
+++ b/lib/gitlab/hook_data/wiki_page_builder.rb
@@ -0,0 +1,15 @@
+module Gitlab
+ module HookData
+ class WikiPageBuilder < BaseBuilder
+ alias_method :wiki_page, :object
+
+ def build
+ wiki_page
+ .attributes
+ .merge(
+ 'content' => absolute_image_urls(wiki_page.content)
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 0f4c3498036..4c411f4847e 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -4,6 +4,7 @@ module Gitlab
include Gitlab::ImportExport::CommandLineUtil
MAX_RETRIES = 8
+ IGNORED_FILENAMES = %w(. ..).freeze
def self.import(*args)
new(*args).import
@@ -59,7 +60,7 @@ module Gitlab
end
def extracted_files
- Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| f =~ %r{.*/\.{1,2}$} }
+ Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| IGNORED_FILENAMES.include?(File.basename(f)) }
end
end
end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index ac3de2a8f71..e1a958c508a 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -73,5 +73,31 @@ module Gitlab
def build_trace_section_regex
@build_trace_section_regexp ||= /section_((?:start)|(?:end)):(\d+):([a-zA-Z0-9_.-]+)\r\033\[0K/.freeze
end
+
+ def markdown_code_or_html_blocks
+ @markdown_code_or_html_blocks ||= %r{
+ (?<code>
+ # Code blocks:
+ # ```
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # ```
+
+ ^```
+ .+?
+ \n```\ *$
+ )
+ |
+ (?<html>
+ # HTML block:
+ # <tag>
+ # Anything, including `>>>` blocks which are ignored by this filter
+ # </tag>
+
+ ^<[^>]+?>\ *\n
+ .+?
+ \n<\/[^>]+?>\ *$
+ )
+ }mx
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1b68156a503..562760552e0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,6 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-07-10 16:02-0700\n"
+"PO-Revision-Date: 2018-07-10 16:02-0700\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -129,9 +131,6 @@ msgstr ""
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
-msgid "(check out the %{link} for information on how to install it)."
-msgstr ""
-
msgid "+ %{moreCount} more"
msgstr ""
@@ -1248,6 +1247,9 @@ msgstr ""
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
+msgid "ClusterIntegration|Did you know?"
+msgstr ""
+
msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
@@ -1410,9 +1412,6 @@ msgstr ""
msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
-msgid "ClusterIntegration|Redeem up to $500 in free credit for Google Cloud Platform"
-msgstr ""
-
msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
@@ -1774,6 +1773,9 @@ msgstr ""
msgid "Copy to clipboard"
msgstr ""
+msgid "Copy token to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -2594,9 +2596,6 @@ msgstr ""
msgid "GitLab Import"
msgstr ""
-msgid "GitLab Runner section"
-msgstr ""
-
msgid "GitLab User"
msgstr ""
@@ -2917,10 +2916,10 @@ msgstr ""
msgid "Inline"
msgstr ""
-msgid "Install Runner on Kubernetes"
+msgid "Install GitLab Runner"
msgstr ""
-msgid "Install a Runner compatible with GitLab CI"
+msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Instance does not support multiple Kubernetes clusters"
@@ -4058,6 +4057,9 @@ msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectPage|Project ID: %{project_id}"
+msgstr ""
+
msgid "Projects"
msgstr ""
@@ -5858,6 +5860,9 @@ msgstr ""
msgid "branch name"
msgstr ""
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
msgid "command line instructions"
msgstr ""
diff --git a/qa/qa.rb b/qa/qa.rb
index ef83722de90..0b48cf58766 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -53,6 +53,7 @@ module QA
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
@@ -136,6 +137,15 @@ 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'
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/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/project/show.rb b/qa/qa/page/project/show.rb
index 88861d5772d..1d3dad4cda0 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -31,10 +31,18 @@ module QA
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
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/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/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 444415011a9..1692f299552 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -53,6 +53,23 @@ describe Projects::MergeRequestsController do
it_behaves_like "loads labels", :show
describe 'as html' do
+ context 'when diff files were cleaned' do
+ render_views
+
+ it 'renders page when diff size is not persisted and diff_refs does not exist' do
+ diff = merge_request.merge_request_diff
+
+ diff.clean!
+ diff.update!(real_size: nil,
+ start_commit_sha: nil,
+ base_commit_sha: nil)
+
+ go(format: :html)
+
+ expect(response).to be_success
+ end
+ end
+
it "renders merge request page" do
go(format: :html)
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 9e3221577c7..6c194c9a646 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -315,6 +315,40 @@ describe "Admin::Users" do
end
end
+ describe 'show breadcrumbs' do
+ it do
+ visit admin_user_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit projects_admin_user_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit keys_admin_user_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit admin_user_impersonation_tokens_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit admin_user_identities_path(user)
+
+ check_breadcrumb(user.name)
+
+ visit new_admin_user_identity_path(user)
+
+ check_breadcrumb("New Identity")
+
+ visit admin_user_identities_path(user)
+
+ find('.table').find(:link, 'Edit').click
+
+ check_breadcrumb("Edit Identity")
+ end
+ end
+
describe 'show user attributes' do
it do
visit admin_users_path
@@ -409,4 +443,8 @@ describe "Admin::Users" do
expect(page).not_to have_content('twitter')
end
end
+
+ def check_breadcrumb(content)
+ expect(find('.breadcrumbs-sub-title')).to have_content(content)
+ end
end
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 c6ec3f08cc5..aa499493dbe 100644
--- a/spec/features/merge_request/user_cherry_picks_spec.rb
+++ b/spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb
@@ -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/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 830565620d6..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_maintainer(user)
- sign_in(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/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/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/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 0f3a95da5bf..241ff07026e 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -24,7 +24,7 @@ describe('diff_file_header', () => {
const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true });
props = {
diffFile,
- currentUser: {},
+ canCurrentUserFork: false,
};
});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 9b994543e19..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();
});
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/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index 919b612bb6a..6210d4a7124 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -184,4 +184,23 @@ describe('Diffs Module Getters', () => {
).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/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 10808315372..e4c98a3bcb5 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -6561,6 +6561,9 @@ export const environmentData = [
folder_path: '/root/hello-prometheus/environments/folders/production',
created_at: '2018-06-29T16:53:38.301Z',
updated_at: '2018-06-29T16:57:09.825Z',
+ last_deployment: {
+ id: 127,
+ },
},
},
{
@@ -6580,6 +6583,20 @@ export const environmentData = [
folder_path: '/root/hello-prometheus/environments/folders/review',
created_at: '2018-07-03T18:39:41.702Z',
updated_at: '2018-07-03T18:44:54.010Z',
+ last_deployment: {
+ id: 128,
+ },
+ },
+ },
+ {
+ name: 'no-deployment',
+ size: 1,
+ latest: {
+ id: 36,
+ name: 'no-deployment/noop-branch',
+ state: 'available',
+ created_at: '2018-07-04T18:39:41.702Z',
+ updated_at: '2018-07-04T18:44:54.010Z',
},
},
];
diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js
index 08d54946787..ccdf4eda563 100644
--- a/spec/javascripts/monitoring/monitoring_store_spec.js
+++ b/spec/javascripts/monitoring/monitoring_store_spec.js
@@ -1,5 +1,5 @@
import MonitoringStore from '~/monitoring/stores/monitoring_store';
-import MonitoringMock, { deploymentData } from './mock_data';
+import MonitoringMock, { deploymentData, environmentData } from './mock_data';
describe('MonitoringStore', function () {
this.store = new MonitoringStore();
@@ -21,4 +21,9 @@ describe('MonitoringStore', function () {
expect(this.store.deploymentData.length).toEqual(3);
expect(typeof this.store.deploymentData[0]).toEqual('object');
});
+
+ it('only stores environment data that contains deployments', () => {
+ this.store.storeEnvironmentsData(environmentData);
+ expect(this.store.environmentsData.length).toEqual(2);
+ });
});
diff --git a/spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js b/spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js
new file mode 100644
index 00000000000..8635203c413
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js
@@ -0,0 +1,45 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import component from '~/vue_shared/components/reports/modal_open_name.vue';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+
+describe('Modal open name', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const store = new Vuex.Store({
+ actions: {
+ openModal: () => {},
+ },
+ state: {},
+ mutations: {},
+ });
+
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: {
+ issue: {
+ title: 'Issue',
+ },
+ status: 'failed',
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders the issue name', () => {
+ expect(vm.$el.textContent.trim()).toEqual('Issue');
+ });
+
+ it('calls openModal actions when button is clicked', () => {
+ spyOn(vm, 'openModal');
+
+ vm.$el.click();
+
+ expect(vm.openModal).toHaveBeenCalled();
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/reports/report_issues_spec.js b/spec/javascripts/vue_shared/components/reports/report_issues_spec.js
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/reports/report_issues_spec.js
diff --git a/spec/javascripts/vue_shared/components/reports/report_link_spec.js b/spec/javascripts/vue_shared/components/reports/report_link_spec.js
new file mode 100644
index 00000000000..a4691f3712f
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/reports/report_link_spec.js
@@ -0,0 +1,71 @@
+import Vue from 'vue';
+import component from '~/vue_shared/components/reports/report_link.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('report link', () => {
+ let vm;
+
+ const Component = Vue.extend(component);
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('With url', () => {
+ it('renders link', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ urlPath: '/Gemfile.lock',
+ },
+ });
+
+ expect(vm.$el.textContent.trim()).toContain('in');
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('/Gemfile.lock');
+ expect(vm.$el.querySelector('a').textContent.trim()).toEqual('Gemfile.lock');
+ });
+ });
+
+ describe('Without url', () => {
+ it('does not render link', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ },
+ });
+
+ expect(vm.$el.querySelector('a')).toBeNull();
+ expect(vm.$el.textContent.trim()).toContain('in');
+ expect(vm.$el.textContent.trim()).toContain('Gemfile.lock');
+ });
+ });
+
+ describe('with line', () => {
+ it('renders line number', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ urlPath:
+ 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
+ line: 22,
+ },
+ });
+
+ expect(vm.$el.querySelector('a').textContent.trim()).toContain('Gemfile.lock:22');
+ });
+ });
+
+ describe('without line', () => {
+ it('does not render line number', () => {
+ vm = mountComponent(Component, {
+ issue: {
+ path: 'Gemfile.lock',
+ urlPath:
+ 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
+ },
+ });
+
+ expect(vm.$el.querySelector('a').textContent.trim()).not.toContain(':22');
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/reports/report_section_spec.js b/spec/javascripts/vue_shared/components/reports/report_section_spec.js
new file mode 100644
index 00000000000..07401181ffd
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/reports/report_section_spec.js
@@ -0,0 +1,174 @@
+import Vue from 'vue';
+import reportSection from '~/vue_shared/components/reports/report_section.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Report section', () => {
+ let vm;
+ const ReportSection = Vue.extend(reportSection);
+
+ const resolvedIssues = [
+ {
+ name: 'Insecure Dependency',
+ fingerprint: 'ca2e59451e98ae60ba2f54e3857c50e5',
+ path: 'Gemfile.lock',
+ line: 12,
+ urlPath: 'foo/Gemfile.lock',
+ },
+ ];
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ beforeEach(() => {
+ vm = mountComponent(ReportSection, {
+ type: 'codequality',
+ status: 'SUCCESS',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ resolvedIssues,
+ hasIssues: false,
+ alwaysOpen: false,
+ });
+ });
+
+ describe('isCollapsible', () => {
+ const testMatrix = [
+ { hasIssues: false, alwaysOpen: false, isCollapsible: false },
+ { hasIssues: false, alwaysOpen: true, isCollapsible: false },
+ { hasIssues: true, alwaysOpen: false, isCollapsible: true },
+ { hasIssues: true, alwaysOpen: true, isCollapsible: false },
+ ];
+
+ testMatrix.forEach(({ hasIssues, alwaysOpen, isCollapsible }) => {
+ const issues = hasIssues ? 'has issues' : 'has no issues';
+ const open = alwaysOpen ? 'is always open' : 'is not always open';
+
+ it(`is ${isCollapsible}, if the report ${issues} and ${open}`, done => {
+ vm.hasIssues = hasIssues;
+ vm.alwaysOpen = alwaysOpen;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.isCollapsible).toBe(isCollapsible);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('isExpanded', () => {
+ const testMatrix = [
+ { isCollapsed: false, alwaysOpen: false, isExpanded: true },
+ { isCollapsed: false, alwaysOpen: true, isExpanded: true },
+ { isCollapsed: true, alwaysOpen: false, isExpanded: false },
+ { isCollapsed: true, alwaysOpen: true, isExpanded: true },
+ ];
+
+ testMatrix.forEach(({ isCollapsed, alwaysOpen, isExpanded }) => {
+ const issues = isCollapsed ? 'is collapsed' : 'is not collapsed';
+ const open = alwaysOpen ? 'is always open' : 'is not always open';
+
+ it(`is ${isExpanded}, if the report ${issues} and ${open}`, done => {
+ vm.isCollapsed = isCollapsed;
+ vm.alwaysOpen = alwaysOpen;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.isExpanded).toBe(isExpanded);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+ });
+ describe('when it is loading', () => {
+ it('should render loading indicator', () => {
+ vm = mountComponent(ReportSection, {
+ type: 'codequality',
+ status: 'LOADING',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ hasIssues: false,
+ });
+ expect(vm.$el.textContent.trim()).toEqual('Loading codeclimate report');
+ });
+ });
+
+ describe('with success status', () => {
+ beforeEach(() => {
+ vm = mountComponent(ReportSection, {
+ type: 'codequality',
+ status: 'SUCCESS',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'foo',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ resolvedIssues,
+ hasIssues: true,
+ });
+ });
+
+ it('should render provided data', () => {
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Code quality improved on 1 point and degraded on 1 point',
+ );
+
+ expect(vm.$el.querySelectorAll('.js-mr-code-resolved-issues li').length).toEqual(
+ resolvedIssues.length,
+ );
+ });
+
+ describe('toggleCollapsed', () => {
+ const hiddenCss = { display: 'none' };
+
+ it('toggles issues', done => {
+ vm.$el.querySelector('button').click();
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-report-section-container')).not.toHaveCss(hiddenCss);
+ expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Collapse');
+
+ vm.$el.querySelector('button').click();
+ })
+ .then(Vue.nextTick)
+ .then(() => {
+ expect(vm.$el.querySelector('.js-report-section-container')).toHaveCss(hiddenCss);
+ expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Expand');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('is always expanded, if always-open is set to true', done => {
+ vm.alwaysOpen = true;
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.js-report-section-container')).not.toHaveCss(hiddenCss);
+ expect(vm.$el.querySelector('button')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('with failed request', () => {
+ it('should render error indicator', () => {
+ vm = mountComponent(ReportSection, {
+ type: 'codequality',
+ status: 'ERROR',
+ loadingText: 'Loading codeclimate report',
+ errorText: 'Failed to load codeclimate report',
+ successText: 'Code quality improved on 1 point and degraded on 1 point',
+ hasIssues: false,
+ });
+ expect(vm.$el.textContent.trim()).toEqual('Failed to load codeclimate report');
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/reports/summary_row_spec.js b/spec/javascripts/vue_shared/components/reports/summary_row_spec.js
new file mode 100644
index 00000000000..ac076f05bc0
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/reports/summary_row_spec.js
@@ -0,0 +1,37 @@
+import Vue from 'vue';
+import component from '~/vue_shared/components/reports/summary_row.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+describe('Summary row', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ summary: 'SAST detected 1 new vulnerability and 1 fixed vulnerability',
+ popoverOptions: {
+ title: 'Static Application Security Testing (SAST)',
+ content: '<a>Learn more about SAST</a>',
+ },
+ statusIcon: 'warning',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders provided summary', () => {
+ expect(
+ vm.$el.querySelector('.report-block-list-issue-description-text').textContent.trim(),
+ ).toEqual(props.summary);
+ });
+
+ it('renders provided icon', () => {
+ expect(vm.$el.querySelector('.report-block-list-icon span').classList).toContain(
+ 'js-ci-status-icon-warning',
+ );
+ });
+});
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index 211e3aaa94b..0735ebd6dcb 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -9,6 +9,11 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:merge_request) { merge_requests.create!(iid: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master').becomes(MergeRequest) }
let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff }
let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) }
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
+ end
before do
allow_any_instance_of(MergeRequestDiff)
@@ -299,11 +304,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:commits) { merge_request_diff.commits.map(&:to_hash) }
let(:first_commit) { project.repository.commit(merge_request_diff.head_commit_sha) }
let(:expected_commits) { commits }
- let(:diffs) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- first_commit.rugged_diff_from_parent.patches
- end
- end
+ let(:diffs) { rugged_diff(first_commit.sha).patches }
let(:expected_diffs) { [] }
include_examples 'updated MR diff'
@@ -313,14 +314,15 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:commits) { merge_request_diff.commits.map(&:to_hash) }
let(:first_commit) { project.repository.commit(merge_request_diff.head_commit_sha) }
let(:expected_commits) { commits }
- let(:diffs) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- first_commit.rugged_diff_from_parent.deltas
- end
- end
+ let(:diffs) { rugged_diff(first_commit.sha).deltas }
let(:expected_diffs) { [] }
include_examples 'updated MR diff'
end
+
+ def rugged_diff(commit_sha)
+ rugged_commit = rugged.lookup(commit_sha)
+ rugged_commit.parents[0].diff(rugged_commit)
+ end
end
end
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/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index ee74c2769eb..0adb684765d 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
}
@parents = [repo.head.target]
- @gitlab_parents = @parents.map { |c| described_class.decorate(repository, c) }
+ @gitlab_parents = @parents.map { |c| described_class.find(repository, c.oid) }
@tree = @parents.first.tree
sha = Rugged::Commit.create(
@@ -41,7 +41,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
)
@raw_commit = repo.lookup(sha)
- @commit = described_class.new(repository, @raw_commit)
+ @commit = described_class.find(repository, sha)
end
it { expect(@commit.short_id).to eq(@raw_commit.oid[0..10]) }
@@ -488,13 +488,15 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe '#init_from_rugged' do
- let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
- subject { gitlab_commit }
+ skip 'move this test to gitaly-ruby' do
+ describe '#init_from_rugged' do
+ let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
+ subject { gitlab_commit }
- describe '#id' do
- subject { super().id }
- it { is_expected.to eq(SeedRepo::Commit::ID) }
+ describe '#id' do
+ subject { super().id }
+ it { is_expected.to eq(SeedRepo::Commit::ID) }
+ end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 6480f6c407d..0365c3b20ef 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -639,21 +639,21 @@ describe Gitlab::Git::Repository, seed_helper: true do
describe "#log" do
shared_examples 'repository log' do
let(:commit_with_old_name) do
- Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id)
+ Gitlab::Git::Commit.find(repository, @commit_with_old_name_id)
end
let(:commit_with_new_name) do
- Gitlab::Git::Commit.decorate(repository, @commit_with_new_name_id)
+ Gitlab::Git::Commit.find(repository, @commit_with_new_name_id)
end
let(:rename_commit) do
- Gitlab::Git::Commit.decorate(repository, @rename_commit_id)
+ Gitlab::Git::Commit.find(repository, @rename_commit_id)
end
before(:context) do
# Add new commits so that there's a renamed file in the commit history
repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- @commit_with_old_name_id = new_commit_edit_old_file(repo)
- @rename_commit_id = new_commit_move_file(repo)
- @commit_with_new_name_id = new_commit_edit_new_file(repo)
+ @commit_with_old_name_id = new_commit_edit_old_file(repo).oid
+ @rename_commit_id = new_commit_move_file(repo).oid
+ @commit_with_new_name_id = new_commit_edit_new_file(repo).oid
end
after(:context) do
@@ -855,8 +855,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
def commit_files(commit)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- commit.rugged_diff_from_parent.deltas.flat_map do |delta|
- [delta.old_file[:path], delta.new_file[:path]].uniq.compact
+ commit.deltas.flat_map do |delta|
+ [delta.old_path, delta.new_path].uniq.compact
end
end
end
@@ -893,10 +893,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'when Gitaly find_commits feature is enabled' do
it_behaves_like 'repository log'
end
-
- context 'when Gitaly find_commits feature is disabled', :disable_gitaly do
- it_behaves_like 'repository log'
- end
end
describe '#count_commits_between' do
@@ -1441,31 +1437,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#batch_existence' do
- let(:refs) { ['deadbeef', SeedRepo::RubyBlob::ID, '909e6157199'] }
-
- around do |example|
- # TODO #batch_existence isn't used anywhere, can we remove it?
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it 'returns existing refs back' do
- result = repository.batch_existence(refs)
-
- expect(result).to eq([SeedRepo::RubyBlob::ID])
- end
-
- context 'existing: true' do
- it 'inverts meaning and returns non-existing refs' do
- result = repository.batch_existence(refs, existing: false)
-
- expect(result).to eq(%w(deadbeef 909e6157199))
- end
- end
- end
-
describe '#local_branches' do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
diff --git a/spec/lib/gitlab/hook_data/base_builder_spec.rb b/spec/lib/gitlab/hook_data/base_builder_spec.rb
new file mode 100644
index 00000000000..a921dd766c3
--- /dev/null
+++ b/spec/lib/gitlab/hook_data/base_builder_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe Gitlab::HookData::BaseBuilder do
+ describe '#absolute_image_urls' do
+ let(:subclass) do
+ Class.new(described_class) do
+ public :absolute_image_urls
+ end
+ end
+
+ subject { subclass.new(nil) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where do
+ {
+ 'relative image URL' => {
+ input: '![an image](foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
+ },
+ 'HTTP URL' => {
+ input: '![an image](http://example.com/foo.png)',
+ output: '![an image](http://example.com/foo.png)'
+ },
+ 'HTTPS URL' => {
+ input: '![an image](https://example.com/foo.png)',
+ output: '![an image](https://example.com/foo.png)'
+ },
+ 'protocol-relative URL' => {
+ input: '![an image](//example.com/foo.png)',
+ output: '![an image](//example.com/foo.png)'
+ },
+ 'URL reference by title' => {
+ input: "![foo]\n\n[foo]: foo.png",
+ output: "![foo]\n\n[foo]: foo.png"
+ },
+ 'URL reference by label' => {
+ input: "![][foo]\n\n[foo]: foo.png",
+ output: "![][foo]\n\n[foo]: foo.png"
+ },
+ 'in Markdown inline code block' => {
+ input: '`![an image](foo.png)`',
+ output: "`![an image](#{Gitlab.config.gitlab.url}/foo.png)`"
+ },
+ 'in HTML tag on the same line' => {
+ input: '<p>![an image](foo.png)</p>',
+ output: "<p>![an image](#{Gitlab.config.gitlab.url}/foo.png)</p>"
+ },
+ 'in Markdown multi-line code block' => {
+ input: "```\n![an image](foo.png)\n```",
+ output: "```\n![an image](foo.png)\n```"
+ },
+ 'in HTML tag on different lines' => {
+ input: "<p>\n![an image](foo.png)\n</p>",
+ output: "<p>\n![an image](foo.png)\n</p>"
+ }
+ }
+ end
+
+ with_them do
+ it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index 506b2c0be20..60093474f8a 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -40,5 +40,14 @@ describe Gitlab::HookData::IssueBuilder do
expect(data).to include(:human_total_time_spent)
expect(data).to include(:assignee_ids)
end
+
+ context 'when the issue has an image in the description' do
+ let(:issue_with_description) { create(:issue, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(issue_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index b61614e4790..dd586af6118 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -56,5 +56,14 @@ describe Gitlab::HookData::MergeRequestBuilder do
expect(data).to include(:human_time_estimate)
expect(data).to include(:human_total_time_spent)
end
+
+ context 'when the MR has an image in the description' do
+ let(:mr_with_description) { create(:merge_request, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(mr_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 58b9fb06cc5..265937f899e 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::FileImporter do
let(:symlink_file) { "#{shared.export_path}/invalid.json" }
let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
+ let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
before do
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
@@ -34,6 +35,10 @@ describe Gitlab::ImportExport::FileImporter do
expect(File.exist?(hidden_symlink_file)).to be false
end
+ it 'removes evil symlinks in root folder' do
+ expect(File.exist?(evil_symlink_file)).to be false
+ end
+
it 'removes symlinks in subfolders' do
expect(File.exist?(subfolder_symlink_file)).to be false
end
@@ -75,5 +80,7 @@ describe Gitlab::ImportExport::FileImporter do
FileUtils.touch(valid_file)
FileUtils.ln_s(valid_file, symlink_file)
FileUtils.ln_s(valid_file, subfolder_symlink_file)
+ FileUtils.ln_s(valid_file, hidden_symlink_file)
+ FileUtils.ln_s(valid_file, evil_symlink_file)
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 581132b6672..ff1a5aa2536 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -1366,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')
@@ -1377,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/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index ac34efa4f9d..a30e6c23ac9 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -6,11 +6,12 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_
describe MigrateProcessCommitWorkerJobs do
let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:commit) do
+ let(:rugged) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- project.commit.raw.rugged_commit
+ project.repository.rugged
end
end
+ let(:commit) { rugged.rev_parse(project.commit.id) }
describe 'Project' do
describe 'find_including_path' do
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index ccc3ff861c5..0aee78ac12d 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -82,6 +82,14 @@ describe MergeRequestDiff do
diff.diffs
end
+
+ it 'returns persisted diffs if diff refs does not exist' do
+ expect(diff).to receive(:load_diffs)
+
+ diff.update!(start_commit_sha: nil, base_commit_sha: nil)
+
+ diff.diffs
+ end
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index a544940800a..528f5b610d7 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -189,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/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 1c765ceac2f..63850939be1 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -554,6 +554,16 @@ describe WikiPage do
end
end
+ describe '#hook_attrs' do
+ it 'adds absolute urls for images in the content' do
+ create_page("test page", "test![WikiPage_Image](/uploads/abc/WikiPage_Image.png)")
+ page = wiki.wiki.page(title: "test page")
+ wiki_page = described_class.new(wiki, page, true)
+
+ expect(wiki_page.hook_attrs['content']).to eq("test![WikiPage_Image](#{Settings.gitlab.url}/uploads/abc/WikiPage_Image.png)")
+ end
+ end
+
private
def remove_temp_repo(path)
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 32d9807f06a..c228bd2393b 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -125,7 +125,8 @@ module CycleAnalyticsHelpers
_, opts = args
commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- raw_repository.commit(branch_update.newrev).rugged_commit
+ rugged = raw_repository.rugged
+ rugged.rev_parse(branch_update.newrev)
end
branch_update.newrev = commit.amend(
diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb
index 15fce65979b..b56940a9613 100644
--- a/spec/views/projects/_home_panel.html.haml_spec.rb
+++ b/spec/views/projects/_home_panel.html.haml_spec.rb
@@ -1,55 +1,48 @@
require 'spec_helper'
describe 'projects/_home_panel' do
- let(:group) { create(:group) }
- let(:project) { create(:project, :public, namespace: group) }
+ context 'notifications' do
+ let(:project) { create(:project) }
- let(:notification_settings) do
- user&.notification_settings_for(project)
- end
+ before do
+ assign(:project, project)
- before do
- assign(:project, project)
- assign(:notification_setting, notification_settings)
+ allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+ end
- allow(view).to receive(:current_user).and_return(user)
- allow(view).to receive(:can?).and_return(false)
- end
+ context 'when user is signed in' do
+ let(:user) { create(:user) }
- context 'when user is signed in' do
- let(:user) { create(:user) }
+ before do
+ notification_settings = user.notification_settings_for(project)
+ assign(:notification_setting, notification_settings)
+ end
- it 'makes it possible to set notification level' do
- render
+ it 'makes it possible to set notification level' do
+ render
- expect(view).to render_template('shared/notifications/_button')
- expect(rendered).to have_selector('.notification-dropdown')
+ expect(view).to render_template('shared/notifications/_button')
+ expect(rendered).to have_selector('.notification-dropdown')
+ end
end
- end
-
- context 'when user is signed out' do
- let(:user) { nil }
- it 'is not possible to set notification level' do
- render
+ context 'when user is signed out' do
+ let(:user) { nil }
- expect(rendered).not_to have_selector('.notification_dropdown')
- end
- end
-
- context 'when project' do
- let!(:user) { create(:user) }
- let(:badges) { project.badges }
+ before do
+ assign(:notification_setting, nil)
+ end
- context 'has no badges' do
- it 'should not render any badge' do
+ it 'is not possible to set notification level' do
render
- expect(rendered).to have_selector('.project-badges')
- expect(rendered).not_to have_selector('.project-badges > a')
+ expect(rendered).not_to have_selector('.notification_dropdown')
end
end
+ end
+ context 'badges' do
shared_examples 'show badges' do
it 'should render the all badges' do
render
@@ -62,7 +55,31 @@ describe 'projects/_home_panel' do
end
end
+ let(:user) { create(:user) }
+ let(:badges) { project.badges }
+
+ before do
+ assign(:project, project)
+
+ allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+ end
+
+ context 'has no badges' do
+ let(:project) { create(:project) }
+
+ it 'should not render any badge' do
+ render
+
+ expect(rendered).to have_selector('.project-badges')
+ expect(rendered).not_to have_selector('.project-badges > a')
+ end
+ end
+
context 'only has group badges' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
before do
create(:group_badge, group: project.group)
end
@@ -71,6 +88,8 @@ describe 'projects/_home_panel' do
end
context 'only has project badges' do
+ let(:project) { create(:project) }
+
before do
create(:project_badge, project: project)
end
@@ -79,6 +98,9 @@ describe 'projects/_home_panel' do
end
context 'has both group and project badges' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
before do
create(:project_badge, project: project)
create(:group_badge, group: project.group)
@@ -87,4 +109,35 @@ describe 'projects/_home_panel' do
it_behaves_like 'show badges'
end
end
+
+ context 'project id' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ assign(:project, project)
+
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ context 'user can read project' do
+ it 'is shown' do
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(true)
+
+ render
+
+ expect(rendered).to have_content("Project ID: #{project.id}")
+ end
+ end
+
+ context 'user cannot read project' do
+ it 'is not shown' do
+ allow(view).to receive(:can?).with(user, :read_project, project).and_return(false)
+
+ render
+
+ expect(rendered).not_to have_content("Project ID: #{project.id}")
+ end
+ end
+ end
end