summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.gitlab/issue_templates/Security developer workflow.md8
-rw-r--r--.rubocop.yml6
-rw-r--r--.rubocop_todo.yml6
-rw-r--r--CONTRIBUTING.md723
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock23
-rw-r--r--Gemfile.rails5.lock5
-rw-r--r--PROCESS.md84
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js8
-rw-r--r--app/assets/javascripts/boards/models/list.js3
-rw-r--r--app/assets/javascripts/commons/polyfills.js1
-rw-r--r--app/assets/javascripts/diffs/components/app.vue12
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue16
-rw-r--r--app/assets/javascripts/diffs/constants.js3
-rw-r--r--app/assets/javascripts/diffs/store/actions.js21
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js43
-rw-r--r--app/assets/javascripts/emoji/support/unicode_support_map.js2
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue2
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/button.vue11
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue31
-rw-r--r--app/assets/javascripts/ide/ide_router.js100
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js3
-rw-r--r--app/assets/javascripts/ide/stores/actions/merge_request.js68
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js32
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/actions.js3
-rw-r--r--app/assets/javascripts/jobs/components/artifacts_block.vue98
-rw-r--r--app/assets/javascripts/jobs/components/erased_block.vue48
-rw-r--r--app/assets/javascripts/jobs/components/job_log.vue33
-rw-r--r--app/assets/javascripts/jobs/components/sidebar_details_block.vue38
-rw-r--r--app/assets/javascripts/jobs/components/stuck_block.vue63
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js2
-rw-r--r--app/assets/javascripts/labels_select.js9
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/show/index.js8
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js94
-rw-r--r--app/assets/javascripts/reports/components/grouped_test_reports_app.vue8
-rw-r--r--app/assets/javascripts/reports/components/issue_body.js (renamed from app/assets/javascripts/vue_shared/components/reports/issue_body.js)2
-rw-r--r--app/assets/javascripts/reports/components/issue_status_icon.vue (renamed from app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue)3
-rw-r--r--app/assets/javascripts/reports/components/issues_list.vue (renamed from app/assets/javascripts/vue_shared/components/reports/issues_list.vue)4
-rw-r--r--app/assets/javascripts/reports/components/modal_open_name.vue (renamed from app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue)0
-rw-r--r--app/assets/javascripts/reports/components/report_issues.vue (renamed from app/assets/javascripts/vue_shared/components/reports/report_issues.vue)4
-rw-r--r--app/assets/javascripts/reports/components/report_link.vue (renamed from app/assets/javascripts/vue_shared/components/reports/report_link.vue)0
-rw-r--r--app/assets/javascripts/reports/components/report_section.vue (renamed from app/assets/javascripts/vue_shared/components/reports/report_section.vue)2
-rw-r--r--app/assets/javascripts/reports/components/summary_row.vue (renamed from app/assets/javascripts/vue_shared/components/reports/summary_row.vue)2
-rw-r--r--app/assets/javascripts/reports/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/reports/constants.js3
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss3
-rw-r--r--app/assets/stylesheets/framework.scss1
-rw-r--r--app/assets/stylesheets/framework/avatar.scss1
-rw-r--r--app/assets/stylesheets/framework/buttons.scss6
-rw-r--r--app/assets/stylesheets/framework/common.scss2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/framework/emojis.scss2
-rw-r--r--app/assets/stylesheets/framework/files.scss6
-rw-r--r--app/assets/stylesheets/framework/filters.scss4
-rw-r--r--app/assets/stylesheets/framework/flash.scss4
-rw-r--r--app/assets/stylesheets/framework/forms.scss4
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss2
-rw-r--r--app/assets/stylesheets/framework/modal.scss1
-rw-r--r--app/assets/stylesheets/framework/selects.scss2
-rw-r--r--app/assets/stylesheets/framework/typography.scss10
-rw-r--r--app/assets/stylesheets/framework/variables.scss37
-rw-r--r--app/assets/stylesheets/framework/variables_overrides.scss16
-rw-r--r--app/assets/stylesheets/framework/zen.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss2
-rw-r--r--app/assets/stylesheets/pages/boards.scss2
-rw-r--r--app/assets/stylesheets/pages/builds.scss2
-rw-r--r--app/assets/stylesheets/pages/commits.scss4
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss2
-rw-r--r--app/assets/stylesheets/pages/diff.scss4
-rw-r--r--app/assets/stylesheets/pages/editor.scss25
-rw-r--r--app/assets/stylesheets/pages/environments.scss4
-rw-r--r--app/assets/stylesheets/pages/graph.scss4
-rw-r--r--app/assets/stylesheets/pages/groups.scss3
-rw-r--r--app/assets/stylesheets/pages/issuable.scss10
-rw-r--r--app/assets/stylesheets/pages/issues.scss4
-rw-r--r--app/assets/stylesheets/pages/labels.scss9
-rw-r--r--app/assets/stylesheets/pages/milestone.scss2
-rw-r--r--app/assets/stylesheets/pages/note_form.scss10
-rw-r--r--app/assets/stylesheets/pages/notes.scss28
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss23
-rw-r--r--app/assets/stylesheets/pages/search.scss6
-rw-r--r--app/assets/stylesheets/pages/settings.scss14
-rw-r--r--app/assets/stylesheets/performance_bar.scss2
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/notification_settings_controller.rb8
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb11
-rw-r--r--app/helpers/avatars_helper.rb52
-rw-r--r--app/helpers/groups_helper.rb5
-rw-r--r--app/helpers/icons_helper.rb2
-rw-r--r--app/helpers/mirror_helper.rb5
-rw-r--r--app/helpers/notifications_helper.rb12
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/ci/job_artifact.rb2
-rw-r--r--app/models/commit.rb4
-rw-r--r--app/models/notification_setting.rb9
-rw-r--r--app/models/programming_language.rb2
-rw-r--r--app/models/project.rb22
-rw-r--r--app/models/project_services/asana_service.rb2
-rw-r--r--app/models/project_services/assembla_service.rb2
-rw-r--r--app/models/project_services/bamboo_service.rb2
-rw-r--r--app/models/project_services/bugzilla_service.rb2
-rw-r--r--app/models/project_services/buildkite_service.rb2
-rw-r--r--app/models/project_services/builds_email_service.rb2
-rw-r--r--app/models/project_services/campfire_service.rb6
-rw-r--r--app/models/project_services/chat_message/base_message.rb2
-rw-r--r--app/models/project_services/chat_message/issue_message.rb2
-rw-r--r--app/models/project_services/chat_message/merge_message.rb2
-rw-r--r--app/models/project_services/chat_message/note_message.rb2
-rw-r--r--app/models/project_services/chat_message/pipeline_message.rb2
-rw-r--r--app/models/project_services/chat_message/push_message.rb2
-rw-r--r--app/models/project_services/chat_message/wiki_page_message.rb2
-rw-r--r--app/models/project_services/chat_notification_service.rb2
-rw-r--r--app/models/project_services/ci_service.rb2
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/deployment_service.rb2
-rw-r--r--app/models/project_services/drone_ci_service.rb2
-rw-r--r--app/models/project_services/emails_on_push_service.rb2
-rw-r--r--app/models/project_services/external_wiki_service.rb2
-rw-r--r--app/models/project_services/flowdock_service.rb2
-rw-r--r--app/models/project_services/gemnasium_service.rb2
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/hangouts_chat_service.rb2
-rw-r--r--app/models/project_services/hipchat_service.rb22
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb2
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/project_services/kubernetes_service.rb2
-rw-r--r--app/models/project_services/mattermost_service.rb2
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb2
-rw-r--r--app/models/project_services/microsoft_teams_service.rb2
-rw-r--r--app/models/project_services/mock_ci_service.rb2
-rw-r--r--app/models/project_services/mock_deployment_service.rb2
-rw-r--r--app/models/project_services/mock_monitoring_service.rb2
-rw-r--r--app/models/project_services/monitoring_service.rb2
-rw-r--r--app/models/project_services/packagist_service.rb2
-rw-r--r--app/models/project_services/pipelines_email_service.rb2
-rw-r--r--app/models/project_services/pivotaltracker_service.rb2
-rw-r--r--app/models/project_services/prometheus_service.rb2
-rw-r--r--app/models/project_services/pushover_service.rb4
-rw-r--r--app/models/project_services/redmine_service.rb2
-rw-r--r--app/models/project_services/slack_service.rb2
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb2
-rw-r--r--app/models/project_services/slash_commands_service.rb2
-rw-r--r--app/models/project_services/teamcity_service.rb2
-rw-r--r--app/models/protected_branch/merge_access_level.rb2
-rw-r--r--app/models/protected_branch/push_access_level.rb2
-rw-r--r--app/models/protected_tag/create_access_level.rb2
-rw-r--r--app/models/repository_language.rb2
-rw-r--r--app/models/site_statistic.rb2
-rw-r--r--app/models/storage/hashed_project.rb2
-rw-r--r--app/models/storage/legacy_project.rb2
-rw-r--r--app/models/system_note_metadata.rb2
-rw-r--r--app/models/user.rb19
-rw-r--r--app/policies/commit_policy.rb5
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--app/presenters/ci/build_runner_presenter.rb2
-rw-r--r--app/serializers/environment_entity.rb11
-rw-r--r--app/serializers/project_mirror_serializer.rb5
-rw-r--r--app/serializers/test_case_entity.rb2
-rw-r--r--app/serializers/test_reports_comparer_entity.rb2
-rw-r--r--app/serializers/test_reports_comparer_serializer.rb2
-rw-r--r--app/serializers/test_suite_comparer_entity.rb2
-rw-r--r--app/services/ci/enqueue_build_service.rb8
-rw-r--r--app/services/ci/process_pipeline_service.rb6
-rw-r--r--app/services/commits/tag_service.rb26
-rw-r--r--app/services/notes/quick_actions_service.rb3
-rw-r--r--app/services/notification_recipient_service.rb12
-rw-r--r--app/services/preview_markdown_service.rb12
-rw-r--r--app/services/projects/autocomplete_service.rb10
-rw-r--r--app/services/projects/detect_repository_languages_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb47
-rw-r--r--app/services/quick_actions/target_service.rb30
-rw-r--r--app/services/system_note_service.rb15
-rw-r--r--app/services/tags/create_service.rb2
-rw-r--r--app/services/todos/destroy/base_service.rb2
-rw-r--r--app/services/todos/destroy/confidential_issue_service.rb2
-rw-r--r--app/services/todos/destroy/entity_leave_service.rb2
-rw-r--r--app/services/todos/destroy/group_private_service.rb2
-rw-r--r--app/services/todos/destroy/private_features_service.rb2
-rw-r--r--app/services/todos/destroy/project_private_service.rb2
-rw-r--r--app/views/admin/spam_logs/index.html.haml2
-rw-r--r--app/views/admin/users/show.html.haml12
-rw-r--r--app/views/projects/blob/_editor.html.haml8
-rw-r--r--app/views/projects/forks/_fork_button.html.haml4
-rw-r--r--app/views/projects/jobs/_sidebar.html.haml7
-rw-r--r--app/views/projects/mirrors/_instructions.html.haml15
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml63
-rw-r--r--app/views/projects/mirrors/_mirror_repos_form.html.haml18
-rw-r--r--app/views/projects/mirrors/_push.html.haml50
-rw-r--r--app/views/projects/mirrors/_show.html.haml4
-rw-r--r--app/views/projects/pages/_use.html.haml4
-rw-r--r--app/views/search/results/_blob.html.haml2
-rw-r--r--app/views/shared/_remote_mirror_update_button.html.haml19
-rw-r--r--app/views/shared/milestones/_issuables.html.haml4
-rw-r--r--app/views/shared/notifications/_custom_notifications.html.haml2
-rw-r--r--app/views/shared/plugins/_index.html.haml4
-rw-r--r--app/views/shared/runners/_form.html.haml30
-rw-r--r--app/workers/detect_repository_languages_worker.rb2
-rw-r--r--app/workers/todos_destroyer/confidential_issue_worker.rb2
-rw-r--r--app/workers/todos_destroyer/entity_leave_worker.rb2
-rw-r--r--app/workers/todos_destroyer/group_private_worker.rb2
-rw-r--r--app/workers/todos_destroyer/private_features_worker.rb2
-rw-r--r--app/workers/todos_destroyer/project_private_worker.rb2
-rwxr-xr-xbin/secpick4
-rw-r--r--changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml4
-rw-r--r--changelogs/unreleased/25990-web-terminal-improvements.yml5
-rw-r--r--changelogs/unreleased/2747-protected-environments-backend-ce.yml5
-rw-r--r--changelogs/unreleased/28930-add-project-reference-filter.yml5
-rw-r--r--changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml5
-rw-r--r--changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml5
-rw-r--r--changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml5
-rw-r--r--changelogs/unreleased/48320-cancel-a-created-job.yml5
-rw-r--r--changelogs/unreleased/48942-rename-backlog-list-to-open-issue-boards.yml5
-rw-r--r--changelogs/unreleased/48967-disable-statement-timeout.yml5
-rw-r--r--changelogs/unreleased/49905-fix-checkboxes-runners.yml5
-rw-r--r--changelogs/unreleased/50047-spam-logs-pagination.yml5
-rw-r--r--changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml5
-rw-r--r--changelogs/unreleased/50101-aritfacts-block.yml5
-rw-r--r--changelogs/unreleased/50101-erased-block.yml5
-rw-r--r--changelogs/unreleased/50101-job-log-component.yml5
-rw-r--r--changelogs/unreleased/50101-stuck-component.yml5
-rw-r--r--changelogs/unreleased/50126-blocked-user-card.yml5
-rw-r--r--changelogs/unreleased/50180-fa-icon-google-audit.yml5
-rw-r--r--changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml6
-rw-r--r--changelogs/unreleased/50257-fix-auto-devops-glibc-pubkey-url.yml5
-rw-r--r--changelogs/unreleased/50281-js-pages-do-not-load-on-windows-8-ie-11.yml5
-rw-r--r--changelogs/unreleased/add_google_noto_color_emoji_font.yml5
-rw-r--r--changelogs/unreleased/auto-devops-gitlab-ci-glic-228.yml5
-rw-r--r--changelogs/unreleased/bvl-merge-base-api.yml5
-rw-r--r--changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml5
-rw-r--r--changelogs/unreleased/expose-all-artifacts-sizes-in-jobs-api.yml5
-rw-r--r--changelogs/unreleased/feat-add-default-avatar-to-group.yml5
-rw-r--r--changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml5
-rw-r--r--changelogs/unreleased/fix-pipeline-fixture-seeder.yml5
-rw-r--r--changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml5
-rw-r--r--changelogs/unreleased/frozen-string-enable-app-vestigial.yml5
-rw-r--r--changelogs/unreleased/gitaly-install-path.yml5
-rw-r--r--changelogs/unreleased/ide-header-buttons-tooltip.yml5
-rw-r--r--changelogs/unreleased/ide-open-empty-merge-request.yml5
-rw-r--r--changelogs/unreleased/mk-bump-rainbow-gem.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml5
-rw-r--r--changelogs/unreleased/sh-bump-rugged-0-27-4.yml5
-rw-r--r--changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml5
-rw-r--r--changelogs/unreleased/tz-mr-incremental-rendering.yml4
-rw-r--r--changelogs/unreleased/visual-improvements-language-bar.yml5
-rw-r--r--db/fixtures/development/14_pipelines.rb63
-rw-r--r--db/fixtures/development/23_spam_logs.rb32
-rw-r--r--db/migrate/20160317092222_add_moved_to_to_issue.rb2
-rw-r--r--db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb8
-rw-r--r--db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb5
-rw-r--r--db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb26
-rw-r--r--db/migrate/20180113220114_rework_redirect_routes_indexes.rb66
-rw-r--r--db/migrate/20180403035759_create_project_ci_cd_settings.rb16
-rw-r--r--db/migrate/20180420010616_cleanup_build_stage_migration.rb76
-rw-r--r--db/migrate/20180504195842_project_name_lower_index.rb18
-rw-r--r--db/migrate/20180702124358_remove_orphaned_routes.rb20
-rw-r--r--db/optional_migrations/composite_primary_keys.rb14
-rw-r--r--db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb6
-rw-r--r--db/post_migrate/20170503004427_update_retried_for_ci_build.rb6
-rw-r--r--db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb18
-rw-r--r--db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb16
-rw-r--r--db/post_migrate/20170526185842_migrate_pipeline_stages.rb24
-rw-r--r--db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb14
-rw-r--r--db/post_migrate/20170711145558_migrate_stages_statuses.rb8
-rw-r--r--db/post_migrate/20171207150343_remove_soft_removed_objects.rb12
-rw-r--r--db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb40
-rw-r--r--db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb16
-rw-r--r--db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb8
-rw-r--r--db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb14
-rw-r--r--db/post_migrate/20180420080616_schedule_stages_index_migration.rb14
-rw-r--r--db/post_migrate/20180604123514_cleanup_stages_position_migration.rb36
-rw-r--r--doc/api/README.md21
-rw-r--r--doc/api/boards.md2
-rw-r--r--doc/api/events.md2
-rw-r--r--doc/api/group_boards.md2
-rw-r--r--doc/api/jobs.md28
-rw-r--r--doc/api/notification_settings.md2
-rw-r--r--doc/api/projects.md3
-rw-r--r--doc/api/repositories.md36
-rw-r--r--doc/api/system_hooks.md5
-rw-r--r--doc/development/contributing/design.md63
-rw-r--r--doc/development/contributing/index.md246
-rw-r--r--doc/development/contributing/issue_workflow.md357
-rw-r--r--doc/development/contributing/merge_request_workflow.md191
-rw-r--r--doc/development/diffs.md17
-rw-r--r--doc/development/migration_style_guide.md28
-rw-r--r--doc/development/new_fe_guide/development/testing.md24
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/integration/bitbucket.md2
-rw-r--r--doc/integration/oauth_provider.md4
-rw-r--r--doc/user/markdown.md5
-rw-r--r--doc/user/project/bulk_editing.md22
-rw-r--r--doc/user/project/img/bulk-editing.pngbin0 -> 197686 bytes
-rw-r--r--doc/user/project/img/issue_board.pngbin100684 -> 327718 bytes
-rw-r--r--doc/user/project/import/bitbucket.md27
-rw-r--r--doc/user/project/import/bitbucket_server.md75
-rw-r--r--doc/user/project/import/img/bitbucket_import_new_project.pngbin1316 -> 0 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_credentials.pngbin0 -> 40566 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_select_project.pngbin0 -> 56750 bytes
-rw-r--r--doc/user/project/import/img/import_projects_from_new_project_page.pngbin36821 -> 81639 bytes
-rw-r--r--doc/user/project/integrations/webhooks.md1
-rw-r--r--doc/user/project/issue_board.md8
-rw-r--r--doc/user/project/quick_actions.md7
-rw-r--r--doc/user/project/web_ide/img/admin_clientside_evaluation.pngbin0 -> 9342 bytes
-rw-r--r--doc/user/project/web_ide/img/clientside_evaluation.pngbin0 -> 60256 bytes
-rw-r--r--doc/user/project/web_ide/index.md34
-rw-r--r--lib/api/entities.rb8
-rw-r--r--lib/api/jobs.rb4
-rw-r--r--lib/api/notification_settings.rb8
-rw-r--r--lib/api/repositories.rb33
-rw-r--r--lib/banzai/filter/project_reference_filter.rb117
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/banzai/reference_parser/project_parser.rb30
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb23
-rw-r--r--lib/gitlab/database/migration_helpers.rb103
-rw-r--r--lib/gitlab/git.rb8
-rw-r--r--lib/gitlab/git/merge_base.rb44
-rw-r--r--lib/gitlab/git/repository.rb15
-rw-r--r--lib/gitlab/import_export/relation_factory.rb6
-rw-r--r--lib/gitlab/setup_helper.rb13
-rw-r--r--lib/tasks/gitlab/gitaly.rake24
-rw-r--r--locale/gitlab.pot135
-rw-r--r--locale/unfound_translations.rb16
-rw-r--r--qa/qa/specs/features/project/auto_devops_spec.rb2
-rw-r--r--rubocop/cop/migration/add_reference.rb49
-rw-r--r--rubocop/rubocop.rb1
-rw-r--r--spec/controllers/application_controller_spec.rb4
-rw-r--r--spec/controllers/notification_settings_controller_spec.rb9
-rw-r--r--spec/factories/uploads.rb7
-rw-r--r--spec/features/admin/admin_users_spec.rb15
-rw-r--r--spec/features/boards/boards_spec.rb2
-rw-r--r--spec/features/commits/user_uses_slash_commands_spec.rb48
-rw-r--r--spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb2
-rw-r--r--spec/features/projects/blobs/shortcuts_blob_spec.rb4
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb3
-rw-r--r--spec/features/projects/files/user_deletes_files_spec.rb6
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb22
-rw-r--r--spec/features/projects/files/user_replaces_files_spec.rb8
-rw-r--r--spec/features/projects/remote_mirror_spec.rb10
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb5
-rw-r--r--spec/helpers/avatars_helper_spec.rb65
-rw-r--r--spec/helpers/groups_helper_spec.rb13
-rw-r--r--spec/helpers/icons_helper_spec.rb20
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js10
-rw-r--r--spec/javascripts/diffs/mock_data/diff_file.js1
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js18
-rw-r--r--spec/javascripts/ide/components/new_dropdown/button_spec.js16
-rw-r--r--spec/javascripts/ide/components/preview/clientside_spec.js6
-rw-r--r--spec/javascripts/ide/stores/actions/merge_request_spec.js101
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js52
-rw-r--r--spec/javascripts/ide/stores/modules/branches/actions_spec.js16
-rw-r--r--spec/javascripts/jobs/artifacts_block_spec.js120
-rw-r--r--spec/javascripts/jobs/erased_block_spec.js56
-rw-r--r--spec/javascripts/jobs/job_log_spec.js45
-rw-r--r--spec/javascripts/jobs/sidebar_details_block_spec.js67
-rw-r--r--spec/javascripts/jobs/stuck_block_spec.js81
-rw-r--r--spec/javascripts/reports/components/modal_open_name_spec.js (renamed from spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js)2
-rw-r--r--spec/javascripts/reports/components/report_link_spec.js (renamed from spec/javascripts/vue_shared/components/reports/report_link_spec.js)4
-rw-r--r--spec/javascripts/reports/components/report_section_spec.js (renamed from spec/javascripts/vue_shared/components/reports/report_section_spec.js)2
-rw-r--r--spec/javascripts/reports/components/summary_row_spec.js (renamed from spec/javascripts/vue_shared/components/reports/summary_row_spec.js)2
-rw-r--r--spec/lib/banzai/filter/project_reference_filter_spec.rb85
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb85
-rw-r--r--spec/lib/banzai/reference_parser/project_parser_spec.rb50
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb75
-rw-r--r--spec/lib/gitlab/cleanup/project_uploads_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb86
-rw-r--r--spec/lib/gitlab/git/merge_base_spec.rb95
-rw-r--r--spec/models/ci/build_spec.rb6
-rw-r--r--spec/models/notification_setting_spec.rb36
-rw-r--r--spec/models/project_spec.rb9
-rw-r--r--spec/models/repository_spec.rb42
-rw-r--r--spec/requests/api/jobs_spec.rb74
-rw-r--r--spec/requests/api/repositories_spec.rb73
-rw-r--r--spec/rubocop/cop/migration/add_reference_spec.rb54
-rw-r--r--spec/serializers/project_mirror_serializer_spec.rb7
-rw-r--r--spec/services/ci/enqueue_build_service_spec.rb16
-rw-r--r--spec/services/commits/tag_service_spec.rb102
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb46
-rw-r--r--spec/services/preview_markdown_service_spec.rb25
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb107
-rw-r--r--spec/services/quick_actions/target_service_spec.rb83
-rw-r--r--spec/services/system_note_service_spec.rb19
-rw-r--r--spec/support/banzai/reference_filter_shared_examples.rb73
-rw-r--r--spec/support/helpers/features/notes_helpers.rb7
-rw-r--r--spec/support/helpers/test_env.rb12
-rw-r--r--spec/support/import_export/configuration_helper.rb2
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb67
-rw-r--r--spec/views/shared/notes/_form.html.haml_spec.rb10
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml31
-rw-r--r--vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml13
395 files changed, 5647 insertions, 1916 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fcf47421b01..fd02d72b4c2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -453,6 +453,7 @@ danger-review:
- master
variables:
- $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
+ - $CI_COMMIT_REF_NAME =~ /.*-stable(-ee)?-prepare-.*/
script:
- git version
- danger --fail-on-errors=true
diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md
index c1f702e9385..64b54b171f7 100644
--- a/.gitlab/issue_templates/Security developer workflow.md
+++ b/.gitlab/issue_templates/Security developer workflow.md
@@ -12,7 +12,7 @@ Set the title to: `[Security] Description of the original issue`
- [ ] Link to the original issue adding it to the [links section](#links)
- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org`
- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-`
-- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
+- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
- [ ] Add a link to the MR to the [links section](#links)
- [ ] Add a link to an EE MR if required
- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
@@ -22,13 +22,13 @@ Set the title to: `[Security] Description of the original issue`
- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases
- [ ] At this point, it might be easy to squash the commits from the MR into one
- - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation]
+ - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [secpick documentation]
- [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
- [ ] Create each MR targetting the security branch `security-X-Y`
- [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager.
-[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
+[secpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script
#### Documentation and final details
@@ -68,4 +68,4 @@ Set the title to: `[Security] Description of the original issue`
[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md
[RM list]: https://about.gitlab.com/release-managers/
-/label ~security
+/label ~security
diff --git a/.rubocop.yml b/.rubocop.yml
index c8b1ce327e2..9858bbe0ddd 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -31,6 +31,10 @@ Style/MutableConstant:
- 'ee/db/post_migrate/**/*'
- 'ee/db/geo/migrate/**/*'
+# TODO: Move this to gitlab-styles
+Style/SafeNavigation:
+ Enabled: false
+
Naming/FileName:
ExpectMatchingDefinition: true
Exclude:
@@ -44,6 +48,8 @@ Naming/FileName:
- 'qa/bin/*'
- 'config/**/*'
- 'lib/generators/**/*'
+ - 'locale/unfound_translations.rb'
+ - 'ee/locale/unfound_translations.rb'
- 'ee/lib/generators/**/*'
IgnoreExecutableScripts: true
AllowedAcronyms:
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 8a1ca6747a8..54e3b8217d8 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -706,12 +706,6 @@ Style/RescueModifier:
Style/RescueStandardError:
Enabled: false
-# Offense count: 92
-# Cop supports --auto-correct.
-# Configuration parameters: ConvertCodeThatCanStartToReturnNil.
-Style/SafeNavigation:
- Enabled: false
-
# Offense count: 8
# Cop supports --auto-correct.
Style/SelfAssignment:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0bf8cba76f3..e68e3b9cab0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -21,41 +21,47 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+- [Contributing Documentation has been moved](#contributing-documentation-has-been-moved)
- [Contribute to GitLab](#contribute-to-gitlab)
- [Security vulnerability disclosure](#security-vulnerability-disclosure)
+- [Code of conduct](#code-of-conduct)
- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
- [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute)
+- [Contribution Flow](#contribution-flow)
- [Workflow labels](#workflow-labels)
- - [Type labels](#type-labels)
- - [Subject labels](#subject-labels)
- - [Team labels](#team-labels)
- - [Release Scoping labels](#release-scoping-labels)
- - [Bug Priority labels](#bug-priority-labels)
- - [Bug Severity labels](#bug-severity-labels)
- - [Severity impact guidance](#severity-impact-guidance)
- - [Label for community contributors](#label-for-community-contributors)
-- [Implement design & UI elements](#implement-design-ui-elements)
+ - [Type labels](#type-labels)
+ - [Subject labels](#subject-labels)
+ - [Team labels](#team-labels)
+ - [Release Scoping labels](#release-scoping-labels)
+ - [Priority labels](#priority-labels)
+ - [Severity labels](#severity-labels)
+ - [Severity impact guidance](#severity-impact-guidance)
+ - [Label for community contributors](#label-for-community-contributors)
+- [Implement design & UI elements](#implement-design--ui-elements)
- [Issue tracker](#issue-tracker)
- - [Issue triaging](#issue-triaging)
- - [Feature proposals](#feature-proposals)
- - [Issue tracker guidelines](#issue-tracker-guidelines)
- - [Issue weight](#issue-weight)
- - [Regression issues](#regression-issues)
- - [Technical and UX debt](#technical-and-ux-debt)
- - [Stewardship](#stewardship)
+ - [Issue triaging](#issue-triaging)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical and UX debt](#technical-and-ux-debt)
+ - [Stewardship](#stewardship)
- [Merge requests](#merge-requests)
- - [Merge request guidelines](#merge-request-guidelines)
- - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
- [Definition of done](#definition-of-done)
- [Style guides](#style-guides)
-- [Code of conduct](#code-of-conduct)
-- [Contribution Flow](#contribution-flow)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
---
+## Contributing Documentation has been moved
+
+As of July 2018, all the documentation for contributing to the GitLab project has been moved to a new location.
+[view the new documentation](doc/development/contributing/index.md) to find the latest information.
+
## Contribute to GitLab
For a first-time step-by-step guide to the contribution process, see
@@ -84,6 +90,36 @@ Please report suspected security vulnerabilities in private to
Please do **NOT** create publicly viewable issues for suspected security
vulnerabilities.
+## Code of conduct
+
+As contributors and maintainers of this project, we pledge to respect all
+people who contribute through reporting issues, posting feature requests,
+updating documentation, submitting pull requests or patches, and other
+activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, or religion.
+
+Examples of unacceptable behavior by participants include the use of sexual
+language or imagery, derogatory comments or personal attacks, trolling, public
+or private harassment, insults, or other unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct. Project maintainers who do not
+follow the Code of Conduct may be removed from the project team.
+
+This code of conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior can be
+reported by emailing `contact@gitlab.com`.
+
+This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
+available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+
## Closing policy for issues and merge requests
GitLab is a popular open source project and the capacity to deal with issues
@@ -123,669 +159,164 @@ learn how to communicate with GitLab. If you're looking for a Gitter or Slack ch
please consider we favor
[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
-## Workflow labels
-
-To allow for asynchronous issue handling, we use [milestones][milestones-page]
-and [labels][labels-page]. Leads and product managers handle most of the
-scheduling into milestones. Labelling is a task for everyone.
-
-Most issues will have labels for at least one of the following:
+## Contribution Flow
-- Type: ~"feature proposal", ~bug, ~customer, etc.
-- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
-- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc.
-- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
-- Priority: ~P1, ~P2, ~P3, ~P4
-- Severity: ~S1, ~S2, ~S3, ~S4
+When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
-All labels, their meaning and priority are defined on the
-[labels page][labels-page].
+When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
-If you come across an issue that has none of these, and you're allowed to set
-labels, you can _always_ add the team and type, and often also the subject.
+When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
-[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
-[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
+Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
-### Type labels
+GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
-Type labels are very important. They define what kind of issue this is. Every
-issue should have one or more.
+GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
-Examples of type labels are ~"feature proposal", ~bug, ~customer, ~security,
-and ~"direction".
+When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
-A number of type labels have a priority assigned to them, which automatically
-makes them float to the top, depending on their importance.
+When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
-Type labels are always lowercase, and can have any color, besides blue (which is
-already reserved for subject labels).
+[core team]: https://about.gitlab.com/core-team/
+[team]: https://about.gitlab.com/team/
+[getting-help]: https://about.gitlab.com/getting-help/
+[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
+[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
+[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
+[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
+[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
+[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab
+[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
+[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests
+[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests
+[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
+[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
+[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
+[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
+[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
+[contributor-covenant]: http://contributor-covenant.org
+[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
+[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
+[changelog]: doc/development/changelog.md "Generate a changelog entry"
+[doc-guidelines]: doc/development/documentation/index.md "Documentation guidelines"
+[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
+[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
+[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
+[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
+[license-finder-doc]: doc/development/licensing.md
+[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
+[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
+[testing]: doc/development/testing_guide/index.md
+[us-english]: https://en.wikipedia.org/wiki/American_English
-The descriptions on the [labels page][labels-page] explain what falls under each type label.
-### Subject labels
+## Workflow labels
-Subject labels are labels that define what area or feature of GitLab this issue
-hits. They are not always necessary, but very convenient.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Examples of subject labels are ~wiki, ~ldap, ~api,
-~issues, ~"merge requests", ~labels, and ~"container registry".
-If you are an expert in a particular area, it makes it easier to find issues to
-work on. You can also subscribe to those labels to receive an email each time an
-issue is labeled with a subject label corresponding to your expertise.
+### Type labels
-Subject labels are always all-lowercase.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-### Team labels
-Team labels specify what team is responsible for this issue.
-Assigning a team label makes sure issues get the attention of the appropriate
-people.
+### Subject labels
-The current team labels are:
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-- ~Configuration
-- ~"CI/CD"
-- ~Create
-- ~Distribution
-- ~Documentation
-- ~Geo
-- ~Gitaly
-- ~Manage
-- ~Monitoring
-- ~Plan
-- ~Quality
-- ~Release
-- ~Secure
-- ~UX
-The descriptions on the [labels page][labels-page] explain what falls under the
-responsibility of each team.
+### Team labels
-Within those team labels, we also have the ~backend and ~frontend labels to
-indicate if an issue needs backend work, frontend work, or both.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Team labels are always capitalized so that they show up as the first label for
-any issue.
### Release Scoping labels
-Release Scoping labels help us clearly communicate expectations of the work for the
-release. There are three levels of Release Scoping labels:
-
-- ~Deliverable: Issues that are expected to be delivered in the current
- milestone.
-- ~Stretch: Issues that are a stretch goal for delivering in the current
- milestone. If these issues are not done in the current release, they will
- strongly be considered for the next release.
-- ~"Next Patch Release": Issues to put in the next patch release. Work on these
- first, and add the "Pick Into X" label to the merge request, along with the
- appropriate milestone.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Each issue scheduled for the current milestone should be labeled ~Deliverable
-or ~"Stretch". Any open issue for a previous milestone should be labeled
-~"Next Patch Release", or otherwise rescheduled to a different milestone.
### Priority labels
-Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
-If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
-This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-| Label | Meaning | Estimate time to fix |
-|-------|-----------------|------------------------------------------------------------------|
-| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com |
-| ~P2 | High Priority | The next release |
-| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) |
-| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) |
### Severity labels
-Severity labels help us clearly communicate the impact of a ~bug on users.
-
-| Label | Meaning | Impact on Functionality | Example |
-|-------|-------------------|-------------------------------------------------------|---------|
-| ~S1 | Blocker | Outage, broken feature with no workaround | Unable to create an issue. Data corruption/loss. Security breach. |
-| ~S2 | Critical Severity | Broken Feature, workaround too complex & unacceptable | Can push commits, but only via the command line. |
-| ~S3 | Major Severity | Broken Feature, workaround acceptable | Can create merge requests only from the Merge Requests page, not through the Issue. |
-| ~S4 | Low Severity | Functionality inconvenience or cosmetic issue | Label colors are incorrect / not being displayed. |
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
#### Severity impact guidance
-Severity levels can be applied further depending on the facet of the impact; e.g. Affected customers, GitLab.com availability, performance and etc. The below is a guideline.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-| Severity | Affected Customers/Users | GitLab.com Availability | Performance Degradation |
-|----------|---------------------------------------------------------------------|----------------------------------------------------|------------------------------|
-| ~S1 | >50% users affected (possible company extinction level event) | Significant impact on all of GitLab.com | |
-| ~S2 | Many users or multiple paid customers affected (but not apocalyptic)| Significant impact on large portions of GitLab.com | Degradation is guaranteed to occur in the near future |
-| ~S3 | A few users or a single paid customer affected | Limited impact on important portions of GitLab.com | Degradation is likely to occur in the near future |
-| ~S4 | No paid users/customer affected, or expected to in the near future | Minor impact on on GitLab.com | Degradation _may_ occur but it's not likely |
### Label for community contributors
-Issues that are beneficial to our users, 'nice to haves', that we currently do
-not have the capacity for or want to give the priority to, are labeled as
-~"Accepting Merge Requests", so the community can make a contribution.
-
-Community contributors can submit merge requests for any issue they want, but
-the ~"Accepting Merge Requests" label has a special meaning. It points to
-changes that:
-
-1. We already agreed on,
-1. Are well-defined,
-1. Are likely to get accepted by a maintainer.
-
-We want to avoid a situation when a contributor picks an
-~"Accepting Merge Requests" issue and then their merge request gets closed,
-because we realize that it does not fit our vision, or we want to solve it in a
-different way.
-
-We add the ~"Accepting Merge Requests" label to:
-
-- Low priority ~bug issues (i.e. we do not add it to the bugs that we want to
-solve in the ~"Next Patch Release")
-- Small ~"feature proposal"
-- Small ~"technical debt" issues
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-After adding the ~"Accepting Merge Requests" label, we try to estimate the
-[weight](#issue-weight) of the issue. We use issue weight to let contributors
-know how difficult the issue is. Additionally:
-
-- We advertise ["Accepting Merge Requests" issues with weight < 5][up-for-grabs]
- as suitable for people that have never contributed to GitLab before on the
- [Up For Grabs campaign](http://up-for-grabs.net)
-- We encourage people that have never contributed to any open source project to
- look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
-
-If you've decided that you would like to work on an issue, please @-mention
-the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
-as soon as possible. The product manager will then pull in appropriate GitLab team
-members to further discuss scope, design, and technical considerations. This will
-ensure that that your contribution is aligned with the GitLab product and minimize
-any rework and delay in getting it merged into master.
-
-GitLab team members who apply the ~"Accepting Merge Requests" label to an issue
-should update the issue description with a responsible product manager, inviting
-any potential community contributor to @-mention per above.
-
-[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
-[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
## Implement design & UI elements
-For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).
-
-The UX team uses labels to manage their workflow.
-
-The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
-To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
-
-Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
+This [documentation](doc/development/contributing/design.md) has been moved.
-The UX team has a special type label called ~"design artifact". This label indicates that the final output
-for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
-Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
-needed until the solution has been decided.
-
-~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
-
-To prevent the misunderstanding that a feature will be be delivered in the
-assigned milestone, when only UX design is planned for that milestone, the
-Product Manager should create a separate issue for the ~"design artifact",
-assign the ~UX, ~"design artifact" and ~"Deliverable" labels, add a milestone
-and use a title that makes it clear that the scheduled issue is design only
-(e.g. `Design exploration for XYZ`).
-
-When the ~"design artifact" issue has been completed, the UXer removes the ~UX
-label, adds the ~"UX ready" label and closes the issue. This indicates the
-design artifact is complete. The UXer will also copy the designs to related
-issues for implementation in an upcoming milestone.
## Issue tracker
-To get support for your particular problem please use the
-[getting help channels](https://about.gitlab.com/getting-help/).
-
-The [GitLab CE issue tracker on GitLab.com][ce-tracker] is for bugs concerning
-the latest GitLab release and [feature proposals](#feature-proposals).
-
-When submitting an issue please conform to the issue submission guidelines
-listed below. Not all issues will be addressed and your issue is more likely to
-be addressed if you submit a merge request which partially or fully solves
-the issue.
-
-If you're unsure where to post, post to the [mailing list][google-group] or
-[Stack Overflow][stackoverflow] first. There are a lot of helpful GitLab users
-there who may be able to help you quickly. If your particular issue turns out
-to be a bug, it will find its way from there.
-
-If it happens that you know the solution to an existing bug, please first
-open the issue in order to keep track of it and then open the relevant merge
-request that potentially fixes it.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
### Issue triaging
-Our issue triage policies are [described in our handbook]. You are very welcome
-to help the GitLab team triage issues. We also organize [issue bash events] once
-every quarter.
-
-The most important thing is making sure valid issues receive feedback from the
-development team. Therefore the priority is mentioning developers that can help
-on those issues. Please select someone with relevant experience from the
-[GitLab team][team]. If there is nobody mentioned with that expertise look in
-the commit history for the affected files to find someone.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-We also use [GitLab Triage] to automate some triaging policies. This is
-currently setup as a [scheduled pipeline] running on [quality/triage-ops]
-project.
-
-[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
-[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
-[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
-[scheduled pipeline]: https://gitlab.com/gitlab-org/quality/triage-ops/pipeline_schedules/10512/edit
-[quality/triage-ops]: https://gitlab.com/gitlab-org/quality/triage-ops
### Feature proposals
-To create a feature proposal for CE, open an issue on the
-[issue tracker of CE][ce-tracker].
-
-For feature proposals for EE, open an issue on the
-[issue tracker of EE][ee-tracker].
-
-In order to help track the feature proposals, we have created a
-[`feature proposal`][fpl] label. For the time being, users that are not members
-of the project cannot add labels. You can instead ask one of the [core team]
-members to add the label ~"feature proposal" to the issue or add the following
-code snippet right after your description in a new line: `~"feature proposal"`.
-
-Please keep feature proposals as small and simple as possible, complex ones
-might be edited to make them small and simple.
-
-Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
-
-For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
-be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
-need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
-
-If you want to create something yourself, consider opening an issue first to
-discuss whether it is interesting to include this in GitLab.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
### Issue tracker guidelines
-**[Search the issue tracker][ce-tracker]** for similar entries before
-submitting your own, there's a good chance somebody else had the same issue or
-feature proposal. Show your support with an award emoji and/or join the
-discussion.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Please submit bugs using the ['Bug' issue template](.gitlab/issue_templates/Bug.md) provided on the issue tracker.
-The text in the parenthesis is there to help you with what to include. Omit it
-when submitting the actual issue. You can copy-paste it and then edit as you
-see fit.
### Issue weight
-Issue weight allows us to get an idea of the amount of work required to solve
-one or multiple issues. This makes it possible to schedule work more accurately.
-
-You are encouraged to set the weight of any issue. Following the guidelines
-below will make it easy to manage this, without unnecessary overhead.
-
-1. Set weight for any issue at the earliest possible convenience
-1. If you don't agree with a set weight, discuss with other developers until
-consensus is reached about the weight
-1. Issue weights are an abstract measurement of complexity of the issue. Do not
-relate issue weight directly to time. This is called [anchoring](https://en.wikipedia.org/wiki/Anchoring)
-and something you want to avoid.
-1. Something that has a weight of 1 (or no weight) is really small and simple.
-Something that is 9 is rewriting a large fundamental part of GitLab,
-which might lead to many hard problems to solve. Changing some text in GitLab
-is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
-1. If something is very large, it should probably be split up in multiple
-issues or chunks. You can simply not set the weight of a parent issue and set
-weights to children issues.
-
-### Regression issues
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Every monthly release has a corresponding issue on the CE issue tracker to keep
-track of functionality broken by that release and any fixes that need to be
-included in a patch release (see [8.3 Regressions] as an example).
-As outlined in the issue description, the intended workflow is to post one note
-with a reference to an issue describing the regression, and then to update that
-note with a reference to the merge request that fixes it as it becomes available.
-
-If you're a contributor who doesn't have the required permissions to update
-other users' notes, please post a new note with a reference to both the issue
-and the merge request.
+### Regression issues
-The release manager will [update the notes] in the regression issue as fixes are
-addressed.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
-[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
### Technical and UX debt
-In order to track things that can be improved in GitLab's codebase,
-we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
-For user experience improvements, we use the ~"UX debt" label.
-
-These labels should be added to issues that describe things that can be improved,
-shortcuts that have been taken, features that need additional attention, and all
-other things that have been left behind due to high velocity of development.
-For example, code that needs refactoring should use the ~"technical debt" label,
-user experience refinements should use the ~"UX debt" label.
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-Everyone can create an issue, though you may need to ask for adding a specific
-label, if you do not have permissions to do it by yourself. Additional labels
-can be combined with these labels, to make it easier to schedule
-the improvements for a release.
-
-Issues tagged with these labels have the same priority like issues
-that describe a new feature to be introduced in GitLab, and should be scheduled
-for a release by the appropriate person.
-
-Make sure to mention the merge request that the ~"technical debt" issue or
-~"UX debt" issue is associated with in the description of the issue.
### Stewardship
-For issues related to the open source stewardship of GitLab,
-there is the ~"stewardship" label.
-
-This label is to be used for issues in which the stewardship of GitLab
-is a topic of discussion. For instance if GitLab Inc. is planning to add
-features from GitLab EE to GitLab CE, related issues would be labelled with
-~"stewardship".
-
-A recent example of this was the issue for
-[bringing the time tracking API to GitLab CE][time-tracking-issue].
+This [documentation](doc/development/contributing/issue_workflow.md) has been moved.
-[time-tracking-issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/25517#note_20019084
## Merge requests
-We welcome merge requests with fixes and improvements to GitLab code, tests,
-and/or documentation. The issues that are specifically suitable for
-community contributions are listed with the label
-[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
-and [EE][accepting-mrs-ee], but you are free to contribute to any other issue
-you want.
-
-Please note that if an issue is marked for the current milestone either before
-or while you are working on it, a team member may take over the merge request
-in order to ensure the work is finished before the release date.
-
-If you want to add a new feature that is not labeled it is best to first create
-a feedback issue (if there isn't one already) and leave a comment asking for it
-to be marked as `Accepting Merge Requests`. Please include screenshots or
-wireframes if the feature will also change the UI.
-
-Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-If you are new to GitLab development (or web development in general), see the
-[I want to contribute!](#i-want-to-contribute) section to get you started with
-some potentially easy issues.
-
-To start with GitLab development download the [GitLab Development Kit][gdk] and
-see the [Development section](doc/development/README.md) for some guidelines.
### Merge request guidelines
-If you can, please submit a merge request with the fix or improvements
-including tests. If you don't know how to fix the issue but can write a test
-that exposes the issue we will accept that as well. In general bug fixes that
-include a regression test are merged quickly while new features without proper
-tests are least likely to receive timely feedback. The workflow to make a merge
-request is as follows:
-
-1. Fork the project into your personal space on GitLab.com
-1. Create a feature branch, branch away from `master`
-1. Write [tests](https://docs.gitlab.com/ee/development/rake_tasks.html#run-tests) and code
-1. [Generate a changelog entry with `bin/changelog`][changelog]
-1. If you are writing documentation, make sure to follow the
- [documentation guidelines][doc-guidelines]
-1. If you have multiple commits please combine them into a few logically
- organized commits by [squashing them][git-squash]
-1. Push the commit(s) to your fork
-1. Submit a merge request (MR) to the `master` branch
- 1. Your merge request needs at least 1 approval but feel free to require more.
- For instance if you're touching backend and frontend code, it's a good idea
- to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
- maintainer
- 1. You don't have to select any approvers, but you can if you really want
- specific people to approve your merge request
-1. The MR title should describe the change you want to make
-1. The MR description should give a motive for your change and the method you
- used to achieve it.
- 1. If you are contributing code, fill in the template already provided in the
- "Description" field.
- 1. If you are contributing documentation, choose `Documentation` from the
- "Choose a template" menu and fill in the template.
- 1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
- `Closes #XXX` syntax to auto-close the issue(s) once the merge request will
- be merged.
-1. If you're allowed to, set a relevant milestone and labels
-1. If the MR changes the UI it should include *Before* and *After* screenshots
-1. If the MR changes CSS classes please include the list of affected pages,
- `grep css-class ./app -R`
-1. Be prepared to answer questions and incorporate feedback even if requests
- for this arrive weeks or months after your MR submission
- 1. If a discussion has been addressed, select the "Resolve discussion" button
- beneath it to mark it resolved.
-1. If your MR touches code that executes shell commands, reads or opens files or
- handles paths to files on disk, make sure it adheres to the
- [shell command guidelines](doc/development/shell_commands.md)
-1. If your code creates new files on disk please read the
- [shared files guidelines](doc/development/shared_files.md).
-1. When writing commit messages please follow
- [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
- [guidelines](http://chris.beams.io/posts/git-commit/).
-1. If your merge request adds one or more migrations, make sure to execute all
- migrations on a fresh database before the MR is reviewed. If the review leads
- to large changes in the MR, do this again once the review is complete.
-1. For more complex migrations, write tests.
-1. Merge requests **must** adhere to the [merge request performance
- guidelines](doc/development/merge_request_performance_guidelines.md).
-1. For tests that use Capybara or PhantomJS, see this [article on how
- to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
-
-Please keep the change in a single MR **as small as possible**. If you want to
-contribute a large feature think very hard what the minimum viable change is.
-Can you split the functionality? Can you only submit the backend/API code? Can
-you start with a very simple UI? Can you do part of the refactor? The increased
-reviewability of small MRs that leads to higher code quality is more important
-to us than having a minimal commit log. The smaller an MR is the more likely it
-is it will be merged (quickly). After that you can send more MRs to enhance it.
-The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this.
-
-For examples of feedback on merge requests please look at already
-[closed merge requests][closed-merge-requests]. If you would like quick feedback
-on your merge request feel free to mention someone from the [core team] or one
-of the [Merge request coaches][team].
-Please ensure that your merge request meets the contribution acceptance criteria.
-
-When having your code reviewed and when reviewing merge requests please take the
-[code review guidelines](doc/development/code_review.md) into account.
-
-### Contribution acceptance criteria
-
-1. The change is as small as possible
-1. Include proper tests and make all tests pass (unless it contains a test
- exposing a bug in existing code). Every new class should have corresponding
- unit tests, even if the class is exercised at a higher level, such as a feature test.
-1. If you suspect a failing CI build is unrelated to your contribution, you may
- try and restart the failing CI job or ask a developer to fix the
- aforementioned failing test
-1. Your MR initially contains a single commit (please use `git rebase -i` to
- squash commits)
-1. Your changes can merge without problems (if not please rebase if you're the
- only one working on your feature branch, otherwise, merge `master`)
-1. Does not break any existing functionality
-1. Fixes one specific issue or implements one specific feature (do not combine
- things, send separate merge requests if needed)
-1. Migrations should do only one thing (e.g., either create a table, move data
- to a new table or remove an old table) to aid retrying on failure
-1. Keeps the GitLab code base clean and well structured
-1. Contains functionality we think other users will benefit from too
-1. Doesn't add configuration options or settings options since they complicate
- making and testing future changes
-1. Changes do not adversely degrade performance.
- - Avoid repeated polling of endpoints that require a significant amount of overhead
- - Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- - Avoid repeated access of filesystem
-1. If you need polling to support real-time features, please use
- [polling with ETag caching][polling-etag].
-1. Changes after submitting the merge request should be in separate commits
- (no squashing).
-1. It conforms to the [style guides](#style-guides) and the following:
- - If your change touches a line that does not follow the style, modify the
- entire line to follow it. This prevents linting tools from generating warnings.
- - Don't touch neighbouring lines. As an exception, automatic mass
- refactoring modifications may leave style non-compliant.
-1. If the merge request adds any new libraries (gems, JavaScript libraries,
- etc.), they should conform to our [Licensing guidelines][license-finder-doc].
- See the instructions in that document for help if your MR fails the
- "license-finder" test with a "Dependencies that need approval" error.
-1. The merge request meets the [definition of done](#definition-of-done).
-
-## Definition of done
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-If you contribute to GitLab please know that changes involve more than just
-code. We have the following [definition of done][definition-of-done]. Please ensure you support
-the feature you contribute through all of these steps.
-
-1. Description explaining the relevancy (see following item)
-1. Working and clean code that is commented where needed
-1. [Unit, integration, and system tests][testing] that pass on the CI server
-1. Performance/scalability implications have been considered, addressed, and tested
-1. [Documented][doc-guidelines] in the `/doc` directory
-1. [Changelog entry added][changelog], if necessary
-1. Reviewed and any concerns are addressed
-1. Merged by a project maintainer
-1. Added to the release blog article, if relevant
-1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/), if relevant
-1. Community questions answered
-1. Answers to questions radiated (in docs/wiki/support etc.)
-
-If you add a dependency in GitLab (such as an operating system package) please
-consider updating the following and note the applicability of each in your
-merge request:
-
-1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
-1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
-1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
-1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
-1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
-1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh
-1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
-## Style guides
-
-1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
- Important sections include [Source Code Layout][rss-source] and
- [Naming][rss-naming]. Use:
- - multi-line method chaining style **Option A**: dot `.` on the second line
- - string literal quoting style **Option A**: single quoted by default
-1. [Rails](https://github.com/bbatsov/rails-style-guide)
-1. [Newlines styleguide][newlines-styleguide]
-1. [Testing][testing]
-1. [JavaScript styleguide][js-styleguide]
-1. [SCSS styleguide][scss-styleguide]
-1. [Shell commands](doc/development/shell_commands.md) created by GitLab
- contributors to enhance security
-1. [Database Migrations](doc/development/migration_style_guide.md)
-1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
-1. [Documentation styleguide](https://docs.gitlab.com/ee/development/documentation/styleguide.html)
-1. Interface text should be written subjectively instead of objectively. It
- should be the GitLab core team addressing a person. It should be written in
- present time and never use past tense (has been/was). For example instead
- of _prohibited this user from being saved due to the following errors:_ the
- text should be _sorry, we could not create your account because:_
-1. Code should be written in [US English][us-english]
-
-This is also the style used by linting tools such as
-[RuboCop](https://github.com/bbatsov/rubocop),
-[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
-
-## Code of conduct
-
-As contributors and maintainers of this project, we pledge to respect all
-people who contribute through reporting issues, posting feature requests,
-updating documentation, submitting pull requests or patches, and other
-activities.
-
-We are committed to making participation in this project a harassment-free
-experience for everyone, regardless of level of experience, gender, gender
-identity and expression, sexual orientation, disability, personal appearance,
-body size, race, ethnicity, age, or religion.
-
-Examples of unacceptable behavior by participants include the use of sexual
-language or imagery, derogatory comments or personal attacks, trolling, public
-or private harassment, insults, or other unprofessional conduct.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct. Project maintainers who do not
-follow the Code of Conduct may be removed from the project team.
-
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
-
-Instances of abusive, harassing, or otherwise unacceptable behavior can be
-reported by emailing `contact@gitlab.com`.
-
-This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
-available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
-
-## Contribution Flow
-
-When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
-
-When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
+### Contribution acceptance criteria
-When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
+This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
-Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
-GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
+## Definition of done
-GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
+This [documentation](doc/development/contributing/merge_request_workflow.md)) has been moved.
-When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
-When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
+## Style guides
-[core team]: https://about.gitlab.com/core-team/
-[team]: https://about.gitlab.com/team/
-[getting-help]: https://about.gitlab.com/getting-help/
-[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
-[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
-[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
-[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
-[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
-[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab
-[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal
-[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests
-[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests
-[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
-[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
-[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
-[closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
-[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
-[contributor-covenant]: http://contributor-covenant.org
-[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
-[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
-[changelog]: doc/development/changelog.md "Generate a changelog entry"
-[doc-guidelines]: doc/development/documentation/index.md "Documentation guidelines"
-[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
-[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
-[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
-[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
-[license-finder-doc]: doc/development/licensing.md
-[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
-[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
-[testing]: doc/development/testing_guide/index.md
-[us-english]: https://en.wikipedia.org/wiki/American_English
+This [documentation](doc/development/contributing/design.md) has been moved.
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 8104cabd36f..0e79152459e 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-8.1.0
+8.1.1
diff --git a/Gemfile b/Gemfile
index d9066081f74..5666e6cebc5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -180,7 +180,7 @@ gem 'rufus-scheduler', '~> 3.4'
gem 'httparty', '~> 0.13.3'
# Colored output to console
-gem 'rainbow', '~> 2.2'
+gem 'rainbow', '~> 3.0'
# Progress bar
gem 'ruby-progressbar'
diff --git a/Gemfile.lock b/Gemfile.lock
index 1537cacaadd..b33dd75c278 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -123,7 +123,7 @@ GEM
numerizer (~> 0.1.1)
chunky_png (1.3.5)
citrus (3.0.2)
- coderay (1.1.1)
+ coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
commonmarker (0.17.8)
@@ -494,7 +494,7 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
- method_source (0.8.2)
+ method_source (0.9.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
@@ -636,12 +636,11 @@ GEM
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.4)
- pry (0.10.4)
+ pry (0.11.3)
coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.4.2)
- byebug (~> 9.0)
+ method_source (~> 0.9.0)
+ pry-byebug (3.4.3)
+ byebug (>= 9.0, < 9.1)
pry (~> 0.10)
pry-rails (0.3.5)
pry (>= 0.9.10)
@@ -692,8 +691,7 @@ GEM
activesupport (= 4.2.10)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- rainbow (2.2.2)
- rake
+ rainbow (3.0.0)
raindrops (0.18.0)
rake (12.3.1)
rb-fsevent (0.10.2)
@@ -811,7 +809,7 @@ GEM
rubyzip (1.2.1)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.2)
+ rugged (0.27.4)
safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
@@ -872,7 +870,6 @@ GEM
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slack-notifier (1.5.1)
- slop (3.6.0)
spring (2.0.1)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
@@ -1136,7 +1133,7 @@ DEPENDENCIES
rails (= 4.2.10)
rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 4.0.9)
- rainbow (~> 2.2)
+ rainbow (~> 3.0)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
rbtrace (~> 0.4)
@@ -1207,4 +1204,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
- 1.16.2
+ 1.16.3
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 39305927c0f..af70e2c1939 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -701,8 +701,7 @@ GEM
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- rainbow (2.2.2)
- rake
+ rainbow (3.0.0)
raindrops (0.18.0)
rake (12.3.1)
rb-fsevent (0.10.2)
@@ -1147,7 +1146,7 @@ DEPENDENCIES
rails-controller-testing
rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 5.1)
- rainbow (~> 2.2)
+ rainbow (~> 3.0)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
rbtrace (~> 0.4)
diff --git a/PROCESS.md b/PROCESS.md
index a06ddb68b77..5f50d472bd7 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -15,8 +15,9 @@
- [Between the 1st and the 7th](#between-the-1st-and-the-7th)
- [On the 7th](#on-the-7th)
- [After the 7th](#after-the-7th)
-- [Regressions](#regressions)
- - [How to manage a regression](#how-to-manage-a-regression)
+- [Bugs](#bugs)
+ - [Regressions](#regressions)
+ - [Managing bugs](#managing-bugs)
- [Release retrospective and kickoff](#release-retrospective-and-kickoff)
- [Retrospective](#retrospective)
- [Kickoff](#kickoff)
@@ -168,7 +169,7 @@ information, see
Once the stable branch is frozen, the only MRs that can be cherry-picked into
the stable branch are:
-* Fixes for [regressions](#regressions)
+* Fixes for [regressions](#regressions) where the affected version `xx.x` in `regression:xx.x` is the current release. See [Managing bugs](#managing-bugs) section.
* Fixes for security issues
* Fixes or improvements to automated QA scenarios
* Documentation updates for changes in the same release
@@ -201,48 +202,59 @@ you can ask for an exception to be made.
Check [this guide](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md) about how to open an exception request before opening one.
-## Regressions
+## Bugs
-A regression for a particular monthly release is a bug that exists in that
-release, but wasn't present in the release before. This includes bugs in
-features that were only added in that monthly release. Every regression **must**
-have the milestone of the release it was introduced in - if a regression doesn't
-have a milestone, it might be 'just' a bug!
+A ~bug is a defect, error, failure which causes the system to behave incorrectly or prevents it from fulfilling the product requirements.
-For instance, if 10.5.0 adds a feature, and that feature doesn't work correctly,
-then this is a regression in 10.5. If 10.5.1 then fixes that, but 10.5.3 somehow
-reintroduces the bug, then this bug is still a regression in 10.5.
+The level of impact of a ~bug can vary from blocking a whole functionality
+or a feature usability bug. A bug should always be linked to a severity level.
+Refer to our [severity levels](../CONTRIBUTING.md#severity-labels)
-Because GitLab.com runs release candidates of new releases, a regression can be
-reported in a release before its 'official' release date on the 22nd of the
-month. When we say 'the most recent monthly release', this can refer to either
-the version currently running on GitLab.com, or the most recent version
-available in the package repositories.
+Whether the bug is also a regression or not, the triage process should start as soon as possible.
+Ensure that the Engineering Manager and/or the Product Manager for the relative area is involved to prioritize the work as needed.
-### How to manage a regression
+### Regressions
-Regressions are very important, and they should be considered high priority
-issues that should be solved as soon as possible, especially if they affect
-users. Despite that, ~regression label itself does not imply when the issue
-will be scheduled.
+A ~regression implies that a previously **verified working functionality** no longer works.
+Regressions are a subset of bugs. We use the ~regression label to imply that the defect caused the functionality to regress.
+The label tells us that something worked before and it needs extra attention from Engineering and Product Managers to schedule/reschedule.
-When a regression is found:
-1. Create an issue describing the problem in the most detailed way possible
-1. If possible, provide links to real examples and how to reproduce the problem
+The regression label does not apply to ~bugs for new features for which functionality was **never verified as working**.
+These, by definition, are not regressions.
+
+A regression should always have the `regression:xx.x` label on it to designate when it was introduced.
+
+Regressions should be considered high priority issues that should be solved as soon as possible, especially if they have severe impact on users.
+
+### Managing bugs
+
+**Prioritization:** We give higher priority to regressions on features that worked in the last recent monthly release and the current release candidates.
+The two scenarios below can [bypass the exception request in the release process](https://gitlab.com/gitlab-org/release/docs/blob/master/general/exception-request/process.md#after-the-7th), where the affected regression version matches the current monthly release version.
+* A regression which worked in the **Last monthly release**
+ * **Example:** In 11.0 we released a new `feature X` that is verified as working. Then in release 11.1 the feature no longer works, this is regression for 11.1. The issue should have the `regression:11.1` label.
+ * *Note:* When we say `the last recent monthly release`, this can refer to either the version currently running on GitLab.com, or the most recent version available in the package repositories.
+* A regression which worked in the **Current release candidates**
+ * **Example:** In 11.1-RC3 we shipped a new feature which has been verified as working. Then in 11.1-RC5 the feature no longer works, this is regression for 11.1. The issue should have the `regression:11.1` label.
+ * *Note:* Because GitLab.com runs release candidates of new releases, a regression can be reported in a release before its 'official' release date on the 22nd of the month.
+
+When a bug is found:
+1. Create an issue describing the problem in the most detailed way possible.
+1. If possible, provide links to real examples and how to reproduce the problem.
1. Label the issue properly, using the [team label](../CONTRIBUTING.md#team-labels),
the [subject label](../CONTRIBUTING.md#subject-labels)
and any other label that may apply in the specific case
-1. Add the ~bug and ~regression labels
-1. Notify the respective Engineering Manager to evaluate the Severity of the regression and add a [Severity label](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#bug-severity-labels). The counterpart Product Manager is included to weigh-in on prioritization as needed to set the [Priority label](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#bug-priority-labels).
-1. If the regression is either an ~S1, ~S2 or ~S3 severity, label the regression with the current milestone as it should be fixed in the current milestone.
- 1. If the regression was introduced in an RC of the current release, label with ~Deliverable
- 1. If the regression was introduced in the previous release, label with ~"Next Patch Release"
-1. If the regression is an ~S4 severity, the regression may be scheduled for later milestones at the discretion of Engineering Manager and Product Manager.
-
-When a new issue is found, the fix should start as soon as possible. You can
-ping the Engineering Manager or the Product Manager for the relative area to
-make them aware of the issue earlier. They will analyze the priority and change
-it if needed.
+1. Notify the respective Engineering Manager to evaluate and apply the [Severity label](../CONTRIBUTING.md#bug-severity-labels) and [Priority label](../CONTRIBUTING.md#bug-priority-labels).
+The counterpart Product Manager is included to weigh-in on prioritization as needed.
+1. If the ~bug is **NOT** a regression:
+ 1. The Engineering Manager decides which milestone the bug will be fixed. The appropriate milestone is applied.
+1. If the bug is a ~regression:
+ 1. Determine the release that the regression affects and add the corresponding `regression:xx.x` label.
+ 1. If the affected release version can't be determined, add the generic ~regression label for the time being.
+ 1. If the affected version `xx.x` in `regression:xx.x` is the **current release**, it's recommended to schedule the fix for the current milestone.
+ 1. This falls under regressions which worked in the last release and the current RCs. More detailed explanations in the **Prioritization** section above.
+ 1. If the affected version `xx.x` in `regression:xx.x` is older than the **current release**
+ 1. If the regression is an ~S1 severity, it's recommended to schedule the fix for the current milestone. We would like to fix the highest severity regression as soon as we can.
+ 1. If the regression is an ~S2, ~S3 or ~S4 severity, the regression may be scheduled for later milestones at the discretion of the Engineering Manager and Product Manager.
## Release retrospective and kickoff
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index a9102743bf9..109e60cbde2 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -3,7 +3,7 @@
import $ from 'jquery';
import Vue from 'vue';
import Flash from '../../flash';
-import { __ } from '../../locale';
+import { sprintf, __ } from '../../locale';
import Sidebar from '../../right_sidebar';
import eventHub from '../../sidebar/event_hub';
import AssigneeTitle from '../../sidebar/components/assignees/assignee_title.vue';
@@ -55,8 +55,10 @@ gl.issueBoards.BoardSidebar = Vue.extend({
return this.issue.labels && this.issue.labels.length;
},
labelDropdownTitle() {
- return this.hasLabels ?
- `${this.issue.labels[0].title} ${this.issue.labels.length - 1}+ more` : 'Label';
+ return this.hasLabels ? sprintf(__('%{firstLabel} +%{labelCount} more'), {
+ firstLabel: this.issue.labels[0].title,
+ labelCount: this.issue.labels.length - 1
+ }) : __('Label');
},
selectedLabels() {
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 050cbd8db48..ad473404c29 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,6 +1,7 @@
/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len */
/* global ListIssue */
+import { __ } from '~/locale';
import ListLabel from '~/vue_shared/models/label';
import ListAssignee from '~/vue_shared/models/assignee';
import queryData from '../utils/query_data';
@@ -30,7 +31,7 @@ class List {
this.id = obj.id;
this._uid = this.guid();
this.position = obj.position;
- this.title = obj.title;
+ this.title = obj.list_type === 'backlog' ? __('Open') : obj.title;
this.type = obj.list_type;
const typeInfo = this.getTypeInfo(this.type);
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index 589eeee9695..742cf490ad2 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -8,6 +8,7 @@ import 'core-js/fn/object/assign';
import 'core-js/fn/promise';
import 'core-js/fn/string/code-point-at';
import 'core-js/fn/string/from-code-point';
+import 'core-js/fn/string/includes';
import 'core-js/fn/symbol';
import 'core-js/es6/map';
import 'core-js/es6/weak-map';
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 7cc4e6a2c3a..b5b05df4d34 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -114,11 +114,15 @@ export default {
this.adjustView();
},
methods: {
- ...mapActions('diffs', ['setBaseConfig', 'fetchDiffFiles']),
+ ...mapActions('diffs', ['setBaseConfig', 'fetchDiffFiles', 'startRenderDiffsQueue']),
fetchData() {
- this.fetchDiffFiles().catch(() => {
- createFlash(__('Something went wrong on our end. Please try again!'));
- });
+ this.fetchDiffFiles()
+ .then(() => {
+ requestIdleCallback(this.startRenderDiffsQueue, { timeout: 1000 });
+ })
+ .catch(() => {
+ createFlash(__('Something went wrong on our end. Please try again!'));
+ });
if (!this.isNotesFetched) {
eventHub.$emit('fetchNotesData');
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 7e7058d8d08..59e9ba08b8b 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -46,16 +46,25 @@ export default {
showExpandMessage() {
return this.isCollapsed && !this.isLoadingCollapsedDiff && !this.file.tooLarge;
},
+ showLoadingIcon() {
+ return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
+ },
},
methods: {
...mapActions('diffs', ['loadCollapsedDiff']),
handleToggle() {
const { collapsed, highlightedDiffLines, parallelDiffLines } = this.file;
- if (collapsed && !highlightedDiffLines && !parallelDiffLines.length) {
+ if (
+ collapsed &&
+ !highlightedDiffLines &&
+ parallelDiffLines !== undefined &&
+ !parallelDiffLines.length
+ ) {
this.handleLoadCollapsedDiff();
} else {
this.file.collapsed = !this.file.collapsed;
+ this.file.renderIt = true;
}
},
handleLoadCollapsedDiff() {
@@ -65,6 +74,7 @@ export default {
.then(() => {
this.isLoadingCollapsedDiff = false;
this.file.collapsed = false;
+ this.file.renderIt = true;
})
.catch(() => {
this.isLoadingCollapsedDiff = false;
@@ -121,12 +131,12 @@ export default {
</div>
<diff-content
- v-if="!isCollapsed"
+ v-if="!isCollapsed && file.renderIt"
:class="{ hidden: isCollapsed || file.tooLarge }"
:diff-file="file"
/>
<loading-icon
- v-if="isLoadingCollapsedDiff"
+ v-else-if="showLoadingIcon"
class="diff-content loading"
/>
<div
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 2fa8367f528..f68afa44837 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -25,3 +25,6 @@ export const CONTEXT_LINE_CLASS_NAME = 'diff-expanded';
export const UNFOLD_COUNT = 20;
export const COUNT_OF_AVATARS_IN_GUTTER = 3;
export const LENGTH_OF_AVATAR_TOOLTIP = 17;
+
+export const LINES_TO_BE_RENDERED_DIRECTLY = 100;
+export const MAX_LINES_TO_BE_RENDERED = 2000;
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 27001142257..4ab6ceb249a 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -29,6 +29,27 @@ export const fetchDiffFiles = ({ state, commit }) => {
.then(handleLocationHash);
};
+export const startRenderDiffsQueue = ({ state, commit }) => {
+ const checkItem = () => {
+ const nextFile = state.diffFiles.find(
+ file => !file.renderIt && (!file.collapsed || !file.text),
+ );
+ if (nextFile) {
+ requestAnimationFrame(() => {
+ commit(types.RENDER_FILE, nextFile);
+ });
+ requestIdleCallback(
+ () => {
+ checkItem();
+ },
+ { timeout: 1000 },
+ );
+ }
+ };
+
+ checkItem();
+};
+
export const setInlineDiffViewType = ({ commit }) => {
commit(types.SET_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE);
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 2c8e1a1466f..c999d637d50 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -8,3 +8,4 @@ export const REMOVE_COMMENT_FORM_LINE = 'REMOVE_COMMENT_FORM_LINE';
export const ADD_CONTEXT_LINES = 'ADD_CONTEXT_LINES';
export const ADD_COLLAPSED_DIFFS = 'ADD_COLLAPSED_DIFFS';
export const EXPAND_ALL_FILES = 'EXPAND_ALL_FILES';
+export const RENDER_FILE = 'RENDER_FILE';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index a98b2be89a3..0522e32c410 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import _ from 'underscore';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { findDiffFile, addLineReferences, removeMatchLine, addContextLines } from './utils';
+import { LINES_TO_BE_RENDERED_DIRECTLY, MAX_LINES_TO_BE_RENDERED } from '../constants';
import * as types from './mutation_types';
export default {
@@ -15,8 +16,48 @@ export default {
},
[types.SET_DIFF_DATA](state, data) {
+ const diffData = convertObjectPropsToCamelCase(data, { deep: true });
+ let showingLines = 0;
+ const filesLength = diffData.diffFiles.length;
+ let i;
+ for (i = 0; i < filesLength; i += 1) {
+ const file = diffData.diffFiles[i];
+ if (file.parallelDiffLines) {
+ const linesLength = file.parallelDiffLines.length;
+ let u = 0;
+ for (u = 0; u < linesLength; u += 1) {
+ const line = file.parallelDiffLines[u];
+ if (line.left) delete line.left.text;
+ if (line.right) delete line.right.text;
+ }
+ }
+
+ if (file.highlightedDiffLines) {
+ const linesLength = file.highlightedDiffLines.length;
+ let u;
+ for (u = 0; u < linesLength; u += 1) {
+ const line = file.highlightedDiffLines[u];
+ delete line.text;
+ }
+ }
+
+ if (file.highlightedDiffLines) {
+ showingLines += file.parallelDiffLines.length;
+ }
+ Object.assign(file, {
+ renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
+ collapsed: file.text && showingLines > MAX_LINES_TO_BE_RENDERED,
+ });
+ }
+
Object.assign(state, {
- ...convertObjectPropsToCamelCase(data, { deep: true }),
+ ...diffData,
+ });
+ },
+
+ [types.RENDER_FILE](state, file) {
+ Object.assign(file, {
+ renderIt: true,
});
},
diff --git a/app/assets/javascripts/emoji/support/unicode_support_map.js b/app/assets/javascripts/emoji/support/unicode_support_map.js
index 8c1861c56db..651169391fe 100644
--- a/app/assets/javascripts/emoji/support/unicode_support_map.js
+++ b/app/assets/javascripts/emoji/support/unicode_support_map.js
@@ -86,7 +86,7 @@ function generateUnicodeSupportMap(testMap) {
canvas.height = numTestEntries * fontSize;
ctx.fillStyle = '#000000';
ctx.textBaseline = 'middle';
- ctx.font = `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
+ ctx.font = `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`;
// Write each emoji to the canvas vertically
let writeIndex = 0;
testMapKeys.forEach(testKey => {
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index 5611b37be7c..00ae5ea2c15 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -61,7 +61,7 @@ export default {
<slot name="header"></slot>
</header>
<div
- class="ide-tree-body"
+ class="ide-tree-body h-100"
>
<repo-file
v-for="file in currentTree.tree"
diff --git a/app/assets/javascripts/ide/components/new_dropdown/button.vue b/app/assets/javascripts/ide/components/new_dropdown/button.vue
index ff114e47741..aa5fce59dbf 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/button.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/button.vue
@@ -1,7 +1,11 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
export default {
+ directives: {
+ tooltip,
+ },
components: {
Icon,
},
@@ -26,6 +30,11 @@ export default {
default: true,
},
},
+ computed: {
+ tooltipTitle() {
+ return this.showLabel ? '' : this.label;
+ },
+ },
methods: {
clicked() {
this.$emit('click');
@@ -36,7 +45,9 @@ export default {
<template>
<button
+ v-tooltip
:aria-label="label"
+ :title="tooltipTitle"
type="button"
class="btn-blank"
@click.stop.prevent="clicked"
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
index fef36eae7b1..39a1bd1f61b 100644
--- a/app/assets/javascripts/ide/components/preview/clientside.vue
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -2,6 +2,7 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore';
import { Manager } from 'smooshpack';
+import { listen } from 'codesandbox-api';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import Navigator from './navigator.vue';
import { packageJsonPath } from '../../constants';
@@ -16,6 +17,7 @@ export default {
return {
manager: {},
loading: false,
+ sandpackReady: false,
};
},
computed: {
@@ -81,6 +83,10 @@ export default {
}
this.manager = {};
+ if (this.listener) {
+ this.listener();
+ }
+
clearTimeout(this.timeout);
this.timeout = null;
},
@@ -96,17 +102,29 @@ export default {
return this.loadFileContent(this.mainEntry)
.then(() => this.$nextTick())
- .then(() =>
+ .then(() => {
this.initManager('#ide-preview', this.sandboxOpts, {
fileResolver: {
isFile: p => Promise.resolve(!!this.entries[createPathWithExt(p)]),
readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content),
},
- }),
- );
+ });
+
+ this.listener = listen(e => {
+ switch (e.type) {
+ case 'done':
+ this.sandpackReady = true;
+ break;
+ default:
+ break;
+ }
+ });
+ });
},
update() {
- if (this.timeout) return;
+ if (!this.sandpackReady) return;
+
+ clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
if (_.isEmpty(this.manager)) {
@@ -116,10 +134,7 @@ export default {
}
this.manager.updatePreview(this.sandboxOpts);
-
- clearTimeout(this.timeout);
- this.timeout = null;
- }, 500);
+ }, 250);
},
initManager(el, opts, resolver) {
this.manager = new Manager(el, opts, resolver);
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index c6d7d218e81..3f6101e58f4 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -3,7 +3,6 @@ import VueRouter from 'vue-router';
import { join as joinPath } from 'path';
import flash from '~/flash';
import store from './stores';
-import { activityBarViews } from './constants';
Vue.use(VueRouter);
@@ -74,98 +73,23 @@ router.beforeEach((to, from, next) => {
projectId: to.params.project,
})
.then(() => {
- const fullProjectId = `${to.params.namespace}/${to.params.project}`;
-
+ const basePath = to.params[0] || '';
+ const projectId = `${to.params.namespace}/${to.params.project}`;
const branchId = to.params.branchid;
+ const mergeRequestId = to.params.mrid;
if (branchId) {
- const basePath = to.params[0] || '';
-
- store.dispatch('setCurrentBranchId', branchId);
-
- store.dispatch('getBranchData', {
- projectId: fullProjectId,
+ store.dispatch('openBranch', {
+ projectId,
branchId,
+ basePath,
+ });
+ } else if (mergeRequestId) {
+ store.dispatch('openMergeRequest', {
+ projectId,
+ mergeRequestId,
+ targetProjectId: to.query.target_project,
});
-
- store
- .dispatch('getFiles', {
- projectId: fullProjectId,
- branchId,
- })
- .then(() => {
- if (basePath) {
- const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
- const treeEntryKey = Object.keys(store.state.entries).find(
- key => key === path && !store.state.entries[key].pending,
- );
- const treeEntry = store.state.entries[treeEntryKey];
-
- if (treeEntry) {
- store.dispatch('handleTreeEntryAction', treeEntry);
- }
- }
- })
- .catch(e => {
- throw e;
- });
- } else if (to.params.mrid) {
- store
- .dispatch('getMergeRequestData', {
- projectId: fullProjectId,
- targetProjectId: to.query.target_project,
- mergeRequestId: to.params.mrid,
- })
- .then(mr => {
- store.dispatch('updateActivityBarView', activityBarViews.review);
-
- store.dispatch('getBranchData', {
- projectId: fullProjectId,
- branchId: mr.source_branch,
- });
-
- return store.dispatch('getFiles', {
- projectId: fullProjectId,
- branchId: mr.source_branch,
- });
- })
- .then(() =>
- store.dispatch('getMergeRequestVersions', {
- projectId: fullProjectId,
- targetProjectId: to.query.target_project,
- mergeRequestId: to.params.mrid,
- }),
- )
- .then(() =>
- store.dispatch('getMergeRequestChanges', {
- projectId: fullProjectId,
- targetProjectId: to.query.target_project,
- mergeRequestId: to.params.mrid,
- }),
- )
- .then(mrChanges => {
- mrChanges.changes.forEach((change, ind) => {
- const changeTreeEntry = store.state.entries[change.new_path];
-
- if (changeTreeEntry) {
- store.dispatch('setFileMrChange', {
- file: changeTreeEntry,
- mrChange: change,
- });
-
- if (ind < 10) {
- store.dispatch('getFileData', {
- path: change.new_path,
- makeFileActive: ind === 0,
- });
- }
- }
- });
- })
- .catch(e => {
- flash('Error while loading the merge request. Please try again.');
- throw e;
- });
}
})
.catch(e => {
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 9e3f5da4676..c9795750d65 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -54,9 +54,6 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => {
commit(types.SET_FILE_ACTIVE, { path, active: true });
dispatch('scrollToTab');
-
- commit(types.SET_CURRENT_PROJECT, file.projectId);
- commit(types.SET_CURRENT_BRANCH, file.branchId);
};
export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive = true }) => {
diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js
index 1887b77b00b..187f8c75d07 100644
--- a/app/assets/javascripts/ide/stores/actions/merge_request.js
+++ b/app/assets/javascripts/ide/stores/actions/merge_request.js
@@ -1,6 +1,8 @@
-import { __ } from '../../../locale';
+import flash from '~/flash';
+import { __ } from '~/locale';
import service from '../../services';
import * as types from '../mutation_types';
+import { activityBarViews } from '../../constants';
export const getMergeRequestData = (
{ commit, dispatch, state },
@@ -104,3 +106,67 @@ export const getMergeRequestVersions = (
resolve(state.projects[projectId].mergeRequests[mergeRequestId].versions);
}
});
+
+export const openMergeRequest = (
+ { dispatch, state },
+ { projectId, targetProjectId, mergeRequestId } = {},
+) =>
+ dispatch('getMergeRequestData', {
+ projectId,
+ targetProjectId,
+ mergeRequestId,
+ })
+ .then(mr => {
+ dispatch('setCurrentBranchId', mr.source_branch);
+
+ dispatch('getBranchData', {
+ projectId,
+ branchId: mr.source_branch,
+ });
+
+ return dispatch('getFiles', {
+ projectId,
+ branchId: mr.source_branch,
+ });
+ })
+ .then(() =>
+ dispatch('getMergeRequestVersions', {
+ projectId,
+ targetProjectId,
+ mergeRequestId,
+ }),
+ )
+ .then(() =>
+ dispatch('getMergeRequestChanges', {
+ projectId,
+ targetProjectId,
+ mergeRequestId,
+ }),
+ )
+ .then(mrChanges => {
+ if (mrChanges.changes.length) {
+ dispatch('updateActivityBarView', activityBarViews.review);
+ }
+
+ mrChanges.changes.forEach((change, ind) => {
+ const changeTreeEntry = state.entries[change.new_path];
+
+ if (changeTreeEntry) {
+ dispatch('setFileMrChange', {
+ file: changeTreeEntry,
+ mrChange: change,
+ });
+
+ if (ind < 10) {
+ dispatch('getFileData', {
+ path: change.new_path,
+ makeFileActive: ind === 0,
+ });
+ }
+ }
+ });
+ })
+ .catch(e => {
+ flash(__('Error while loading the merge request. Please try again.'));
+ throw e;
+ });
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index 501e25d452b..543dc6c0461 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -124,3 +124,35 @@ export const showBranchNotFoundError = ({ dispatch }, branchId) => {
actionPayload: branchId,
});
};
+
+export const openBranch = (
+ { dispatch, state },
+ { projectId, branchId, basePath },
+) => {
+ dispatch('setCurrentBranchId', branchId);
+
+ dispatch('getBranchData', {
+ projectId,
+ branchId,
+ });
+
+ return (
+ dispatch('getFiles', {
+ projectId,
+ branchId,
+ })
+ .then(() => {
+ if (basePath) {
+ const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
+ const treeEntryKey = Object.keys(state.entries).find(
+ key => key === path && !state.entries[key].pending,
+ );
+ const treeEntry = state.entries[treeEntryKey];
+
+ if (treeEntry) {
+ dispatch('handleTreeEntryAction', treeEntry);
+ }
+ }
+ })
+ );
+};
diff --git a/app/assets/javascripts/ide/stores/modules/branches/actions.js b/app/assets/javascripts/ide/stores/modules/branches/actions.js
index 74aa98ef9f9..f90c2d77f2b 100644
--- a/app/assets/javascripts/ide/stores/modules/branches/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/branches/actions.js
@@ -33,7 +33,4 @@ export const fetchBranches = ({ dispatch, rootGetters }, { search = '' }) => {
export const resetBranches = ({ commit }) => commit(types.RESET_BRANCHES);
-export const openBranch = ({ rootState, dispatch }, id) =>
- dispatch('goToRoute', `/project/${rootState.currentProjectId}/edit/${id}`, { root: true });
-
export default () => {};
diff --git a/app/assets/javascripts/jobs/components/artifacts_block.vue b/app/assets/javascripts/jobs/components/artifacts_block.vue
new file mode 100644
index 00000000000..525c5eec91a
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/artifacts_block.vue
@@ -0,0 +1,98 @@
+<script>
+ import TimeagoTooltiop from '~/vue_shared/components/time_ago_tooltip.vue';
+
+ export default {
+ components: {
+ TimeagoTooltiop,
+ },
+ props: {
+ // @build.artifacts_expired?
+ haveArtifactsExpired: {
+ type: Boolean,
+ required: true,
+ },
+ // @build.has_expiring_artifacts?
+ willArtifactsExpire: {
+ type: Boolean,
+ required: true,
+ },
+ expireAt: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ keepArtifactsPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ downloadArtifactsPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ browseArtifactsPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ };
+</script>
+<template>
+ <div class="block">
+ <div class="title">
+ {{ s__('Job|Job artifacts') }}
+ </div>
+
+ <p
+ v-if="haveArtifactsExpired"
+ class="js-artifacts-removed build-detail-row"
+ >
+ {{ s__('Job|The artifacts were removed') }}
+ </p>
+ <p
+ v-else-if="willArtifactsExpire"
+ class="js-artifacts-will-be-removed build-detail-row"
+ >
+ {{ s__('Job|The artifacts will be removed') }}
+ </p>
+
+ <timeago-tooltiop
+ v-if="expireAt"
+ :time="expireAt"
+ />
+
+ <div
+ class="btn-group d-flex"
+ role="group"
+ >
+ <a
+ v-if="keepArtifactsPath"
+ :href="keepArtifactsPath"
+ class="js-keep-artifacts btn btn-sm btn-default"
+ data-method="post"
+ >
+ {{ s__('Job|Keep') }}
+ </a>
+
+ <a
+ v-if="downloadArtifactsPath"
+ :href="downloadArtifactsPath"
+ class="js-download-artifacts btn btn-sm btn-default"
+ download
+ rel="nofollow"
+ >
+ {{ s__('Job|Download') }}
+ </a>
+
+ <a
+ v-if="browseArtifactsPath"
+ :href="browseArtifactsPath"
+ class="js-browse-artifacts btn btn-sm btn-default"
+ >
+ {{ s__('Job|Browse') }}
+ </a>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/erased_block.vue b/app/assets/javascripts/jobs/components/erased_block.vue
new file mode 100644
index 00000000000..d688eebfa95
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/erased_block.vue
@@ -0,0 +1,48 @@
+<script>
+import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+export default {
+ components: {
+ TimeagoTooltip,
+ },
+ props: {
+ erasedByUser: {
+ type: Boolean,
+ required: true,
+ },
+ username: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ linkToUser: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ erasedAt: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="prepend-top-default js-build-erased">
+ <div class="erased alert alert-warning">
+ <template v-if="erasedByUser">
+ {{ s__("Job|Job has been erased by") }}
+ <a :href="linkToUser">
+ {{ username }}
+ </a>
+ </template>
+ <template v-else>
+ {{ s__("Job|Job has been erased") }}
+ </template>
+
+ <timeago-tooltip
+ :time="erasedAt"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/job_log.vue b/app/assets/javascripts/jobs/components/job_log.vue
new file mode 100644
index 00000000000..3c4749d996b
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_log.vue
@@ -0,0 +1,33 @@
+<script>
+ export default {
+ name: 'JobLog',
+ props: {
+ trace: {
+ type: String,
+ required: true,
+ },
+ isReceivingBuildTrace: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ };
+</script>
+<template>
+ <pre class="build-trace">
+ <code
+ class="bash"
+ v-html="trace"
+ >
+ </code>
+
+ <div
+ v-if="isReceivingBuildTrace"
+ class="js-log-animation build-loader-animation"
+ >
+ <div class="dot"></div>
+ <div class="dot"></div>
+ <div class="dot"></div>
+ </div>
+ </pre>
+</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
index d2adf628050..36d4a3e2bc9 100644
--- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue
+++ b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
@@ -1,14 +1,16 @@
<script>
-import detailRow from './sidebar_detail_row.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-import timeagoMixin from '../../vue_shared/mixins/timeago';
-import { timeIntervalInWords } from '../../lib/utils/datetime_utility';
+import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
+import Icon from '~/vue_shared/components/icon.vue';
+import DetailRow from './sidebar_detail_row.vue';
export default {
name: 'SidebarDetailsBlock',
components: {
- detailRow,
- loadingIcon,
+ DetailRow,
+ LoadingIcon,
+ Icon,
},
mixins: [timeagoMixin],
props: {
@@ -20,16 +22,16 @@ export default {
type: Boolean,
required: true,
},
- canUserRetry: {
- type: Boolean,
- required: false,
- default: false,
- },
runnerHelpUrl: {
type: String,
required: false,
default: '',
},
+ terminalPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
computed: {
shouldRenderContent() {
@@ -92,7 +94,7 @@ export default {
{{ job.name }}
</strong>
<a
- v-if="canUserRetry"
+ v-if="job.retry_path"
:class="retryButtonClass"
:href="job.retry_path"
data-method="post"
@@ -100,6 +102,16 @@ export default {
>
{{ __('Retry') }}
</a>
+ <a
+ v-if="terminalPath"
+ :href="terminalPath"
+ class="js-terminal-link pull-right btn btn-primary
+ btn-inverted visible-md-block visible-lg-block"
+ target="_blank"
+ >
+ {{ __('Debug') }}
+ <icon name="external-link" />
+ </a>
<button
:aria-label="__('Toggle Sidebar')"
type="button"
@@ -125,7 +137,7 @@ export default {
{{ __('New issue') }}
</a>
<a
- v-if="canUserRetry"
+ v-if="job.retry_path"
:href="job.retry_path"
class="js-retry-job btn btn-inverted-secondary"
data-method="post"
diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/stuck_block.vue
new file mode 100644
index 00000000000..18883fea950
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/stuck_block.vue
@@ -0,0 +1,63 @@
+<script>
+/**
+ * Renders Stuck Runners block for job's view.
+ */
+export default {
+ props: {
+ hasNoRunnersForProject: {
+ type: Boolean,
+ required: true,
+ },
+ tags: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ runnersPath: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="bs-callout bs-callout-warning">
+ <p
+ v-if="hasNoRunnersForProject"
+ class="js-stuck-no-runners"
+ >
+ {{ s__(`Job|This job is stuck, because the project
+ doesn't have any runners online assigned to it.`) }}
+ </p>
+ <p
+ v-else-if="tags.length"
+ class="js-stuck-with-tags"
+ >
+ {{ s__(`This job is stuck, because you don't have
+ any active runners online with any of these tags assigned to them:`) }}
+ <span
+ v-for="(tag, index) in tags"
+ :key="index"
+ class="badge badge-primary"
+ >
+ {{ tag }}
+ </span>
+ </p>
+ <p
+ v-else
+ class="js-stuck-no-active-runner"
+ >
+ {{ s__(`This job is stuck, because you don't
+ have any active runners that can run this job.`) }}
+ </p>
+
+ {{ __("Go to") }}
+ <a
+ v-if="runnersPath"
+ :href="runnersPath"
+ class="js-runners-path"
+ >
+ {{ __("Runners page") }}
+ </a>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
index 0db7b95636c..a84324f14b2 100644
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ b/app/assets/javascripts/jobs/job_details_bundle.js
@@ -52,9 +52,9 @@ export default () => {
return createElement('details-block', {
props: {
isLoading: this.mediator.state.isLoading,
- canUserRetry: !!('canUserRetry' in detailsBlockDataset),
job: this.mediator.store.state.job,
runnerHelpUrl: dataset.runnerHelpUrl,
+ terminalPath: detailsBlockDataset.terminalPath,
},
});
},
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index cb851ff6745..6499b919787 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -4,7 +4,7 @@
import $ from 'jquery';
import _ from 'underscore';
-import { __ } from './locale';
+import { sprintf, __ } from './locale';
import axios from './lib/utils/axios_utils';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import DropdownUtils from './filtered_search/dropdown_utils';
@@ -39,7 +39,7 @@ export default class LabelsSelect {
showNo = $dropdown.data('showNo');
showAny = $dropdown.data('showAny');
showMenuAbove = $dropdown.data('showMenuAbove');
- defaultLabel = $dropdown.data('defaultLabel') || 'Label';
+ defaultLabel = $dropdown.data('defaultLabel') || __('Label');
abilityName = $dropdown.data('abilityName');
$selectbox = $dropdown.closest('.selectbox');
$block = $selectbox.closest('.block');
@@ -267,7 +267,10 @@ export default class LabelsSelect {
return selectedLabels;
}
else if (selectedLabels.length) {
- return selectedLabels[0] + " +" + (selectedLabels.length - 1) + " more";
+ return sprintf(__('%{firstLabel} +%{labelCount} more'), {
+ firstLabel: selectedLabels[0],
+ labelCount: selectedLabels.length - 1
+ });
}
else {
return defaultLabel;
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
index ffc84dc106b..78cf5406e43 100644
--- a/app/assets/javascripts/pages/projects/settings/repository/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
@@ -1,3 +1,9 @@
import initForm from '../form';
+import MirrorRepos from './mirror_repos';
-document.addEventListener('DOMContentLoaded', initForm);
+document.addEventListener('DOMContentLoaded', () => {
+ initForm();
+
+ const mirrorReposContainer = document.querySelector('.js-mirror-settings');
+ if (mirrorReposContainer) new MirrorRepos(mirrorReposContainer).init();
+});
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js b/app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js
new file mode 100644
index 00000000000..4c56af20cc3
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/repository/show/mirror_repos.js
@@ -0,0 +1,94 @@
+import $ from 'jquery';
+import _ from 'underscore';
+import { __ } from '~/locale';
+import Flash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+
+export default class MirrorRepos {
+ constructor(container) {
+ this.$container = $(container);
+ this.$form = $('.js-mirror-form', this.$container);
+ this.$urlInput = $('.js-mirror-url', this.$form);
+ this.$protectedBranchesInput = $('.js-mirror-protected', this.$form);
+ this.$table = $('.js-mirrors-table-body', this.$container);
+ this.mirrorEndpoint = this.$form.data('projectMirrorEndpoint');
+ }
+
+ init() {
+ this.initMirrorPush();
+ this.registerUpdateListeners();
+ }
+
+ initMirrorPush() {
+ this.$passwordGroup = $('.js-password-group', this.$container);
+ this.$password = $('.js-password', this.$passwordGroup);
+ this.$authMethod = $('.js-auth-method', this.$form);
+
+ this.$authMethod.on('change', () => this.togglePassword());
+ this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl());
+ }
+
+ updateUrl() {
+ let val = this.$urlInput.val();
+
+ if (this.$password) {
+ const password = this.$password.val();
+ if (password) val = val.replace('@', `:${password}@`);
+ }
+
+ $('.js-mirror-url-hidden', this.$form).val(val);
+ }
+
+ updateProtectedBranches() {
+ const val = this.$protectedBranchesInput.get(0).checked
+ ? this.$protectedBranchesInput.val()
+ : '0';
+ $('.js-mirror-protected-hidden', this.$form).val(val);
+ }
+
+ registerUpdateListeners() {
+ this.debouncedUpdateUrl = _.debounce(() => this.updateUrl(), 200);
+ this.$urlInput.on('input', () => this.debouncedUpdateUrl());
+ this.$protectedBranchesInput.on('change', () => this.updateProtectedBranches());
+ this.$table.on('click', '.js-delete-mirror', event => this.deleteMirror(event));
+ }
+
+ togglePassword() {
+ const isPassword = this.$authMethod.val() === 'password';
+
+ if (!isPassword) {
+ this.$password.val('');
+ this.updateUrl();
+ }
+ this.$passwordGroup.collapse(isPassword ? 'show' : 'hide');
+ }
+
+ deleteMirror(event, existingPayload) {
+ const $target = $(event.currentTarget);
+ let payload = existingPayload;
+
+ if (!payload) {
+ payload = {
+ project: {
+ remote_mirrors_attributes: {
+ id: $target.data('mirrorId'),
+ enabled: 0,
+ },
+ },
+ };
+ }
+
+ return axios
+ .put(this.mirrorEndpoint, payload)
+ .then(() => this.removeRow($target))
+ .catch(() => Flash(__('Failed to remove mirror.')));
+ }
+
+ /* eslint-disable class-methods-use-this */
+ removeRow($target) {
+ const row = $target.closest('tr');
+ $('.js-delete-mirror', row).tooltip('hide');
+ row.remove();
+ }
+ /* eslint-enable class-methods-use-this */
+}
diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
index 140475b4dfa..7b37f4e9a97 100644
--- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
+++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue
@@ -1,10 +1,10 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
- import { componentNames } from '~/vue_shared/components/reports/issue_body';
- import ReportSection from '~/vue_shared/components/reports/report_section.vue';
- import SummaryRow from '~/vue_shared/components/reports/summary_row.vue';
- import IssuesList from '~/vue_shared/components/reports/issues_list.vue';
+ import { componentNames } from './issue_body';
+ import ReportSection from './report_section.vue';
+ import SummaryRow from './summary_row.vue';
+ import IssuesList from './issues_list.vue';
import Modal from './modal.vue';
import createStore from '../store';
import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils';
diff --git a/app/assets/javascripts/vue_shared/components/reports/issue_body.js b/app/assets/javascripts/reports/components/issue_body.js
index 54dfb7b16bf..8b5af263d50 100644
--- a/app/assets/javascripts/vue_shared/components/reports/issue_body.js
+++ b/app/assets/javascripts/reports/components/issue_body.js
@@ -1,4 +1,4 @@
-import TestIssueBody from '~/reports/components/test_issue_body.vue';
+import TestIssueBody from './test_issue_body.vue';
export const components = {
TestIssueBody,
diff --git a/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue b/app/assets/javascripts/reports/components/issue_status_icon.vue
index f8189117ac3..85811698a37 100644
--- a/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue
+++ b/app/assets/javascripts/reports/components/issue_status_icon.vue
@@ -1,11 +1,10 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
-
import {
STATUS_FAILED,
STATUS_NEUTRAL,
STATUS_SUCCESS,
-} from '~/vue_shared/components/reports/constants';
+} from '../constants';
export default {
name: 'IssueStatusIcon',
diff --git a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue
index 2545e84f932..df42201b5de 100644
--- a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue
+++ b/app/assets/javascripts/reports/components/issues_list.vue
@@ -1,10 +1,10 @@
<script>
-import IssuesBlock from '~/vue_shared/components/reports/report_issues.vue';
+import IssuesBlock from '~/reports/components/report_issues.vue';
import {
STATUS_SUCCESS,
STATUS_FAILED,
STATUS_NEUTRAL,
-} from '~/vue_shared/components/reports/constants';
+} from '~/reports/constants';
/**
* Renders block of issues
diff --git a/app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue b/app/assets/javascripts/reports/components/modal_open_name.vue
index 4f81cee2a38..4f81cee2a38 100644
--- a/app/assets/javascripts/vue_shared/components/reports/modal_open_name.vue
+++ b/app/assets/javascripts/reports/components/modal_open_name.vue
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue b/app/assets/javascripts/reports/components/report_issues.vue
index 1f13e555b31..c553a374f66 100644
--- a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue
+++ b/app/assets/javascripts/reports/components/report_issues.vue
@@ -1,6 +1,6 @@
<script>
-import IssueStatusIcon from '~/vue_shared/components/reports/issue_status_icon.vue';
-import { components, componentNames } from '~/vue_shared/components/reports/issue_body';
+import IssueStatusIcon from '~/reports/components/issue_status_icon.vue';
+import { components, componentNames } from '~/reports/components/issue_body';
export default {
name: 'ReportIssues',
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_link.vue b/app/assets/javascripts/reports/components/report_link.vue
index 74d68f9f439..74d68f9f439 100644
--- a/app/assets/javascripts/vue_shared/components/reports/report_link.vue
+++ b/app/assets/javascripts/reports/components/report_link.vue
diff --git a/app/assets/javascripts/vue_shared/components/reports/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue
index a6dbf21092b..dc609d6f90e 100644
--- a/app/assets/javascripts/vue_shared/components/reports/report_section.vue
+++ b/app/assets/javascripts/reports/components/report_section.vue
@@ -1,8 +1,8 @@
<script>
import { __ } from '~/locale';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import Popover from '~/vue_shared/components/help_popover.vue';
import IssuesList from './issues_list.vue';
-import Popover from '../help_popover.vue';
const LOADING = 'LOADING';
const ERROR = 'ERROR';
diff --git a/app/assets/javascripts/vue_shared/components/reports/summary_row.vue b/app/assets/javascripts/reports/components/summary_row.vue
index 063beab58fc..4456d84c968 100644
--- a/app/assets/javascripts/vue_shared/components/reports/summary_row.vue
+++ b/app/assets/javascripts/reports/components/summary_row.vue
@@ -1,7 +1,7 @@
<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';
+import Popover from '~/vue_shared/components/help_popover.vue';
/**
* Renders the summary row for each report
diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/reports/constants.js
index 807ecb1039e..c323dc543f3 100644
--- a/app/assets/javascripts/reports/constants.js
+++ b/app/assets/javascripts/reports/constants.js
@@ -11,6 +11,8 @@ export const SUCCESS = 'SUCCESS';
export const STATUS_FAILED = 'failed';
export const STATUS_SUCCESS = 'success';
+export const STATUS_NEUTRAL = 'neutral';
+
export const ICON_WARNING = 'warning';
export const ICON_SUCCESS = 'success';
export const ICON_NOTFOUND = 'notfound';
diff --git a/app/assets/javascripts/vue_shared/components/reports/constants.js b/app/assets/javascripts/vue_shared/components/reports/constants.js
deleted file mode 100644
index dbde648bfdb..00000000000
--- a/app/assets/javascripts/vue_shared/components/reports/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const STATUS_FAILED = 'failed';
-export const STATUS_SUCCESS = 'success';
-export const STATUS_NEUTRAL = 'neutral';
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index c20738a20c3..e8e707cf90c 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -14,7 +14,6 @@ $border-radius-base: 3px !default;
$modal-body-bg: $white-light;
$input-border: $border-color;
-$input-border-focus: $focus-border-color;
$padding-base-vertical: $gl-vert-padding;
$padding-base-horizontal: $gl-padding;
@@ -86,7 +85,7 @@ strong {
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
hr {
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index c46b0b5db09..b1a20c06910 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -1,4 +1,5 @@
@import 'framework/variables';
+@import 'framework/variables_overrides';
@import 'framework/mixins';
@import 'bootstrap';
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 369556dc24e..4c7c399a3ca 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -103,6 +103,7 @@
display: flex;
a {
+ width: 100%;
display: flex;
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 646cedd79ed..0dc7aa4ef68 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -350,7 +350,7 @@
&:focus {
cursor: text;
box-shadow: none;
- border-color: lighten($dropdown-input-focus-border, 20%);
+ border-color: lighten($blue-300, 20%);
color: $gray-darkest;
background-color: $gray-light;
}
@@ -434,7 +434,7 @@
&:hover,
&:active,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
@@ -445,7 +445,7 @@
&:hover,
&:active,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index af17210f341..268e68dbb15 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -114,7 +114,7 @@ hr {
.item-title { font-weight: $gl-font-weight-bold; }
.author-link {
- color: $gl-link-color;
+ color: $blue-600;
}
.back-link {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index eebce8b9011..83bc3776178 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -615,7 +615,7 @@
&:focus {
color: $dropdown-link-color;
- border-color: $dropdown-input-focus-border;
+ border-color: $blue-300;
box-shadow: 0 0 4px $dropdown-input-focus-shadow;
~ .fa {
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index 3cde0490371..a8ec1e1145a 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -2,7 +2,7 @@ gl-emoji {
font-style: normal;
display: inline-flex;
vertical-align: middle;
- font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1.5em;
line-height: 0.9;
}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 00eac1688f2..54882633fea 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -286,19 +286,19 @@ span.idiff {
.new-file {
a {
- color: $gl-text-green;
+ color: $green-600;
}
}
.renamed-file {
a {
- color: $gl-text-orange;
+ color: $orange-600;
}
}
.deleted-file {
a {
- color: $gl-text-red;
+ color: $red-500;
}
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 5d79610b21e..9b09ed0ed0a 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -205,7 +205,7 @@
&.focus,
&.focus:hover {
- border-color: $dropdown-input-focus-border;
+ border-color: $blue-300;
box-shadow: 0 0 4px $search-input-focus-shadow-color;
}
@@ -294,7 +294,7 @@
&:hover,
&:focus {
color: $gl-text-color;
- border-color: $dropdown-input-focus-border;
+ border-color: $blue-300;
outline: none;
}
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index e4bcb92876d..7a4c3914fb0 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -16,10 +16,10 @@
color: $gl-text-color;
a {
- color: $gl-link-color;
+ color: $blue-600;
&:hover {
- color: $gl-link-hover-color;
+ color: $blue-800;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index d7149d93622..a70eece8f68 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -170,7 +170,7 @@ label {
}
.form-control::-webkit-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
.input-group {
@@ -201,7 +201,7 @@ label {
}
.gl-show-field-errors {
- .form-control {
+ .form-control:not(textarea) {
height: 34px;
}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 7290a174668..d8391b59a8c 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -179,7 +179,7 @@
&:hover,
&:focus {
svg {
- fill: $gl-link-color;
+ fill: $blue-600;
}
}
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index ffb40166c15..7d53a631cdf 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -79,7 +79,6 @@ body.modal-open {
.modal {
background-color: $black-transparent;
- z-index: 2100;
@include media-breakpoint-up(md) {
.modal-dialog {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index b40dcf93969..88d2f0aaf85 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -153,7 +153,7 @@
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
&:focus {
- border-color: $input-border-focus;
+ border-color: $blue-300;
}
&.select2-active {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 473ca408c04..eccc814b747 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -180,7 +180,7 @@
}
a > code {
- color: $gl-link-color;
+ color: $blue-600;
}
dd {
@@ -423,25 +423,25 @@ h4 {
input,
textarea {
&::-webkit-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
// support firefox 19+ vendor prefix
&::-moz-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
opacity: 1; // FF defaults to 0.54
}
// scss-lint:disable PseudoElement
// support Edge vendor prefix
&::-ms-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
// scss-lint:disable PseudoElement
// support IE vendor prefix
&:-ms-input-placeholder {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 4db9efff6ee..866cb88ba5b 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -177,7 +177,6 @@ $border-gray-dark: darken($white-normal, $darken-border-factor);
* UI elements
*/
$border-color: #e5e5e5;
-$focus-border-color: $blue-300;
$well-expand-item: #e8f2f7;
$well-inner-border: #eef0f2;
$well-light-border: #f1f1f1;
@@ -196,19 +195,10 @@ $gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, 0.85);
$gl-text-color-disabled: #919191;
-$gl-text-green: $green-600;
-$gl-text-green-hover: $green-700;
-$gl-text-red: $red-500;
-$gl-text-orange: $orange-600;
-$gl-link-color: $blue-600;
-$gl-link-hover-color: $blue-800;
$gl-grayish-blue: #7f8fa4;
-$gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54;
-$gl-header-nav-hover-color: #434343;
-$placeholder-text-color: $gl-text-color-tertiary;
/*
* Lists
@@ -227,7 +217,7 @@ $list-warning-row-color: $orange-700;
/*
* Markdown
*/
-$md-link-color: $gl-link-color;
+$md-link-color: $blue-600;
$md-area-border: #ddd;
/*
@@ -374,7 +364,7 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%);
$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
'Courier New', 'andale mono', 'lucida console', monospace;
$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
- 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
+ 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
/*
* Dropdowns
@@ -392,8 +382,7 @@ $dropdown-divider-color: rgba(#000, 0.1);
$dropdown-title-btn-color: #bfbfbf;
$dropdown-input-color: #555;
$dropdown-input-fa-color: #c7c7c7;
-$dropdown-input-focus-border: $focus-border-color;
-$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, 0.4);
+$dropdown-input-focus-shadow: rgba($blue-300, 0.4);
$dropdown-loading-bg: rgba(#fff, 0.6);
$dropdown-chevron-size: 10px;
$dropdown-toggle-active-border-color: darken($border-color, 14%);
@@ -585,8 +574,8 @@ $commit-message-text-area-bg: rgba(0, 0, 0, 0);
$common-gray: $gl-text-color;
$common-gray-light: #bbb;
$common-gray-dark: #444;
-$common-red: $gl-text-red;
-$common-green: $gl-text-green;
+$common-red: $red-500;
+$common-green: $green-600;
/*
* Editor
@@ -834,19 +823,3 @@ Prometheus
$prometheus-table-row-highlight-color: $theme-gray-100;
$priority-label-empty-state-width: 114px;
-
-/*
- * Override Bootstrap 4 variables
- */
-
-$secondary: $gray-light;
-$input-disabled-bg: $gray-light;
-$input-border-color: $theme-gray-200;
-$input-color: $gl-text-color;
-$font-family-sans-serif: $regular-font;
-$font-family-monospace: $monospace-font;
-$input-line-height: 20px;
-$btn-line-height: 20px;
-$table-accent-bg: $gray-light;
-$card-border-color: $border-color;
-$card-cap-bg: $gray-light;
diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss
new file mode 100644
index 00000000000..b9c343fa2e9
--- /dev/null
+++ b/app/assets/stylesheets/framework/variables_overrides.scss
@@ -0,0 +1,16 @@
+/*
+ * This file is only for overriding Bootstrap 4 variables.
+ * Please add any new variables to variables.scss
+ */
+
+$secondary: $gray-light;
+$input-disabled-bg: $gray-light;
+$input-border-color: $theme-gray-200;
+$input-color: $gl-text-color;
+$font-family-sans-serif: $regular-font;
+$font-family-monospace: $monospace-font;
+$input-line-height: 20px;
+$btn-line-height: 20px;
+$table-accent-bg: $gray-light;
+$card-border-color: $border-color;
+$card-cap-bg: $gray-light;
diff --git a/app/assets/stylesheets/framework/zen.scss b/app/assets/stylesheets/framework/zen.scss
index dbd3144b9b4..f2d296fb875 100644
--- a/app/assets/stylesheets/framework/zen.scss
+++ b/app/assets/stylesheets/framework/zen.scss
@@ -44,7 +44,7 @@
color: $gl-text-color-secondary;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 2b8163b8c68..af91497d0ea 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -1158,7 +1158,7 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index a68b47b1d02..91f470ca709 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -225,7 +225,7 @@
outline: 0;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index e8158cd7f6b..1696d18584d 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -221,7 +221,7 @@
color: $gl-text-color;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index bce83bf0dd0..10764e0f3df 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -279,7 +279,7 @@
}
&.autodevops-link {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -321,7 +321,7 @@
}
.commit-sha {
- color: $gl-link-color;
+ color: $blue-600;
}
.commit-row-message {
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index e2c0a7a6225..bba9f38d3dd 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -360,7 +360,7 @@
}
.commit-sha {
- color: $gl-link-color;
+ color: $blue-600;
line-height: 1.3;
vertical-align: top;
font-weight: $gl-font-weight-normal;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 591e21243ed..47778110bae 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -511,13 +511,13 @@
padding: 0;
background-color: transparent;
border: 0;
- color: $gl-link-color;
+ color: $blue-600;
font-weight: $gl-font-weight-bold;
&:hover,
&:focus {
outline: none;
- color: $gl-link-hover-color;
+ color: $blue-800;
}
.caret-icon {
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index ddd1f8cc98a..892da152b5f 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -36,10 +36,7 @@
line-height: 35px;
padding-top: 7px;
padding-bottom: 7px;
-
- .float-right {
- height: 20px;
- }
+ display: flex;
}
.editor-ref {
@@ -60,20 +57,18 @@
.new-file-name {
display: inline-block;
- max-width: 450px;
+ max-width: 420px;
float: left;
@media(max-width: map-get($grid-breakpoints, lg)-1) {
- width: 280px;
- }
-
- @media(max-width: map-get($grid-breakpoints, md)-1) {
width: 180px;
}
}
.file-buttons {
- font-size: 0;
+ display: flex;
+ flex: 1;
+ justify-content: flex-end;
}
.select2 {
@@ -111,12 +106,10 @@
}
-@include media-breakpoint-down(xs) {
+@include media-breakpoint-down(sm) {
.file-editor {
.file-title {
- .float-right {
- height: auto;
- }
+ display: block;
}
.new-file-name {
@@ -144,6 +137,10 @@
}
}
}
+
+ .editor-ref {
+ max-width: 250px;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 8a074017344..179c0964567 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -479,10 +479,10 @@
.deploy-info-text-link {
font-family: $monospace-font;
- fill: $gl-link-color;
+ fill: $blue-600;
&:hover {
- fill: $gl-link-hover-color;
+ fill: $blue-800;
}
}
diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss
index 49d8a5d959b..22fce893fd7 100644
--- a/app/assets/stylesheets/pages/graph.scss
+++ b/app/assets/stylesheets/pages/graph.scss
@@ -24,11 +24,11 @@
}
.graph-additions {
- color: $gl-text-green;
+ color: $green-600;
}
.graph-deletions {
- color: $gl-text-red;
+ color: $red-500;
}
}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 1587aebfe1d..fa8a0f26b5d 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -419,6 +419,7 @@
.stats {
position: relative;
line-height: normal;
+ text-align: right;
flex-shrink: 0;
> span {
@@ -464,7 +465,7 @@
}
.last-updated {
- position: absolute;
+ position: relative;
right: 12px;
min-width: 250px;
text-align: right;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 8e78d9f65eb..d16a63d009a 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -141,7 +141,7 @@
color: inherit;
&:hover {
- color: $gl-link-hover-color;
+ color: $blue-800;
.avatar {
border-color: rgba($avatar-border, .2);
@@ -241,7 +241,7 @@
&:hover {
text-decoration: underline;
- color: $gl-link-hover-color;
+ color: $blue-800;
}
}
}
@@ -329,7 +329,7 @@
}
.btn-secondary-hover-link:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
.sidebar-collapsed-icon {
@@ -448,8 +448,8 @@
}
.todo-undone {
- color: $gl-link-color;
- fill: $gl-link-color;
+ color: $blue-600;
+ fill: $blue-600;
}
.author {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 212e5979273..0f95fb911e1 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -157,7 +157,7 @@ ul.related-merge-requests > li {
.issuable-email-modal-btn {
padding: 0;
- color: $gl-link-color;
+ color: $blue-600;
background-color: transparent;
border: 0;
outline: 0;
@@ -190,7 +190,7 @@ ul.related-merge-requests > li {
.create-mr-dropdown-wrap {
.ref::selection {
- color: $placeholder-text-color;
+ color: $gl-text-color-tertiary;
}
.dropdown {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 2b40404971c..d32943fceec 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -114,7 +114,7 @@
}
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
&.remove-row {
color: $gl-danger;
@@ -253,7 +253,7 @@
text-align: right;
padding: 0;
position: relative;
- top: -3px;
+ margin: 0;
}
.label-badge {
@@ -274,6 +274,7 @@
.label-links {
list-style: none;
+ margin: 0;
padding: 0;
white-space: nowrap;
}
@@ -342,10 +343,10 @@
&.remove-row {
&:hover {
- color: $gl-text-red;
+ color: $red-500;
svg {
- fill: $gl-text-red;
+ fill: $red-500;
}
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 46437ce5841..1e92582d6d9 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -30,7 +30,7 @@
.milestone-progress {
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index dcf590e7331..4f861d43f55 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -69,7 +69,7 @@
.comment-toolbar,
.nav-links {
- border-color: $focus-border-color;
+ border-color: $blue-300;
}
}
@@ -306,7 +306,7 @@
&:hover,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
outline: 0;
}
@@ -424,7 +424,7 @@
.uploading-error-icon,
.uploading-error-message {
- color: $gl-text-red;
+ color: $red-500;
}
.uploading-error-message {
@@ -443,7 +443,7 @@
.attach-new-file,
.button-attach-file,
.retry-uploading-link {
- color: $gl-link-color;
+ color: $blue-600;
padding: 0;
background: none;
border: 0;
@@ -452,5 +452,5 @@
}
.markdown-selector {
- color: $gl-link-color;
+ color: $blue-600;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index c369d89d63c..8d28daac750 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -210,7 +210,7 @@ ul.notes {
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
p {
@@ -253,14 +253,14 @@ ul.notes {
overflow: hidden;
.system-note-commit-list-toggler {
- color: $gl-link-color;
+ color: $blue-600;
padding: 10px 0 0;
cursor: pointer;
position: relative;
z-index: 2;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
text-decoration: underline;
}
}
@@ -390,7 +390,7 @@ ul.notes {
color: inherit;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
&:focus,
@@ -451,7 +451,7 @@ ul.notes {
.discussion-headline-light {
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -560,12 +560,12 @@ ul.notes {
&:hover,
&.is-active {
.danger-highlight {
- color: $gl-text-red;
+ color: $red-500;
}
.link-highlight {
- color: $gl-link-color;
- fill: $gl-link-color;
+ color: $blue-600;
+ fill: $blue-600;
}
.award-control-icon-neutral {
@@ -597,13 +597,13 @@ ul.notes {
transition: color 0.1s linear;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
&:focus {
text-decoration: underline;
outline: none;
- color: $gl-link-color;
+ color: $blue-600;
}
.fa {
@@ -673,7 +673,7 @@ ul.notes {
}
a {
- color: $gl-link-color;
+ color: $blue-600;
}
}
@@ -759,16 +759,16 @@ ul.notes {
&:not(.is-disabled) {
&:hover,
&:focus {
- color: $gl-text-green;
+ color: $green-600;
}
}
&.is-active {
- color: $gl-text-green;
+ color: $green-600;
&:hover,
&:focus {
- color: $gl-text-green-hover;
+ color: $green-700;
}
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index b68c89c25d8..ad057ed3c83 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -175,7 +175,7 @@
}
.commit-sha {
- color: $gl-link-color;
+ color: $blue-600;
}
.badge {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 944421604fe..fffb440027c 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -388,7 +388,7 @@
line-height: $gl-btn-line-height;
&:hover {
- color: $gl-link-color;
+ color: $blue-600;
}
}
}
@@ -754,8 +754,19 @@
}
.repository-languages-bar {
- height: 6px;
- margin-bottom: 8px;
+ height: 8px;
+ margin-bottom: $gl-padding-8;
+ background-color: $white-light;
+ border-radius: $border-radius-default;
+
+ .progress-bar {
+ margin-right: 2px;
+ padding: 0 $gl-padding-4;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
}
pre.light-well {
@@ -823,10 +834,6 @@ pre.light-well {
.avatar-container {
align-self: flex-start;
-
- > a {
- width: 100%;
- }
}
.project-details {
@@ -954,7 +961,7 @@ pre.light-well {
margin-left: 5px;
&.is-done {
- color: $gl-text-green;
+ color: $green-600;
}
}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 60b280fd12e..5b3a468cd1c 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -23,7 +23,7 @@ $search-avatar-size: 16px;
.search-text-input:hover,
.form-control:hover,
:not[readonly] {
- border-color: lighten($dropdown-input-focus-border, 20%);
+ border-color: lighten($blue-300, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
@@ -127,7 +127,7 @@ input[type='checkbox']:hover {
&.search-active {
form {
@extend .form-control:focus;
- border-color: $dropdown-input-focus-border;
+ border-color: $blue-300;
box-shadow: none;
@include media-breakpoint-up(xl) {
@@ -259,6 +259,6 @@ input[type='checkbox']:hover {
&:hover,
&:focus {
- color: $gl-link-color;
+ color: $blue-600;
}
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 839ac5ba59b..fb03970f64f 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -301,3 +301,17 @@
margin-bottom: 0;
}
}
+
+.mirror-error-badge {
+ background-color: $error-bg;
+ border-radius: $border-radius-default;
+ color: $white-light;
+}
+
+.push-pull-table {
+ margin-top: 1em;
+
+ .mirror-action-buttons {
+ padding-right: 0;
+ }
+}
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 7a93c4dfa28..57d43beaf21 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -6,7 +6,7 @@
left: 0;
top: 0;
width: 100%;
- z-index: 2000;
+ z-index: 1039;
height: $performance-bar-height;
background: $black;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 05ed3669a41..e5b38898a67 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -11,7 +11,6 @@ class ApplicationController < ActionController::Base
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
- before_action :limit_unauthenticated_session_times
before_action :authenticate_sessionless_user!
before_action :authenticate_user!
before_action :enforce_terms!, if: :should_enforce_terms?
@@ -27,6 +26,7 @@ class ApplicationController < ActionController::Base
around_action :set_locale
after_action :set_page_title_header, if: :json_request?
+ after_action :limit_unauthenticated_session_times
protect_from_forgery with: :exception, prepend: true
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index ed20302487c..461f26561f1 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -5,14 +5,14 @@ class NotificationSettingsController < ApplicationController
return render_404 unless can_read?(resource)
@notification_setting = current_user.notification_settings_for(resource)
- @saved = @notification_setting.update(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params_for(resource))
render_response
end
def update
@notification_setting = current_user.notification_settings.find(params[:id])
- @saved = @notification_setting.update(notification_setting_params)
+ @saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source))
render_response
end
@@ -42,8 +42,8 @@ class NotificationSettingsController < ApplicationController
}
end
- def notification_setting_params
- allowed_fields = NotificationSetting::EMAIL_EVENTS.dup
+ def notification_setting_params_for(source)
+ allowed_fields = NotificationSetting.email_events(source).dup
allowed_fields << :level
params.require(:notification_setting).permit(allowed_fields)
end
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 07627ffb69f..a8f73ed5cb0 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -32,13 +32,8 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
end
def target
- case params[:type]&.downcase
- when 'issue'
- IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:type_id])
- when 'mergerequest'
- MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:type_id])
- when 'commit'
- @project.commit(params[:type_id])
- end
+ QuickActions::TargetService
+ .new(project, current_user)
+ .execute(params[:type], params[:type_id])
end
end
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index d48dae8f06d..494f785e305 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -1,28 +1,10 @@
module AvatarsHelper
def project_icon(project_id, options = {})
- project =
- if project_id.respond_to?(:avatar_url)
- project_id
- else
- Project.find_by_full_path(project_id)
- end
-
- if project.avatar_url
- image_tag project.avatar_url, options
- else # generated icon
- project_identicon(project, options)
- end
+ source_icon(Project, project_id, options)
end
- def project_identicon(project, options = {})
- bg_key = (project.id % 7) + 1
- options[:class] ||= ''
- options[:class] << ' identicon'
- options[:class] << " bg#{bg_key}"
-
- content_tag(:div, class: options[:class]) do
- project.name[0, 1].upcase
- end
+ def group_icon(group_id, options = {})
+ source_icon(Group, group_id, options)
end
# Takes both user and email and returns the avatar_icon by
@@ -123,4 +105,32 @@ module AvatarsHelper
mail_to(options[:user_email], avatar)
end
end
+
+ private
+
+ def source_icon(klass, source_id, options = {})
+ source =
+ if source_id.respond_to?(:avatar_url)
+ source_id
+ else
+ klass.find_by_full_path(source_id)
+ end
+
+ if source.avatar_url
+ image_tag source.avatar_url, options
+ else
+ source_identicon(source, options)
+ end
+ end
+
+ def source_identicon(source, options = {})
+ bg_key = (source.id % 7) + 1
+ options[:class] ||= ''
+ options[:class] << ' identicon'
+ options[:class] << " bg#{bg_key}"
+
+ content_tag(:div, class: options[:class].strip) do
+ source.name[0, 1].upcase
+ end
+ end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 3c5c8bbd71b..5b51d2f2425 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -33,11 +33,6 @@ module GroupsHelper
.count
end
- def group_icon(group, options = {})
- img_path = group_icon_url(group, options)
- image_tag img_path, options
- end
-
def group_icon_url(group, options = {})
if group.is_a?(String)
group = Group.find_by_full_path(group)
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 41084ec686f..a8a10c98d69 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -62,6 +62,8 @@ module IconsHelper
names = "key"
when "two-factor"
names = "key"
+ when "google_oauth2"
+ names = "google"
end
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
diff --git a/app/helpers/mirror_helper.rb b/app/helpers/mirror_helper.rb
new file mode 100644
index 00000000000..93ed22513ac
--- /dev/null
+++ b/app/helpers/mirror_helper.rb
@@ -0,0 +1,5 @@
+module MirrorHelper
+ def mirrors_form_data_attributes
+ { project_mirror_endpoint: project_mirror_path(@project) }
+ end
+end
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 3e42063224e..a185f2916d4 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -83,21 +83,11 @@ module NotificationsHelper
end
def notification_event_name(event)
- # All values from NotificationSetting::EMAIL_EVENTS
+ # All values from NotificationSetting.email_events
case event
when :success_pipeline
s_('NotificationEvent|Successful pipeline')
else
- N_('NotificationEvent|New note')
- N_('NotificationEvent|New issue')
- N_('NotificationEvent|Reopen issue')
- N_('NotificationEvent|Close issue')
- N_('NotificationEvent|Reassign issue')
- N_('NotificationEvent|New merge request')
- N_('NotificationEvent|Close merge request')
- N_('NotificationEvent|Reassign merge request')
- N_('NotificationEvent|Merge merge request')
- N_('NotificationEvent|Failed pipeline')
s_(event.to_s.humanize)
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 9292929be98..3c69677baf0 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -226,7 +226,7 @@ module Ci
end
def cancelable?
- active?
+ active? || created?
end
def retryable?
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index d7c5f29be96..17b7ee4f07e 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -33,7 +33,7 @@ module Ci
where(file_type: types)
end
- delegate :exists?, :open, to: :file
+ delegate :filename, :exists?, :open, to: :file
enum file_type: {
archive: 1,
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 8b9f4490ffa..27fbdc3e386 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -448,6 +448,10 @@ class Commit
true
end
+ def to_ability_name
+ model_name.singular
+ end
+
def touch
# no-op but needs to be defined since #persisted? is defined
end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 1df3a51a7fc..1600acfc575 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -45,6 +45,15 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline
].freeze
+ # Update unfound_translations.rb when events are changed
+ def self.email_events(source = nil)
+ EMAIL_EVENTS
+ end
+
+ def email_events
+ self.class.email_events(source)
+ end
+
EXCLUDED_PARTICIPATING_EVENTS = [
:success_pipeline
].freeze
diff --git a/app/models/programming_language.rb b/app/models/programming_language.rb
index 400d6c407a7..0e667dac21e 100644
--- a/app/models/programming_language.rb
+++ b/app/models/programming_language.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProgrammingLanguage < ActiveRecord::Base
validates :name, presence: true
validates :color, allow_blank: false, color: true
diff --git a/app/models/project.rb b/app/models/project.rb
index 36089995ed3..7735f23cb9e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -470,6 +470,24 @@ class Project < ActiveRecord::Base
}x
end
+ def reference_postfix
+ '>'
+ end
+
+ def reference_postfix_escaped
+ '&gt;'
+ end
+
+ # Pattern used to extract `namespace/project>` project references from text.
+ # '>' or its escaped form ('&gt;') are checked for because '>' is sometimes escaped
+ # when the reference comes from an external source.
+ def markdown_reference_pattern
+ %r{
+ #{reference_pattern}
+ (#{reference_postfix}|#{reference_postfix_escaped})
+ }x
+ end
+
def trending
joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id')
.reorder('trending_projects.id ASC')
@@ -908,6 +926,10 @@ class Project < ActiveRecord::Base
end
end
+ def to_reference_with_postfix
+ "#{to_reference(full: true)}#{self.class.reference_postfix}"
+ end
+
# `from` argument can be a Namespace or Project.
def to_reference(from = nil, full: false)
if full || cross_namespace_reference?(from)
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
index 4f289e6e215..35c19049c04 100644
--- a/app/models/project_services/asana_service.rb
+++ b/app/models/project_services/asana_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'asana'
class AsanaService < Service
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
index 4234b8044e5..60575e45a90 100644
--- a/app/models/project_services/assembla_service.rb
+++ b/app/models/project_services/assembla_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AssemblaService < Service
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated?
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index edc5c00d9c4..d502423726c 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BambooService < CiService
include ReactiveService
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
index e4e3a80976b..1a2bb6a171b 100644
--- a/app/models/project_services/bugzilla_service.rb
+++ b/app/models/project_services/bugzilla_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class BugzillaService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb
index 35884c4560c..43edfde851c 100644
--- a/app/models/project_services/buildkite_service.rb
+++ b/app/models/project_services/buildkite_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "addressable/uri"
class BuildkiteService < CiService
diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb
index 0c526b53d72..f2295a95b60 100644
--- a/app/models/project_services/builds_email_service.rb
+++ b/app/models/project_services/builds_email_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class is to be removed with 9.1
# We should also by then remove BuildsEmailService from database
class BuildsEmailService < Service
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
index cb4af73807b..1d7877a1fb5 100644
--- a/app/models/project_services/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CampfireService < Service
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated?
@@ -82,7 +84,7 @@ class CampfireService < Service
before = push[:before]
after = push[:after]
- message = ""
+ message = []
message << "[#{project.full_name}] "
message << "#{push[:user_name]} "
@@ -95,6 +97,6 @@ class CampfireService < Service
message << "#{project.web_url}/compare/#{before}...#{after}"
end
- message
+ message.join
end
end
diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb
index f710fa85b5d..8c68ddc40f2 100644
--- a/app/models/project_services/chat_message/base_message.rb
+++ b/app/models/project_services/chat_message/base_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'slack-notifier'
module ChatMessage
diff --git a/app/models/project_services/chat_message/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb
index 3273f41dbd2..0cdcfcf0237 100644
--- a/app/models/project_services/chat_message/issue_message.rb
+++ b/app/models/project_services/chat_message/issue_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class IssueMessage < BaseMessage
attr_reader :title
diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb
index f412b6833d9..58631e09538 100644
--- a/app/models/project_services/chat_message/merge_message.rb
+++ b/app/models/project_services/chat_message/merge_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class MergeMessage < BaseMessage
attr_reader :merge_request_iid
diff --git a/app/models/project_services/chat_message/note_message.rb b/app/models/project_services/chat_message/note_message.rb
index 7f9486132e6..741474fb27b 100644
--- a/app/models/project_services/chat_message/note_message.rb
+++ b/app/models/project_services/chat_message/note_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class NoteMessage < BaseMessage
attr_reader :note
diff --git a/app/models/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb
index 96fd23aede3..62aec4351db 100644
--- a/app/models/project_services/chat_message/pipeline_message.rb
+++ b/app/models/project_services/chat_message/pipeline_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class PipelineMessage < BaseMessage
attr_reader :ref_type
diff --git a/app/models/project_services/chat_message/push_message.rb b/app/models/project_services/chat_message/push_message.rb
index 8d599c5f116..82be33a12a1 100644
--- a/app/models/project_services/chat_message/push_message.rb
+++ b/app/models/project_services/chat_message/push_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class PushMessage < BaseMessage
attr_reader :after
diff --git a/app/models/project_services/chat_message/wiki_page_message.rb b/app/models/project_services/chat_message/wiki_page_message.rb
index d84b80f2de2..b605d289278 100644
--- a/app/models/project_services/chat_message/wiki_page_message.rb
+++ b/app/models/project_services/chat_message/wiki_page_message.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChatMessage
class WikiPageMessage < BaseMessage
attr_reader :title
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index a60b4c7fd0d..c10ee07ccf4 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for Chat notifications services
# This class is not meant to be used directly, but only to inherit from.
class ChatNotificationService < Service
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index 82979c8bd34..f0ef2d925ab 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for CI services
# List methods you need to implement to get your CI service
# working with GitLab Merge Requests
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index 456c7f5cee2..b8f8072869c 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CustomIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb
index 5b8320158fc..6dae4f3a4a6 100644
--- a/app/models/project_services/deployment_service.rb
+++ b/app/models/project_services/deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for deployment services
#
# These services integrate with a deployment solution like Kubernetes/OpenShift,
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index ab4e46da89f..158ae0bf255 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DroneCiService < CiService
include ReactiveService
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
index b604d860a87..fb73d430fb1 100644
--- a/app/models/project_services/emails_on_push_service.rb
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class EmailsOnPushService < Service
boolean_accessor :send_from_committer_email
boolean_accessor :disable_diffs
diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb
index a4b1ef09e93..d2835c6ac82 100644
--- a/app/models/project_services/external_wiki_service.rb
+++ b/app/models/project_services/external_wiki_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ExternalWikiService < Service
prop_accessor :external_wiki_url
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index da01ac1b7cf..2545df06f6b 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "flowdock-git-hook"
# Flow dock depends on Grit to compute the number of commits between two given
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 8a6b0ed1a5f..67a92c441b1 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "gemnasium/gitlab_service"
class GemnasiumService < Service
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 16e32a4139e..fa9abf58e62 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing
diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb
index a8512c5f57c..272cd0f4e47 100644
--- a/app/models/project_services/hangouts_chat_service.rb
+++ b/app/models/project_services/hangouts_chat_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'hangouts_chat'
class HangoutsChatService < ChatNotificationService
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index dce878e485f..66012f0da99 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class HipchatService < Service
include ActionView::Helpers::SanitizeHelper
@@ -108,7 +110,7 @@ class HipchatService < Service
before = push[:before]
after = push[:after]
- message = ""
+ message = []
message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before)
@@ -132,7 +134,7 @@ class HipchatService < Service
end
end
- message
+ message.join
end
def markdown(text, options = {})
@@ -165,11 +167,11 @@ class HipchatService < Service
description = obj_attr[:description]
issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
- message = "#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"
+ message = ["#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"]
message << "<pre>#{markdown(description)}</pre>"
- message
+ message.join
end
def create_merge_request_message(data)
@@ -184,12 +186,11 @@ class HipchatService < Service
merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
- message = "#{user_name} #{state} #{merge_request_link} in " \
- "#{project_link}: <b>#{title}</b>"
+ message = ["#{user_name} #{state} #{merge_request_link} in " \
+ "#{project_link}: <b>#{title}</b>"]
message << "<pre>#{markdown(description)}</pre>"
-
- message
+ message.join
end
def format_title(title)
@@ -235,12 +236,11 @@ class HipchatService < Service
end
subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
- message = "#{user_name} commented on #{subject_html} in #{project_link}: "
+ message = ["#{user_name} commented on #{subject_html} in #{project_link}: "]
message << title
message << "<pre>#{markdown(note, ref: commit_id)}</pre>"
-
- message
+ message.join
end
def create_pipeline_message(data)
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 27bdf708c80..a783a314071 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'uri'
class IrkerService < Service
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index df6dcd90985..c7520d766a8 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class IssueTrackerService < Service
validate :one_issue_tracker, if: :activated?, on: :manual_change
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 82d438d5378..cc98b3f5a41 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class JiraService < IssueTrackerService
include Gitlab::Routing
include ApplicationHelper
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index 722642f6da7..bda1f67b8ff 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
##
# NOTE:
# We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic.
diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb
index 0362ed172c7..b8bc83b870e 100644
--- a/app/models/project_services/mattermost_service.rb
+++ b/app/models/project_services/mattermost_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MattermostService < ChatNotificationService
def title
'Mattermost notifications'
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
index 227d430083d..ca324f68d2d 100644
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ b/app/models/project_services/mattermost_slash_commands_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MattermostSlashCommandsService < SlashCommandsService
include TriggersHelper
diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb
index 99500caec0e..5b0e5fed092 100644
--- a/app/models/project_services/microsoft_teams_service.rb
+++ b/app/models/project_services/microsoft_teams_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MicrosoftTeamsService < ChatNotificationService
def title
'Microsoft Teams Notification'
diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb
index b89dc07a73e..6883976f0c8 100644
--- a/app/models/project_services/mock_ci_service.rb
+++ b/app/models/project_services/mock_ci_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service
class MockCiService < CiService
ALLOWED_STATES = %w[failed canceled running pending success success_with_warnings skipped not_found].freeze
diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb
index 59a3811ce5d..7ab1687f8ba 100644
--- a/app/models/project_services/mock_deployment_service.rb
+++ b/app/models/project_services/mock_deployment_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MockDeploymentService < DeploymentService
def title
'Mock deployment'
diff --git a/app/models/project_services/mock_monitoring_service.rb b/app/models/project_services/mock_monitoring_service.rb
index ed0318c6b27..bcf8f1df5da 100644
--- a/app/models/project_services/mock_monitoring_service.rb
+++ b/app/models/project_services/mock_monitoring_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MockMonitoringService < MonitoringService
def title
'Mock monitoring'
diff --git a/app/models/project_services/monitoring_service.rb b/app/models/project_services/monitoring_service.rb
index 9af68b4e821..1b530a8247b 100644
--- a/app/models/project_services/monitoring_service.rb
+++ b/app/models/project_services/monitoring_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for monitoring services
#
# These services integrate with a deployment solution like Prometheus
diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb
index ba62a5b7ac0..003884bb7ac 100644
--- a/app/models/project_services/packagist_service.rb
+++ b/app/models/project_services/packagist_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PackagistService < Service
prop_accessor :username, :token, :server
diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb
index 4cf149ac044..6f39a5e6e83 100644
--- a/app/models/project_services/pipelines_email_service.rb
+++ b/app/models/project_services/pipelines_email_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PipelinesEmailService < Service
prop_accessor :recipients
boolean_accessor :notify_only_broken_pipelines
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index 3476e7d2283..617e502b639 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PivotaltrackerService < Service
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index df4254e0523..509e5b6089b 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PrometheusService < MonitoringService
include PrometheusAdapter
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 8777a44b72f..4e48c348b45 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PushoverService < Service
BASE_URI = 'https://api.pushover.net/1'.freeze
@@ -79,7 +81,7 @@ class PushoverService < Service
end
if data[:total_commits_count] > 0
- message << "\nTotal commits count: #{data[:total_commits_count]}"
+ message = [message, "Total commits count: #{data[:total_commits_count]}"].join("\n")
end
pushover_data = {
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index 3721093a6d1..a80be4b06da 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RedmineService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 71da0af75f6..482808255f9 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SlackService < ChatNotificationService
def title
'Slack notifications'
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 1c3892a3f75..6c82e088231 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SlackSlashCommandsService < SlashCommandsService
include TriggersHelper
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index 37ea45109ae..e3ab60adefd 100644
--- a/app/models/project_services/slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Base class for Chat services
# This class is not meant to be used directly, but only to inherrit from.
class SlashCommandsService < Service
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 802678147cf..eeeff5e802a 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TeamcityService < CiService
include ReactiveService
diff --git a/app/models/protected_branch/merge_access_level.rb b/app/models/protected_branch/merge_access_level.rb
index e8d35ac326f..b0d5c64e931 100644
--- a/app/models/protected_branch/merge_access_level.rb
+++ b/app/models/protected_branch/merge_access_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
end
diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb
index 7a2e9e5ec5d..b2a88229853 100644
--- a/app/models/protected_branch/push_access_level.rb
+++ b/app/models/protected_branch/push_access_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
end
diff --git a/app/models/protected_tag/create_access_level.rb b/app/models/protected_tag/create_access_level.rb
index 6b6ab3d8279..b06e55fb5dd 100644
--- a/app/models/protected_tag/create_access_level.rb
+++ b/app/models/protected_tag/create_access_level.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ProtectedTag::CreateAccessLevel < ActiveRecord::Base
include ProtectedTagAccess
diff --git a/app/models/repository_language.rb b/app/models/repository_language.rb
index f467d4eafa3..b18142a2ac4 100644
--- a/app/models/repository_language.rb
+++ b/app/models/repository_language.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RepositoryLanguage < ActiveRecord::Base
belongs_to :project
belongs_to :programming_language
diff --git a/app/models/site_statistic.rb b/app/models/site_statistic.rb
index 9c9c3172fe6..daac1c57db9 100644
--- a/app/models/site_statistic.rb
+++ b/app/models/site_statistic.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class SiteStatistic < ActiveRecord::Base
# prevents the creation of multiple rows
default_value_for :id, 1
diff --git a/app/models/storage/hashed_project.rb b/app/models/storage/hashed_project.rb
index 26b4b78ac64..90710f73fd3 100644
--- a/app/models/storage/hashed_project.rb
+++ b/app/models/storage/hashed_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
class HashedProject
attr_accessor :project
diff --git a/app/models/storage/legacy_project.rb b/app/models/storage/legacy_project.rb
index 27cb388c702..9f6f19acb41 100644
--- a/app/models/storage/legacy_project.rb
+++ b/app/models/storage/legacy_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Storage
class LegacyProject
attr_accessor :project
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index c5c77bc8333..376ef673ca8 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -15,7 +15,7 @@ class SystemNoteMetadata < ActiveRecord::Base
commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved
opened closed merged duplicate locked unlocked
- outdated
+ outdated tag
].freeze
validates :note, presence: true
diff --git a/app/models/user.rb b/app/models/user.rb
index 37f2e8b680e..fb19de4b980 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -101,6 +101,10 @@ class User < ActiveRecord::Base
has_many :groups, through: :group_members
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
+ has_many :owned_or_maintainers_groups,
+ -> { where(members: { access_level: [Gitlab::Access::MAINTAINER, Gitlab::Access::OWNER] }) },
+ through: :group_members,
+ source: :group
alias_attribute :masters_groups, :maintainers_groups
# Projects
@@ -982,15 +986,7 @@ class User < ActiveRecord::Base
end
def manageable_groups
- union_sql = Gitlab::SQL::Union.new([owned_groups.select(:id), maintainers_groups.select(:id)]).to_sql
-
- # Update this line to not use raw SQL when migrated to Rails 5.2.
- # Either ActiveRecord or Arel constructions are fine.
- # This was replaced with the raw SQL construction because of bugs in the arel gem.
- # Bugs were fixed in arel 9.0.0 (Rails 5.2).
- owned_and_maintainer_groups = Group.where("namespaces.id IN (#{union_sql})") # rubocop:disable GitlabSecurity/SqlInjection
-
- Gitlab::GroupHierarchy.new(owned_and_maintainer_groups).base_and_descendants
+ Gitlab::GroupHierarchy.new(owned_or_maintainers_groups).base_and_descendants
end
def namespaces
@@ -1244,11 +1240,6 @@ class User < ActiveRecord::Base
!terms_accepted?
end
- def owned_or_maintainers_groups
- union = Gitlab::SQL::Union.new([owned_groups, maintainers_groups])
- Group.from("(#{union.to_sql}) namespaces")
- end
-
# @deprecated
alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
diff --git a/app/policies/commit_policy.rb b/app/policies/commit_policy.rb
new file mode 100644
index 00000000000..67e9bc12804
--- /dev/null
+++ b/app/policies/commit_policy.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class CommitPolicy < BasePolicy
+ delegate { @subject.project }
+end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index f52a3bad77d..00c58f15013 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -251,6 +251,7 @@ class ProjectPolicy < BasePolicy
enable :update_pages
enable :read_cluster
enable :create_cluster
+ enable :create_environment_terminal
end
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 02f6c5bdf81..880218e2727 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Ci
class BuildRunnerPresenter < SimpleDelegator
def artifacts
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index b18e9706db6..07a13c33b89 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -23,9 +23,8 @@ class EnvironmentEntity < Grape::Entity
stop_project_environment_path(environment.project, environment)
end
- expose :terminal_path, if: ->(environment, _) { environment.has_terminals? } do |environment|
- can?(request.current_user, :admin_environment, environment.project) &&
- terminal_project_environment_path(environment.project, environment)
+ expose :terminal_path, if: ->(*) { environment.has_terminals? && can_access_terminal? } do |environment|
+ terminal_project_environment_path(environment.project, environment)
end
expose :folder_path do |environment|
@@ -40,7 +39,13 @@ class EnvironmentEntity < Grape::Entity
private
+ alias_method :environment, :object
+
def current_user
request.current_user
end
+
+ def can_access_terminal?
+ can?(request.current_user, :create_environment_terminal, environment)
+ end
end
diff --git a/app/serializers/project_mirror_serializer.rb b/app/serializers/project_mirror_serializer.rb
new file mode 100644
index 00000000000..6a9462aa7cb
--- /dev/null
+++ b/app/serializers/project_mirror_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ProjectMirrorSerializer < BaseSerializer
+ entity ProjectMirrorEntity
+end
diff --git a/app/serializers/test_case_entity.rb b/app/serializers/test_case_entity.rb
index 5c1cbf37182..ec60055ba5b 100644
--- a/app/serializers/test_case_entity.rb
+++ b/app/serializers/test_case_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TestCaseEntity < Grape::Entity
expose :status
expose :name
diff --git a/app/serializers/test_reports_comparer_entity.rb b/app/serializers/test_reports_comparer_entity.rb
index b95d820d093..d7a3dd34fdc 100644
--- a/app/serializers/test_reports_comparer_entity.rb
+++ b/app/serializers/test_reports_comparer_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TestReportsComparerEntity < Grape::Entity
expose :total_status, as: :status
diff --git a/app/serializers/test_reports_comparer_serializer.rb b/app/serializers/test_reports_comparer_serializer.rb
index a739858efb2..7fb8d28b09a 100644
--- a/app/serializers/test_reports_comparer_serializer.rb
+++ b/app/serializers/test_reports_comparer_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TestReportsComparerSerializer < BaseSerializer
entity TestReportsComparerEntity
end
diff --git a/app/serializers/test_suite_comparer_entity.rb b/app/serializers/test_suite_comparer_entity.rb
index a3965ba3930..9fa3a897ebe 100644
--- a/app/serializers/test_suite_comparer_entity.rb
+++ b/app/serializers/test_suite_comparer_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TestSuiteComparerEntity < Grape::Entity
expose :name
expose :total_status, as: :status
diff --git a/app/services/ci/enqueue_build_service.rb b/app/services/ci/enqueue_build_service.rb
new file mode 100644
index 00000000000..8140651d980
--- /dev/null
+++ b/app/services/ci/enqueue_build_service.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+module Ci
+ class EnqueueBuildService < BaseService
+ def execute(build)
+ build.enqueue
+ end
+ end
+end
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index cda9bbff3b4..cafee76a33c 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -37,7 +37,7 @@ module Ci
def process_build(build, current_status)
if valid_statuses_for_when(build.when).include?(current_status)
- build.action? ? build.actionize : build.enqueue
+ build.action? ? build.actionize : enqueue_build(build)
true
else
build.skip
@@ -93,5 +93,9 @@ module Ci
.where.not(id: latest_statuses.map(&:first))
.update_all(retried: true) if latest_statuses.any?
end
+
+ def enqueue_build(build)
+ Ci::EnqueueBuildService.new(project, @user).execute(build)
+ end
end
end
diff --git a/app/services/commits/tag_service.rb b/app/services/commits/tag_service.rb
new file mode 100644
index 00000000000..7961ba4d3c4
--- /dev/null
+++ b/app/services/commits/tag_service.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Commits
+ class TagService < BaseService
+ def execute(commit)
+ unless params[:tag_name]
+ return error('Missing parameter tag_name')
+ end
+
+ tag_name = params[:tag_name]
+ message = params[:tag_message]
+ release_description = nil
+
+ result = Tags::CreateService
+ .new(commit.project, current_user)
+ .execute(tag_name, commit.sha, message, release_description)
+
+ if result[:status] == :success
+ tag = result[:tag]
+ SystemNoteService.tag_commit(commit, commit.project, current_user, tag.name)
+ end
+
+ result
+ end
+ end
+end
diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb
index 7280449bb1c..4c14d834949 100644
--- a/app/services/notes/quick_actions_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -4,7 +4,8 @@ module Notes
class QuickActionsService < BaseService
UPDATE_SERVICES = {
'Issue' => Issues::UpdateService,
- 'MergeRequest' => MergeRequests::UpdateService
+ 'MergeRequest' => MergeRequests::UpdateService,
+ 'Commit' => Commits::TagService
}.freeze
def self.noteable_update_service(note)
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 4389fd89538..5c0e8a35cb0 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -130,7 +130,7 @@ module NotificationRecipientService
end
def add_project_watchers
- add_recipients(project_watchers, :watch, nil)
+ add_recipients(project_watchers, :watch, nil) if project
end
def add_group_watchers
@@ -220,6 +220,8 @@ module NotificationRecipientService
end
class Default < Base
+ MENTION_TYPE_ACTIONS = [:new_issue, :new_merge_request].freeze
+
attr_reader :target
attr_reader :current_user
attr_reader :action
@@ -252,7 +254,7 @@ module NotificationRecipientService
add_subscribed_users
- if [:new_issue, :new_merge_request].include?(custom_action)
+ if self.class.mention_type_actions.include?(custom_action)
# These will all be participants as well, but adding with the :mention
# type ensures that users with the mention notification level will
# receive them, too.
@@ -279,10 +281,14 @@ module NotificationRecipientService
end
# Build event key to search on custom notification level
- # Check NotificationSetting::EMAIL_EVENTS
+ # Check NotificationSetting.email_events
def custom_action
@custom_action ||= "#{action}_#{target.class.model_name.name.underscore}".to_sym
end
+
+ def self.mention_type_actions
+ MENTION_TYPE_ACTIONS.dup
+ end
end
class NewNote < Base
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index a15ee4911ef..11b996ed4b6 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -16,7 +16,7 @@ class PreviewMarkdownService < BaseService
private
def explain_quick_actions(text)
- return text, [] unless %w(Issue MergeRequest).include?(commands_target_type)
+ return text, [] unless %w(Issue MergeRequest Commit).include?(commands_target_type)
quick_actions_service = QuickActions::InterpretService.new(project, current_user)
quick_actions_service.explain(text, find_commands_target)
@@ -29,13 +29,9 @@ class PreviewMarkdownService < BaseService
end
def find_commands_target
- if commands_target_id.present?
- finder = commands_target_type == 'Issue' ? IssuesFinder : MergeRequestsFinder
- finder.new(current_user, project_id: project.id).find(commands_target_id)
- else
- collection = commands_target_type == 'Issue' ? project.issues : project.merge_requests
- collection.build
- end
+ QuickActions::TargetService
+ .new(project, current_user)
+ .execute(commands_target_type, commands_target_id)
end
def commands_target_type
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 10eb2cea4a2..5286b92ab6b 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -47,15 +47,7 @@ module Projects
end
def commands(noteable, type)
- noteable ||=
- case type
- when 'Issue'
- @project.issues.build
- when 'MergeRequest'
- @project.merge_requests.build
- end
-
- return [] unless noteable&.is_a?(Issuable)
+ return [] unless noteable
QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
end
diff --git a/app/services/projects/detect_repository_languages_service.rb b/app/services/projects/detect_repository_languages_service.rb
index 4b4108de231..3488b9ce47e 100644
--- a/app/services/projects/detect_repository_languages_service.rb
+++ b/app/services/projects/detect_repository_languages_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Projects
class DetectRepositoryLanguagesService < BaseService
attr_reader :detected_repository_languages, :programming_languages
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index cdc8514c47c..8838ed06324 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -60,7 +60,8 @@ module QuickActions
"Closes this #{issuable.to_ability_name.humanize(capitalize: false)}."
end
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.open? &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
end
@@ -75,7 +76,8 @@ module QuickActions
"Reopens this #{issuable.to_ability_name.humanize(capitalize: false)}."
end
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.closed? &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
end
@@ -149,7 +151,8 @@ module QuickActions
issuable.allows_multiple_assignees? ? '@user1 @user2' : ''
end
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.assignees.any? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
@@ -188,7 +191,8 @@ module QuickActions
"Removes #{issuable.milestone.to_reference(format: :name)} milestone."
end
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.milestone_id? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
@@ -231,7 +235,8 @@ module QuickActions
end
params '~label1 ~"label 2"'
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.labels.any? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
@@ -257,7 +262,8 @@ module QuickActions
end
params '~label1 ~"label 2"'
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.labels.any? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
@@ -295,7 +301,8 @@ module QuickActions
desc 'Add a todo'
explanation 'Adds a todo.'
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
!TodoService.new.todo_exist?(issuable, current_user)
end
command :todo do
@@ -317,7 +324,8 @@ module QuickActions
"Subscribes to this #{issuable.to_ability_name.humanize(capitalize: false)}."
end
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
!issuable.subscribed?(current_user, project)
end
command :subscribe do
@@ -329,7 +337,8 @@ module QuickActions
"Unsubscribes from this #{issuable.to_ability_name.humanize(capitalize: false)}."
end
condition do
- issuable.persisted? &&
+ issuable.is_a?(Issuable) &&
+ issuable.persisted? &&
issuable.subscribed?(current_user, project)
end
command :unsubscribe do
@@ -385,7 +394,8 @@ module QuickActions
end
params ':emoji:'
condition do
- issuable.persisted?
+ issuable.is_a?(Issuable) &&
+ issuable.persisted?
end
parse_params do |emoji_param|
match = emoji_param.match(Banzai::Filter::EmojiFilter.emoji_pattern)
@@ -574,6 +584,23 @@ module QuickActions
@updates[:confidential] = true
end
+ desc 'Tag this commit.'
+ explanation do |tag_name, message|
+ with_message = %{ with "#{message}"} if message.present?
+ "Tags this commit to #{tag_name}#{with_message}."
+ end
+ params 'v1.2.3 <message>'
+ parse_params do |tag_name_and_message|
+ tag_name_and_message.split(' ', 2)
+ end
+ condition do
+ issuable.is_a?(Commit) && current_user.can?(:push_code, project)
+ end
+ command :tag do |tag_name, message|
+ @updates[:tag_name] = tag_name
+ @updates[:tag_message] = message
+ end
+
def extract_users(params)
return [] if params.nil?
diff --git a/app/services/quick_actions/target_service.rb b/app/services/quick_actions/target_service.rb
new file mode 100644
index 00000000000..d8ba52c6e50
--- /dev/null
+++ b/app/services/quick_actions/target_service.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QuickActions
+ class TargetService < BaseService
+ def execute(type, type_id)
+ case type&.downcase
+ when 'issue'
+ issue(type_id)
+ when 'mergerequest'
+ merge_request(type_id)
+ when 'commit'
+ commit(type_id)
+ end
+ end
+
+ private
+
+ def issue(type_id)
+ IssuesFinder.new(current_user, project_id: project.id).find_by(iid: type_id) || project.issues.build
+ end
+
+ def merge_request(type_id)
+ MergeRequestsFinder.new(current_user, project_id: project.id).find_by(iid: type_id) || project.merge_requests.build
+ end
+
+ def commit(type_id)
+ project.commit(type_id)
+ end
+ end
+end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 77494295f14..dda89830179 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -32,6 +32,21 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'commit', commit_count: total_count))
end
+ # Called when a commit was tagged
+ #
+ # noteable - Noteable object
+ # project - Project owning noteable
+ # author - User performing the tag
+ # tag_name - The created tag name
+ #
+ # Returns the created Note object
+ def tag_commit(noteable, project, author, tag_name)
+ link = url_helpers.project_tag_url(project, id: tag_name)
+ body = "tagged commit #{noteable.sha} to [`#{tag_name}`](#{link})"
+
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'tag'))
+ end
+
# Called when the assignee of a Noteable is changed or removed
#
# noteable - Noteable object
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index 329722df747..35390f5082c 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -7,7 +7,7 @@ module Tags
return error('Tag name invalid') unless valid_tag
repository = project.repository
- message&.strip!
+ message = message&.strip
new_tag = nil
diff --git a/app/services/todos/destroy/base_service.rb b/app/services/todos/destroy/base_service.rb
index dff5e1f30e5..aeb60e50c64 100644
--- a/app/services/todos/destroy/base_service.rb
+++ b/app/services/todos/destroy/base_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Todos
module Destroy
class BaseService
diff --git a/app/services/todos/destroy/confidential_issue_service.rb b/app/services/todos/destroy/confidential_issue_service.rb
index c5b66df057a..efec0f22da5 100644
--- a/app/services/todos/destroy/confidential_issue_service.rb
+++ b/app/services/todos/destroy/confidential_issue_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Todos
module Destroy
class ConfidentialIssueService < ::Todos::Destroy::BaseService
diff --git a/app/services/todos/destroy/entity_leave_service.rb b/app/services/todos/destroy/entity_leave_service.rb
index 045f5ecaae7..4cb9d08713d 100644
--- a/app/services/todos/destroy/entity_leave_service.rb
+++ b/app/services/todos/destroy/entity_leave_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Todos
module Destroy
class EntityLeaveService < ::Todos::Destroy::BaseService
diff --git a/app/services/todos/destroy/group_private_service.rb b/app/services/todos/destroy/group_private_service.rb
index d13fa7a6516..f67f1d40597 100644
--- a/app/services/todos/destroy/group_private_service.rb
+++ b/app/services/todos/destroy/group_private_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Todos
module Destroy
class GroupPrivateService < ::Todos::Destroy::BaseService
diff --git a/app/services/todos/destroy/private_features_service.rb b/app/services/todos/destroy/private_features_service.rb
index 4d8e2877bfb..7e204885b31 100644
--- a/app/services/todos/destroy/private_features_service.rb
+++ b/app/services/todos/destroy/private_features_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Todos
module Destroy
class PrivateFeaturesService < ::Todos::Destroy::BaseService
diff --git a/app/services/todos/destroy/project_private_service.rb b/app/services/todos/destroy/project_private_service.rb
index 315a0c33398..ae8fab3ffca 100644
--- a/app/services/todos/destroy/project_private_service.rb
+++ b/app/services/todos/destroy/project_private_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Todos
module Destroy
class ProjectPrivateService < ::Todos::Destroy::BaseService
diff --git a/app/views/admin/spam_logs/index.html.haml b/app/views/admin/spam_logs/index.html.haml
index 8aaa6379730..b45d3e4823b 100644
--- a/app/views/admin/spam_logs/index.html.haml
+++ b/app/views/admin/spam_logs/index.html.haml
@@ -17,6 +17,6 @@
%th Primary Action
%th
= render @spam_logs
- = paginate @spam_logs
+ = paginate @spam_logs, theme: 'gitlab'
- else
%h4 There are no Spam Logs
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index f730fd05176..029efadd75d 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -128,8 +128,8 @@
.col-md-6
- unless @user == current_user
- unless @user.confirmed?
- .card.bg-info
- .card-header
+ .card.border-info
+ .card-header.bg-info.text-white
Confirm user
.card-body
- if @user.unconfirmed_email.present?
@@ -138,8 +138,8 @@
%br
= link_to 'Confirm user', confirm_admin_user_path(@user), method: :put, class: "btn btn-info", data: { confirm: 'Are you sure?' }
- if @user.blocked?
- .card.bg-info
- .card-header
+ .card.border-info
+ .card-header.bg-info.text-white
This user is blocked
.card-body
%p A blocked user cannot:
@@ -162,8 +162,8 @@
%br
= link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-warning"
- if @user.access_locked?
- .card.bg-info
- .card-header
+ .card.border-info
+ .card-header.bg-info.text-white
This account has been locked
.card-body
%p This user has been temporarily locked due to excessive number of failed logins. You may manually unlock the account.
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 8560b72fe85..24f256d083b 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -2,11 +2,11 @@
.file-holder-bottom-radius.file-holder.file.append-bottom-default
.js-file-title.file-title.clearfix{ data: { current_action: action } }
- .editor-ref
+ .editor-ref.block-truncated
= sprite_icon('fork', size: 12)
= ref
- %span.editor-file-name
- - if current_action?(:edit) || current_action?(:update)
+ - if current_action?(:edit) || current_action?(:update)
+ %span.editor-file-name
= text_field_tag 'file_path', (params[:file_path] || @path),
class: 'form-control new-file-path js-file-path-name-input'
@@ -16,7 +16,7 @@
= text_field_tag 'file_name', params[:file_name], placeholder: "File name",
required: true, class: 'form-control new-file-name js-file-path-name-input'
- .float-right.file-buttons
+ .file-buttons
= button_tag class: 'soft-wrap-toggle btn', type: 'button', tabindex: '-1' do
%span.no-wrap
= custom_icon('icon_no_wrap')
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
index 8a549d431ee..12cf40bb65f 100644
--- a/app/views/projects/forks/_fork_button.html.haml
+++ b/app/views/projects/forks/_fork_button.html.haml
@@ -5,7 +5,7 @@
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked
= link_to project_path(forked_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = project_identicon(namespace, class: "avatar s100 identicon")
+ = project_icon(namespace, class: "avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
@@ -18,7 +18,7 @@
class: ("disabled has-tooltip" unless can_create_project),
title: (_('You have reached your project limit') unless can_create_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = project_identicon(namespace, class: "avatar s100 identicon")
+ = project_icon(namespace, class: "avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml
index 759efd4e9d4..86b2b8bf2f7 100644
--- a/app/views/projects/jobs/_sidebar.html.haml
+++ b/app/views/projects/jobs/_sidebar.html.haml
@@ -1,12 +1,7 @@
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
.sidebar-container
.blocks-container
- - if can?(current_user, :create_build_terminal, @build)
- .block
- = link_to terminal_project_job_path(@project, @build), class: 'terminal-button pull-right btn visible-md-block visible-lg-block', title: 'Terminal' do
- Terminal
-
- #js-details-block-vue{ data: { can_user_retry: can?(current_user, :update_build, @build) && @build.retryable? } }
+ #js-details-block-vue{ data: { terminal_path: can?(current_user, :create_build_terminal, @build) && @build.has_terminal? ? terminal_project_job_path(@project, @build) : nil } }
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
.block
diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml
index 64f0fde30cf..3d811be3fe3 100644
--- a/app/views/projects/mirrors/_instructions.html.haml
+++ b/app/views/projects/mirrors/_instructions.html.haml
@@ -1,10 +1,11 @@
.account-well.prepend-top-default.append-bottom-default
%ul
%li
- The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> or <code>git://</code>.
- %li
- Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.
- %li
- The update action will time out after 10 minutes. For big repositories, use a clone/push combination.
- %li
- The Git LFS objects will <strong>not</strong> be synced.
+ = _('The repository must be accessible over <code>http://</code>,
+ <code>https://</code>, <code>ssh://</code> and <code>git://</code>.').html_safe
+ %li= _('Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>.').html_safe
+ %li= _('The update action will time out after 15 minutes. For big repositories, use a clone/push combination.')
+ %li= _('The Git LFS objects will <strong>not</strong> be synced.').html_safe
+ %li
+ = _('This user will be the author of all events in the activity feed that are the result of an update,
+ like new branches being created or new commits being pushed to existing branches.')
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
new file mode 100644
index 00000000000..53387b3a50c
--- /dev/null
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -0,0 +1,63 @@
+- expanded = Rails.env.test?
+- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
+
+%section.settings.project-mirror-settings.js-mirror-settings.no-animate{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4= _('Mirroring repositories')
+ %button.btn.js-settings-toggle
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _('Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically.')
+ = link_to _('Read more'), help_page_path('workflow/repository_mirroring'), target: '_blank'
+
+ .settings-content
+ = form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'false', data: mirrors_form_data_attributes } do |f|
+ .panel.panel-default
+ .panel-heading
+ %h3.panel-title= _('Mirror a repository')
+ .panel-body
+ %div= form_errors(@project)
+
+ .form-group.has-feedback
+ = label_tag :url, _('Git repository URL'), class: 'label-light'
+ = text_field_tag :url, nil, class: 'form-control js-mirror-url js-repo-url', placeholder: _('Input your repository URL'), required: true, pattern: "(#{protocols}):\/\/.+"
+
+ = render 'projects/mirrors/instructions'
+
+ = render 'projects/mirrors/mirror_repos_form', f: f
+
+ .form-check.append-bottom-10
+ = check_box_tag :only_protected_branches, '1', false, class: 'js-mirror-protected form-check-input'
+ = label_tag :only_protected_branches, _('Only mirror protected branches'), class: 'form-check-label'
+ = link_to icon('question-circle'), help_page_path('user/project/protected_branches')
+
+ .panel-footer
+ = f.submit _('Mirror repository'), class: 'btn btn-create', name: :update_remote_mirror
+
+ .panel.panel-default
+ .table-responsive
+ %table.table.push-pull-table
+ %thead
+ %tr
+ %th
+ = _('Mirrored repositories')
+ = render_if_exists 'projects/mirrors/mirrored_repositories_count'
+ %th= _('Direction')
+ %th= _('Last update')
+ %th
+ %th
+ %tbody.js-mirrors-table-body
+ = render_if_exists 'projects/mirrors/table_pull_row'
+ - @project.remote_mirrors.each_with_index do |mirror, index|
+ - if mirror.enabled
+ %tr
+ %td= mirror.safe_url
+ %td= _('Push')
+ %td= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
+ %td
+ - if mirror.last_error.present?
+ .badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
+ %td.mirror-action-buttons
+ .btn-group.mirror-actions-group.pull-right{ role: 'group' }
+ = render 'shared/remote_mirror_update_button', remote_mirror: mirror
+ %button.js-delete-mirror.btn.btn-danger{ type: 'button', data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' }, title: _('Remove') }= icon('trash-o')
diff --git a/app/views/projects/mirrors/_mirror_repos_form.html.haml b/app/views/projects/mirrors/_mirror_repos_form.html.haml
new file mode 100644
index 00000000000..93994cb30ac
--- /dev/null
+++ b/app/views/projects/mirrors/_mirror_repos_form.html.haml
@@ -0,0 +1,18 @@
+- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
+
+.form-group
+ = label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
+ = select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control js-mirror-direction', disabled: true
+
+= f.fields_for :remote_mirrors, @project.remote_mirrors.build do |rm_f|
+ = rm_f.hidden_field :enabled, value: '1'
+ = rm_f.hidden_field :url, class: 'js-mirror-url-hidden', required: true, pattern: "(#{protocols}):\/\/.+"
+ = rm_f.hidden_field :only_protected_branches, class: 'js-mirror-protected-hidden'
+
+.form-group
+ = label_tag :auth_method, _('Authentication method'), class: 'label-bold'
+ = select_tag :auth_method, options_for_select([[_('None'), 'none'], [_('Password'), 'password']], 'none'), { class: "form-control js-auth-method" }
+
+.form-group.js-password-group.collapse
+ = label_tag :password, _('Password'), class: 'label-bold'
+ = text_field_tag :password, '', class: 'form-control js-password'
diff --git a/app/views/projects/mirrors/_push.html.haml b/app/views/projects/mirrors/_push.html.haml
deleted file mode 100644
index 08375e09816..00000000000
--- a/app/views/projects/mirrors/_push.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-- expanded = Rails.env.test?
-%section.settings.no-animate#js-push-remote-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- Push to a remote repository
- %button.btn.js-settings-toggle
- = expanded ? 'Collapse' : 'Expand'
- %p
- Set up the remote repository that you want to update with the content of the current repository
- every time someone pushes to it.
- = link_to 'Read more', help_page_path('workflow/repository_mirroring', anchor: 'pushing-to-a-remote-repository'), target: '_blank'
- .settings-content
- = form_for @project, url: project_mirror_path(@project) do |f|
- %div
- = form_errors(@project)
- = render "shared/remote_mirror_update_button", remote_mirror: @remote_mirror
- - if @remote_mirror.last_error.present?
- .panel.panel-danger
- .panel-heading
- - if @remote_mirror.last_update_at
- The remote repository failed to update #{time_ago_with_tooltip(@remote_mirror.last_update_at)}.
- - else
- The remote repository failed to update.
-
- - if @remote_mirror.last_successful_update_at
- Last successful update #{time_ago_with_tooltip(@remote_mirror.last_successful_update_at)}.
- .panel-body
- %pre
- :preserve
- #{h(@remote_mirror.last_error.strip)}
- = f.fields_for :remote_mirrors, @remote_mirror do |rm_form|
- .form-group
- = rm_form.check_box :enabled, class: "float-left"
- .prepend-left-20
- = rm_form.label :enabled, "Remote mirror repository", class: "label-bold append-bottom-0"
- %p.light.append-bottom-0
- Automatically update the remote mirror's branches, tags, and commits from this repository every time someone pushes to it.
- .form-group.has-feedback
- = rm_form.label :url, "Git repository URL", class: "label-bold"
- = rm_form.text_field :url, class: "form-control", placeholder: 'https://username:password@gitlab.company.com/group/project.git'
-
- = render "projects/mirrors/instructions"
-
- .form-group
- = rm_form.check_box :only_protected_branches, class: 'float-left'
- .prepend-left-20
- = rm_form.label :only_protected_branches, class: 'label-bold'
- = link_to icon('question-circle'), help_page_path('user/project/protected_branches')
-
- = f.submit 'Save changes', class: 'btn btn-create', name: 'update_remote_mirror'
diff --git a/app/views/projects/mirrors/_show.html.haml b/app/views/projects/mirrors/_show.html.haml
index de77701a373..8318d5898a1 100644
--- a/app/views/projects/mirrors/_show.html.haml
+++ b/app/views/projects/mirrors/_show.html.haml
@@ -1,3 +1 @@
-- if can?(current_user, :admin_remote_mirror, @project)
- = render 'projects/mirrors/push'
-
+= render 'projects/mirrors/mirror_repos'
diff --git a/app/views/projects/pages/_use.html.haml b/app/views/projects/pages/_use.html.haml
index cd9177c0f9e..988dabef3a0 100644
--- a/app/views/projects/pages/_use.html.haml
+++ b/app/views/projects/pages/_use.html.haml
@@ -1,6 +1,6 @@
- unless @project.pages_deployed?
- .card.bg-info
- .card-header
+ .card.border-info
+ .card-header.bg-info.text-white
Configure pages
.card-body
%p
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index fdcd126e7a3..a8d4d4af93a 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,4 +1,6 @@
- project = find_project_for_result_blob(blob)
+- return unless project
+
- file_name, blob = parse_search_result(blob)
- blob_link = project_blob_path(project, tree_join(blob.ref, file_name))
diff --git a/app/views/shared/_remote_mirror_update_button.html.haml b/app/views/shared/_remote_mirror_update_button.html.haml
index 34de1c0695f..f32cff18fa8 100644
--- a/app/views/shared/_remote_mirror_update_button.html.haml
+++ b/app/views/shared/_remote_mirror_update_button.html.haml
@@ -1,13 +1,6 @@
-- if @project.has_remote_mirror?
- .append-bottom-default
- - if remote_mirror.update_in_progress?
- %span.btn.disabled
- = icon("refresh spin")
- Updating&hellip;
- - else
- = link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn" do
- = icon("refresh")
- Update Now
- - if @remote_mirror.last_successful_update_at
- %p.inline.prepend-left-10
- Successfully updated #{time_ago_with_tooltip(@remote_mirror.last_successful_update_at)}.
+- if remote_mirror.update_in_progress?
+ %button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body' }, title: _('Updating') }
+ = icon("refresh spin")
+- else
+ = link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn", data: { toggle: 'tooltip', container: 'body' }, title: _('Update now') do
+ = icon("refresh")
diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml
index ee6354b1c28..ee97f0172da 100644
--- a/app/views/shared/milestones/_issuables.html.haml
+++ b/app/views/shared/milestones/_issuables.html.haml
@@ -2,8 +2,8 @@
- primary = local_assigns.fetch(:primary, false)
- panel_class = primary ? 'bg-primary text-white' : ''
-.card{ class: panel_class }
- .card-header
+.card
+ .card-header{ class: panel_class }
.title
= title
- if show_counter
diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml
index 1f6e8f98bbb..43a87fd8397 100644
--- a/app/views/shared/notifications/_custom_notifications.html.haml
+++ b/app/views/shared/notifications/_custom_notifications.html.haml
@@ -19,7 +19,7 @@
- paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe }
#{ paragraph.html_safe }
.col-lg-8
- - NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index|
+ - notification_setting.email_events.each_with_index do |event, index|
- field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
.form-group
.form-check{ class: ("prepend-top-0" if index == 0) }
diff --git a/app/views/shared/plugins/_index.html.haml b/app/views/shared/plugins/_index.html.haml
index 7bcc54e7459..9d230d12be2 100644
--- a/app/views/shared/plugins/_index.html.haml
+++ b/app/views/shared/plugins/_index.html.haml
@@ -19,5 +19,5 @@
.monospace
= File.basename(file)
- else
- %p.card.bg-light.text-center
- No plugins found.
+ .card.bg-light.text-center
+ .nothing-here-block No plugins found.
diff --git a/app/views/shared/runners/_form.html.haml b/app/views/shared/runners/_form.html.haml
index 0337680d79b..fa93307be31 100644
--- a/app/views/shared/runners/_form.html.haml
+++ b/app/views/shared/runners/_form.html.haml
@@ -1,56 +1,56 @@
= form_for runner, url: runner_form_url do |f|
= form_errors(runner)
.form-group.row
- = label :active, "Active", class: 'col-form-label col-sm-2'
+ = label :active, _("Active"), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :active, { class: 'form-check-input' }
- %span.light Paused Runners don't accept new jobs
+ %label.light{ for: :runner_active }= _("Paused Runners don't accept new jobs")
.form-group.row
- = label :protected, "Protected", class: 'col-form-label col-sm-2'
+ = label :protected, _("Protected"), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :access_level, { class: 'form-check-input' }, 'ref_protected', 'not_protected'
- %span.light This runner will only run on pipelines triggered on protected branches
+ %label.light{ for: :runner_access_level }= _('This runner will only run on pipelines triggered on protected branches')
.form-group.row
- = label :run_untagged, 'Run untagged jobs', class: 'col-form-label col-sm-2'
+ = label :run_untagged, _('Run untagged jobs'), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :run_untagged, { class: 'form-check-input' }
- %span.light Indicates whether this runner can pick jobs without tags
+ %label.light{ for: :runner_run_untagged }= _('Indicates whether this runner can pick jobs without tags')
- unless runner.group_type?
.form-group.row
= label :locked, _('Lock to current projects'), class: 'col-form-label col-sm-2'
.col-sm-10
.form-check
= f.check_box :locked, { class: 'form-check-input' }
- %span.light= _('When a runner is locked, it cannot be assigned to other projects')
+ %label.light{ for: :runner_locked }= _('When a runner is locked, it cannot be assigned to other projects')
.form-group.row
= label_tag :token, class: 'col-form-label col-sm-2' do
- Token
+ = _('Token')
.col-sm-10
= f.text_field :token, class: 'form-control', readonly: true
.form-group.row
= label_tag :ip_address, class: 'col-form-label col-sm-2' do
- IP Address
+ = _('IP Address')
.col-sm-10
= f.text_field :ip_address, class: 'form-control', readonly: true
.form-group.row
= label_tag :description, class: 'col-form-label col-sm-2' do
- Description
+ = _('Description')
.col-sm-10
= f.text_field :description, class: 'form-control'
.form-group.row
= label_tag :maximum_timeout_human_readable, class: 'col-form-label col-sm-2' do
- Maximum job timeout
+ = _('Maximum job timeout')
.col-sm-10
= f.text_field :maximum_timeout_human_readable, class: 'form-control'
- .form-text.text-muted This timeout will take precedence when lower than Project-defined timeout
+ .form-text.text-muted= _('This timeout will take precedence when lower than Project-defined timeout')
.form-group.row
= label_tag :tag_list, class: 'col-form-label col-sm-2' do
- Tags
+ = _('Tags')
.col-sm-10
= f.text_field :tag_list, value: runner.tag_list.sort.join(', '), class: 'form-control'
- .form-text.text-muted You can setup jobs to only use Runners with specific tags. Separate tags with commas.
+ .form-text.text-muted= _('You can setup jobs to only use Runners with specific tags. Separate tags with commas.')
.form-actions
- = f.submit 'Save changes', class: 'btn btn-save'
+ = f.submit _('Save changes'), class: 'btn btn-success'
diff --git a/app/workers/detect_repository_languages_worker.rb b/app/workers/detect_repository_languages_worker.rb
index 537b8fd5963..cfbbbffc8a6 100644
--- a/app/workers/detect_repository_languages_worker.rb
+++ b/app/workers/detect_repository_languages_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DetectRepositoryLanguagesWorker
include ApplicationWorker
include ExceptionBacktrace
diff --git a/app/workers/todos_destroyer/confidential_issue_worker.rb b/app/workers/todos_destroyer/confidential_issue_worker.rb
index 9d640c14963..481fde8c83d 100644
--- a/app/workers/todos_destroyer/confidential_issue_worker.rb
+++ b/app/workers/todos_destroyer/confidential_issue_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosDestroyer
class ConfidentialIssueWorker
include ApplicationWorker
diff --git a/app/workers/todos_destroyer/entity_leave_worker.rb b/app/workers/todos_destroyer/entity_leave_worker.rb
index e62d9876f4a..7db3f6c84b4 100644
--- a/app/workers/todos_destroyer/entity_leave_worker.rb
+++ b/app/workers/todos_destroyer/entity_leave_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosDestroyer
class EntityLeaveWorker
include ApplicationWorker
diff --git a/app/workers/todos_destroyer/group_private_worker.rb b/app/workers/todos_destroyer/group_private_worker.rb
index 3e47eec7461..21ec4abe478 100644
--- a/app/workers/todos_destroyer/group_private_worker.rb
+++ b/app/workers/todos_destroyer/group_private_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosDestroyer
class GroupPrivateWorker
include ApplicationWorker
diff --git a/app/workers/todos_destroyer/private_features_worker.rb b/app/workers/todos_destroyer/private_features_worker.rb
index f457d5e0471..1e68f0fd5ae 100644
--- a/app/workers/todos_destroyer/private_features_worker.rb
+++ b/app/workers/todos_destroyer/private_features_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosDestroyer
class PrivateFeaturesWorker
include ApplicationWorker
diff --git a/app/workers/todos_destroyer/project_private_worker.rb b/app/workers/todos_destroyer/project_private_worker.rb
index 7a853c36370..064e001530c 100644
--- a/app/workers/todos_destroyer/project_private_worker.rb
+++ b/app/workers/todos_destroyer/project_private_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TodosDestroyer
class ProjectPrivateWorker
include ApplicationWorker
diff --git a/bin/secpick b/bin/secpick
index 5029fe57cfe..5e30c8e72c5 100755
--- a/bin/secpick
+++ b/bin/secpick
@@ -35,7 +35,9 @@ parser.parse!
abort("Missing options. Use #{$0} --help to see the list of options available".red) if options.values.include?(nil)
abort("Wrong version format #{options[:version].bold}".red) unless options[:version] =~ /\A\d*\-\d*\Z/
-branch = [BRANCH_PREFIX, options[:branch], options[:version]].join('-').freeze
+branch = "#{options[:branch]}-#{options[:version]}"
+branch.prepend("#{BRANCH_PREFIX}-") unless branch.start_with?("#{BRANCH_PREFIX}-")
+branch = branch.freeze
stable_branch = "#{BRANCH_PREFIX}-#{options[:version]}".freeze
command = "git fetch #{REMOTE} #{stable_branch} && git checkout #{stable_branch} && git pull #{REMOTE} #{stable_branch} && git checkout -B #{branch} && git cherry-pick #{options[:sha]} && git push #{REMOTE} #{branch}"
diff --git a/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml b/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml
new file mode 100644
index 00000000000..f7521ff9225
--- /dev/null
+++ b/changelogs/unreleased/#47282-Improving-Contributor-On-Boarding-Documentation.yml
@@ -0,0 +1,4 @@
+title: First Improvements made to the contributor on-boarding experience.
+merge_request: 20682
+author: Eddie Stubbington
+type: added
diff --git a/changelogs/unreleased/25990-web-terminal-improvements.yml b/changelogs/unreleased/25990-web-terminal-improvements.yml
new file mode 100644
index 00000000000..99a4a82ea66
--- /dev/null
+++ b/changelogs/unreleased/25990-web-terminal-improvements.yml
@@ -0,0 +1,5 @@
+---
+title: Make terminal button more visible
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/2747-protected-environments-backend-ce.yml b/changelogs/unreleased/2747-protected-environments-backend-ce.yml
new file mode 100644
index 00000000000..dcec74a33a7
--- /dev/null
+++ b/changelogs/unreleased/2747-protected-environments-backend-ce.yml
@@ -0,0 +1,5 @@
+---
+title: CE Port of Protected Environments backend
+merge_request: 20859
+author:
+type: other
diff --git a/changelogs/unreleased/28930-add-project-reference-filter.yml b/changelogs/unreleased/28930-add-project-reference-filter.yml
new file mode 100644
index 00000000000..c7679c5fe76
--- /dev/null
+++ b/changelogs/unreleased/28930-add-project-reference-filter.yml
@@ -0,0 +1,5 @@
+---
+title: Add the ability to reference projects in comments and other markdown text.
+merge_request: 20285
+author: Reuben Pereira
+type: added
diff --git a/changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml b/changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml
new file mode 100644
index 00000000000..e452a91d8b7
--- /dev/null
+++ b/changelogs/unreleased/41996-copy-to-clipboard-tooltip-appears-under-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Solve tooltip appears under modal
+merge_request: 21017
+author:
+type: fixed
diff --git a/changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml b/changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml
new file mode 100644
index 00000000000..6d664511e6e
--- /dev/null
+++ b/changelogs/unreleased/45663-tag-quick-action-on-commit-comments.yml
@@ -0,0 +1,5 @@
+---
+title: "`/tag` quick action on Commit comments"
+merge_request: 20694
+author: Peter Leitzen
+type: added
diff --git a/changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml b/changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml
new file mode 100644
index 00000000000..81ca632947d
--- /dev/null
+++ b/changelogs/unreleased/47752-buttons-on-new-file-page-wrap-outside-of-container-for-long-branch-names.yml
@@ -0,0 +1,5 @@
+---
+title: Fix buttons on the new file page wrapping outside of the container
+merge_request: 21015
+author:
+type: fixed
diff --git a/changelogs/unreleased/48320-cancel-a-created-job.yml b/changelogs/unreleased/48320-cancel-a-created-job.yml
new file mode 100644
index 00000000000..3e7a9e9ae52
--- /dev/null
+++ b/changelogs/unreleased/48320-cancel-a-created-job.yml
@@ -0,0 +1,5 @@
+---
+title: Allows to cancel a Created job
+merge_request: 20635
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/48942-rename-backlog-list-to-open-issue-boards.yml b/changelogs/unreleased/48942-rename-backlog-list-to-open-issue-boards.yml
new file mode 100644
index 00000000000..26851fc2dec
--- /dev/null
+++ b/changelogs/unreleased/48942-rename-backlog-list-to-open-issue-boards.yml
@@ -0,0 +1,5 @@
+---
+title: Change 'Backlog' list title to 'Open' in Issue Boards
+merge_request: 21131
+author:
+type: changed
diff --git a/changelogs/unreleased/48967-disable-statement-timeout.yml b/changelogs/unreleased/48967-disable-statement-timeout.yml
new file mode 100644
index 00000000000..2da800ed41e
--- /dev/null
+++ b/changelogs/unreleased/48967-disable-statement-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: disable_statement_timeout no longer leak to other migrations
+merge_request: 20503
+author:
+type: fixed
diff --git a/changelogs/unreleased/49905-fix-checkboxes-runners.yml b/changelogs/unreleased/49905-fix-checkboxes-runners.yml
new file mode 100644
index 00000000000..af40e5348b8
--- /dev/null
+++ b/changelogs/unreleased/49905-fix-checkboxes-runners.yml
@@ -0,0 +1,5 @@
+---
+title: Fix checkboxes on runner admin settings - The labels are now clickable
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/50047-spam-logs-pagination.yml b/changelogs/unreleased/50047-spam-logs-pagination.yml
new file mode 100644
index 00000000000..ca5f432cd8c
--- /dev/null
+++ b/changelogs/unreleased/50047-spam-logs-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Add gitlab theme to spam logs pagination
+merge_request: 21145
+author:
+type: fixed
diff --git a/changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml b/changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml
new file mode 100644
index 00000000000..ca17a41d611
--- /dev/null
+++ b/changelogs/unreleased/50063-add-missing-i18n-strings-to-issue-boards.yml
@@ -0,0 +1,5 @@
+---
+title: Added missing i18n strings to issue boards lables dropdown
+merge_request: 21081
+author:
+type: other
diff --git a/changelogs/unreleased/50101-aritfacts-block.yml b/changelogs/unreleased/50101-aritfacts-block.yml
new file mode 100644
index 00000000000..435e9d9d486
--- /dev/null
+++ b/changelogs/unreleased/50101-aritfacts-block.yml
@@ -0,0 +1,5 @@
+---
+title: Creates Vue component for artifacts block on job page
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/50101-erased-block.yml b/changelogs/unreleased/50101-erased-block.yml
new file mode 100644
index 00000000000..5a5c9bc0fc4
--- /dev/null
+++ b/changelogs/unreleased/50101-erased-block.yml
@@ -0,0 +1,5 @@
+---
+title: Creates vue component for erased block on job view
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/50101-job-log-component.yml b/changelogs/unreleased/50101-job-log-component.yml
new file mode 100644
index 00000000000..0759e7cfbd9
--- /dev/null
+++ b/changelogs/unreleased/50101-job-log-component.yml
@@ -0,0 +1,5 @@
+---
+title: Creates vue component for job log trace
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/50101-stuck-component.yml b/changelogs/unreleased/50101-stuck-component.yml
new file mode 100644
index 00000000000..bfe4009a2b3
--- /dev/null
+++ b/changelogs/unreleased/50101-stuck-component.yml
@@ -0,0 +1,5 @@
+---
+title: Creates Vvue component for warning block about stuck runners
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/50126-blocked-user-card.yml b/changelogs/unreleased/50126-blocked-user-card.yml
new file mode 100644
index 00000000000..a42d62e5530
--- /dev/null
+++ b/changelogs/unreleased/50126-blocked-user-card.yml
@@ -0,0 +1,5 @@
+---
+title: Fix blocked user card style
+merge_request: 21095
+author:
+type: fixed
diff --git a/changelogs/unreleased/50180-fa-icon-google-audit.yml b/changelogs/unreleased/50180-fa-icon-google-audit.yml
new file mode 100644
index 00000000000..fb1771a7570
--- /dev/null
+++ b/changelogs/unreleased/50180-fa-icon-google-audit.yml
@@ -0,0 +1,5 @@
+---
+title: Show google icon in audit log
+merge_request: 21207
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml b/changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml
new file mode 100644
index 00000000000..0f6208d0c7a
--- /dev/null
+++ b/changelogs/unreleased/50243-auto-devops-behind-a-proxy.yml
@@ -0,0 +1,6 @@
+---
+title: Vendor Auto-DevOps.gitlab-ci.yml with new proxy env vars passed through to
+ docker
+merge_request: 21159
+author: kinolaev
+type: added
diff --git a/changelogs/unreleased/50257-fix-auto-devops-glibc-pubkey-url.yml b/changelogs/unreleased/50257-fix-auto-devops-glibc-pubkey-url.yml
new file mode 100644
index 00000000000..e081dfe6093
--- /dev/null
+++ b/changelogs/unreleased/50257-fix-auto-devops-glibc-pubkey-url.yml
@@ -0,0 +1,5 @@
+---
+title: 'Auto-DevOps.gitlab-ci.yml: Update glibc package signing key URL'
+merge_request: 21182
+author: sgerrand
+type: fixed
diff --git a/changelogs/unreleased/50281-js-pages-do-not-load-on-windows-8-ie-11.yml b/changelogs/unreleased/50281-js-pages-do-not-load-on-windows-8-ie-11.yml
new file mode 100644
index 00000000000..eb20e34c466
--- /dev/null
+++ b/changelogs/unreleased/50281-js-pages-do-not-load-on-windows-8-ie-11.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken JavaScript in IE11
+merge_request: 21214
+author:
+type: fixed
diff --git a/changelogs/unreleased/add_google_noto_color_emoji_font.yml b/changelogs/unreleased/add_google_noto_color_emoji_font.yml
new file mode 100644
index 00000000000..9ba46262767
--- /dev/null
+++ b/changelogs/unreleased/add_google_noto_color_emoji_font.yml
@@ -0,0 +1,5 @@
+---
+title: Add Noto Color Emoji font support
+merge_request: 19036
+author: Alexander Popov
+type: changed
diff --git a/changelogs/unreleased/auto-devops-gitlab-ci-glic-228.yml b/changelogs/unreleased/auto-devops-gitlab-ci-glic-228.yml
new file mode 100644
index 00000000000..a1625193189
--- /dev/null
+++ b/changelogs/unreleased/auto-devops-gitlab-ci-glic-228.yml
@@ -0,0 +1,5 @@
+---
+title: 'Auto-DevOps.gitlab-ci.yml: update glibc package to 2.28'
+merge_request: 21191
+author: sgerrand
+type: fixed
diff --git a/changelogs/unreleased/bvl-merge-base-api.yml b/changelogs/unreleased/bvl-merge-base-api.yml
new file mode 100644
index 00000000000..78fb3ce0897
--- /dev/null
+++ b/changelogs/unreleased/bvl-merge-base-api.yml
@@ -0,0 +1,5 @@
+---
+title: Get the merge base of two refs through the API
+merge_request: 20929
+author:
+type: added
diff --git a/changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml b/changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml
new file mode 100644
index 00000000000..0c6a1071cdd
--- /dev/null
+++ b/changelogs/unreleased/ce-5666-optimize_querying_manageable_groups.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize querying User#manageable_groups
+merge_request: 21050
+author:
+type: performance
diff --git a/changelogs/unreleased/expose-all-artifacts-sizes-in-jobs-api.yml b/changelogs/unreleased/expose-all-artifacts-sizes-in-jobs-api.yml
new file mode 100644
index 00000000000..1453d39934b
--- /dev/null
+++ b/changelogs/unreleased/expose-all-artifacts-sizes-in-jobs-api.yml
@@ -0,0 +1,5 @@
+---
+title: Expose all artifacts sizes in jobs api
+merge_request: 20821
+author: Peter Marko
+type: added
diff --git a/changelogs/unreleased/feat-add-default-avatar-to-group.yml b/changelogs/unreleased/feat-add-default-avatar-to-group.yml
new file mode 100644
index 00000000000..56d8f2ccd6d
--- /dev/null
+++ b/changelogs/unreleased/feat-add-default-avatar-to-group.yml
@@ -0,0 +1,5 @@
+---
+title: Add default avatar to group
+merge_request: 17271
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml b/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml
new file mode 100644
index 00000000000..d215d034917
--- /dev/null
+++ b/changelogs/unreleased/fix-labels-list-item-height-with-no-description.yml
@@ -0,0 +1,5 @@
+---
+title: Fix label list item container height when there is no label description
+merge_request: 21106
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-pipeline-fixture-seeder.yml b/changelogs/unreleased/fix-pipeline-fixture-seeder.yml
new file mode 100644
index 00000000000..02b83062e07
--- /dev/null
+++ b/changelogs/unreleased/fix-pipeline-fixture-seeder.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pipeline fixture seeder
+merge_request: 21088
+author:
+type: fixed
diff --git a/changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml b/changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml
new file mode 100644
index 00000000000..068681dfe19
--- /dev/null
+++ b/changelogs/unreleased/fl-reduce-ee-conflicts-reports-code.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce differences between CE and EE code base in reports components
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml b/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml
new file mode 100644
index 00000000000..a77f3baeed3
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-models-even-more-still.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in rest of app/models/**/*.rb
+merge_request: gfyoung
+author:
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-vestigial.yml b/changelogs/unreleased/frozen-string-enable-app-vestigial.yml
new file mode 100644
index 00000000000..8cb7bd43784
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-vestigial.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in vestigial app files
+merge_request:
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/gitaly-install-path.yml b/changelogs/unreleased/gitaly-install-path.yml
new file mode 100644
index 00000000000..4b24cd81dc7
--- /dev/null
+++ b/changelogs/unreleased/gitaly-install-path.yml
@@ -0,0 +1,5 @@
+---
+title: Remove storage path dependency of gitaly install task
+merge_request: 21101
+author:
+type: changed
diff --git a/changelogs/unreleased/ide-header-buttons-tooltip.yml b/changelogs/unreleased/ide-header-buttons-tooltip.yml
new file mode 100644
index 00000000000..4c8f6fd554f
--- /dev/null
+++ b/changelogs/unreleased/ide-header-buttons-tooltip.yml
@@ -0,0 +1,5 @@
+---
+title: Added tooltips to tree list header
+merge_request: 21138
+author:
+type: added
diff --git a/changelogs/unreleased/ide-open-empty-merge-request.yml b/changelogs/unreleased/ide-open-empty-merge-request.yml
new file mode 100644
index 00000000000..05f2de5d31c
--- /dev/null
+++ b/changelogs/unreleased/ide-open-empty-merge-request.yml
@@ -0,0 +1,5 @@
+---
+title: Fix empty merge requests not opening in the Web IDE
+merge_request: 21102
+author:
+type: fixed
diff --git a/changelogs/unreleased/mk-bump-rainbow-gem.yml b/changelogs/unreleased/mk-bump-rainbow-gem.yml
new file mode 100644
index 00000000000..31c003fb4d9
--- /dev/null
+++ b/changelogs/unreleased/mk-bump-rainbow-gem.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bin/secpick error and security branch prefixing
+merge_request: 21210
+author:
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml b/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml
new file mode 100644
index 00000000000..e31768773b1
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-duplicate-gpg-signature.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix specs duplicate key value violates unique constraint 'index_gpg_signatures_on_commit_sha'
+merge_request: 21119
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/sh-bump-rugged-0-27-4.yml b/changelogs/unreleased/sh-bump-rugged-0-27-4.yml
new file mode 100644
index 00000000000..50373cb81ad
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-rugged-0-27-4.yml
@@ -0,0 +1,5 @@
+---
+title: Bump rugged to 0.27.4 for security fixes
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml b/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml
new file mode 100644
index 00000000000..3f7044833f1
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-bitbucket-cloud-importer-replies.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Bitbucket Cloud importer omitting replies
+merge_request: 21076
+author:
+type: fixed
diff --git a/changelogs/unreleased/tz-mr-incremental-rendering.yml b/changelogs/unreleased/tz-mr-incremental-rendering.yml
new file mode 100644
index 00000000000..a35fa200363
--- /dev/null
+++ b/changelogs/unreleased/tz-mr-incremental-rendering.yml
@@ -0,0 +1,4 @@
+title: Incremental rendering with Vue on merge request page
+merge_request: 21063
+author:
+type: performance
diff --git a/changelogs/unreleased/visual-improvements-language-bar.yml b/changelogs/unreleased/visual-improvements-language-bar.yml
new file mode 100644
index 00000000000..23cae22b962
--- /dev/null
+++ b/changelogs/unreleased/visual-improvements-language-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Improve visuals of language bar on projects
+merge_request: 21006
+author:
+type: changed
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index d3a63aa2a78..5535c4a14e5 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -41,7 +41,7 @@ class Gitlab::Seeder::Pipelines
when: 'manual', status: :skipped },
# notify stage
- { name: 'slack', stage: 'notify', when: 'manual', status: :created },
+ { name: 'slack', stage: 'notify', when: 'manual', status: :success },
]
EXTERNAL_JOBS = [
{ name: 'jenkins', stage: 'test', status: :success,
@@ -54,16 +54,10 @@ class Gitlab::Seeder::Pipelines
def seed!
pipelines.each do |pipeline|
- begin
- BUILDS.each { |opts| build_create!(pipeline, opts) }
- EXTERNAL_JOBS.each { |opts| commit_status_create!(pipeline, opts) }
- print '.'
- rescue ActiveRecord::RecordInvalid
- print 'F'
- ensure
- pipeline.update_duration
- pipeline.update_status
- end
+ BUILDS.each { |opts| build_create!(pipeline, opts) }
+ EXTERNAL_JOBS.each { |opts| commit_status_create!(pipeline, opts) }
+ pipeline.update_duration
+ pipeline.update_status
end
end
@@ -87,7 +81,9 @@ class Gitlab::Seeder::Pipelines
branch = merge_request.source_branch
merge_request.commits.last(4).map do |commit|
- create_pipeline!(project, branch, commit)
+ create_pipeline!(project, branch, commit).tap do |pipeline|
+ merge_request.update!(head_pipeline_id: pipeline.id)
+ end
end
end
@@ -98,7 +94,7 @@ class Gitlab::Seeder::Pipelines
def create_pipeline!(project, ref, commit)
- project.pipelines.create(sha: commit.id, ref: ref, source: :push)
+ project.pipelines.create!(sha: commit.id, ref: ref, source: :push)
end
def build_create!(pipeline, opts = {})
@@ -111,24 +107,39 @@ class Gitlab::Seeder::Pipelines
# block directly to `Ci::Build#create!`.
setup_artifacts(build)
+ setup_test_reports(build)
setup_build_log(build)
build.project.environments.
find_or_create_by(name: build.expanded_environment_name)
- build.save
+ build.save!
end
end
def setup_artifacts(build)
- return unless %w[build test].include?(build.stage)
+ return unless build.stage == "build"
artifacts_cache_file(artifacts_archive_path) do |file|
- build.job_artifacts.build(project: build.project, file_type: :archive, file: file)
+ build.job_artifacts.build(project: build.project, file_type: :archive, file_format: :zip, file: file)
end
artifacts_cache_file(artifacts_metadata_path) do |file|
- build.job_artifacts.build(project: build.project, file_type: :metadata, file: file)
+ build.job_artifacts.build(project: build.project, file_type: :metadata, file_format: :gzip, file: file)
+ end
+ end
+
+ def setup_test_reports(build)
+ return unless build.stage == "test" && build.name == "rspec:osx"
+
+ if build.ref == build.project.default_branch
+ artifacts_cache_file(test_reports_pass_path) do |file|
+ build.job_artifacts.build(project: build.project, file_type: :junit, file_format: :gzip, file: file)
+ end
+ else
+ artifacts_cache_file(test_reports_failed_path) do |file|
+ build.job_artifacts.build(project: build.project, file_type: :junit, file_format: :gzip, file: file)
+ end
end
end
@@ -171,13 +182,21 @@ class Gitlab::Seeder::Pipelines
Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
end
+ def test_reports_pass_path
+ Rails.root + 'spec/fixtures/junit/junit_ant.xml.gz'
+ end
+
+ def test_reports_failed_path
+ Rails.root + 'spec/fixtures/junit/junit.xml.gz'
+ end
+
def artifacts_cache_file(file_path)
- cache_path = file_path.to_s.gsub('ci_', "p#{@project.id}_")
+ file = Tempfile.new("artifacts")
+ file.close
- FileUtils.copy(file_path, cache_path)
- File.open(cache_path) do |file|
- yield file
- end
+ FileUtils.copy(file_path, file.path)
+
+ yield(UploadedFile.new(file.path, filename: File.basename(file_path)))
end
end
diff --git a/db/fixtures/development/23_spam_logs.rb b/db/fixtures/development/23_spam_logs.rb
new file mode 100644
index 00000000000..81cc13e6b2d
--- /dev/null
+++ b/db/fixtures/development/23_spam_logs.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Db
+ module Fixtures
+ module Development
+ class SpamLog
+ def self.seed
+ Gitlab::Seeder.quiet do
+ (::SpamLog.default_per_page + 3).times do |i|
+ ::SpamLog.create(
+ user: self.random_user,
+ user_agent: FFaker::Lorem.sentence,
+ source_ip: FFaker::Internet.ip_v4_address,
+ title: FFaker::Lorem.sentence,
+ description: FFaker::Lorem.paragraph,
+ via_api: FFaker::Boolean.random,
+ submitted_as_ham: FFaker::Boolean.random,
+ recaptcha_verified: FFaker::Boolean.random)
+ print '.'
+ end
+ end
+ end
+
+ def self.random_user
+ User.find(User.pluck(:id).sample)
+ end
+ end
+ end
+ end
+end
+
+Db::Fixtures::Development::SpamLog.seed
diff --git a/db/migrate/20160317092222_add_moved_to_to_issue.rb b/db/migrate/20160317092222_add_moved_to_to_issue.rb
index 461e7fb3a9b..2bf549d7ecd 100644
--- a/db/migrate/20160317092222_add_moved_to_to_issue.rb
+++ b/db/migrate/20160317092222_add_moved_to_to_issue.rb
@@ -1,5 +1,5 @@
class AddMovedToToIssue < ActiveRecord::Migration
def change
- add_reference :issues, :moved_to, references: :issues
+ add_reference :issues, :moved_to, references: :issues # rubocop:disable Migration/AddReference
end
end
diff --git a/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb b/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
index 1199073ed3a..12352d98a62 100644
--- a/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
+++ b/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
@@ -106,11 +106,11 @@ class ProjectForeignKeysWithCascadingDeletes < ActiveRecord::Migration
# Disables statement timeouts for the current connection. This is
# necessary as removing of orphaned data might otherwise exceed the
# statement timeout.
- disable_statement_timeout
+ disable_statement_timeout do
+ remove_orphans(*queue.pop) until queue.empty?
- remove_orphans(*queue.pop) until queue.empty?
-
- steal_from_queues(queues - [queue])
+ steal_from_queues(queues - [queue])
+ end
end
end
end
diff --git a/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb b/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb
index db60c2087b9..a770ff63b4e 100644
--- a/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb
+++ b/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb
@@ -25,8 +25,9 @@ class AddLowerPathIndexToRedirectRoutes < ActiveRecord::Migration
# trivial to write a query that checks for an index. BUT there is a
# convenient `IF EXISTS` parameter for `DROP INDEX`.
if supports_drop_index_concurrently?
- disable_statement_timeout
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
+ disable_statement_timeout do
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
+ end
else
execute "DROP INDEX IF EXISTS #{INDEX_NAME};"
end
diff --git a/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb b/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb
index d1a039ed551..130b24fe6f0 100644
--- a/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb
+++ b/db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb
@@ -8,25 +8,25 @@ class AddIndexOnNamespacesLowerName < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- if Gitlab::Database.version.to_f >= 9.5
- # Allow us to hot-patch the index manually ahead of the migration
- execute "CREATE INDEX CONCURRENTLY IF NOT EXISTS #{INDEX_NAME} ON namespaces (lower(name));"
- else
- execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON namespaces (lower(name));"
+ disable_statement_timeout do
+ if Gitlab::Database.version.to_f >= 9.5
+ # Allow us to hot-patch the index manually ahead of the migration
+ execute "CREATE INDEX CONCURRENTLY IF NOT EXISTS #{INDEX_NAME} ON namespaces (lower(name));"
+ else
+ execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON namespaces (lower(name));"
+ end
end
end
def down
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- if Gitlab::Database.version.to_f >= 9.2
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
- else
- execute "DROP INDEX IF EXISTS #{INDEX_NAME};"
+ disable_statement_timeout do
+ if Gitlab::Database.version.to_f >= 9.2
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};"
+ else
+ execute "DROP INDEX IF EXISTS #{INDEX_NAME};"
+ end
end
end
end
diff --git a/db/migrate/20180113220114_rework_redirect_routes_indexes.rb b/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
index ab9971be074..53f82a31203 100644
--- a/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
+++ b/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
@@ -18,51 +18,51 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration
OLD_INDEX_NAME_PATH_LOWER = "index_on_redirect_routes_lower_path"
def up
- disable_statement_timeout
-
- # this is a plain btree on a single boolean column. It'll never be
- # selective enough to be valuable. This class is called by
- # setup_postgresql.rake so it needs to be able to handle this
- # index not existing.
- if index_exists?(:redirect_routes, :permanent)
- remove_concurrent_index(:redirect_routes, :permanent)
- end
+ disable_statement_timeout do
+ # this is a plain btree on a single boolean column. It'll never be
+ # selective enough to be valuable. This class is called by
+ # setup_postgresql.rake so it needs to be able to handle this
+ # index not existing.
+ if index_exists?(:redirect_routes, :permanent)
+ remove_concurrent_index(:redirect_routes, :permanent)
+ end
- # If we're on MySQL then the existing index on path is ok. But on
- # Postgres we need to clean things up:
- return unless Gitlab::Database.postgresql?
+ # If we're on MySQL then the existing index on path is ok. But on
+ # Postgres we need to clean things up:
+ break unless Gitlab::Database.postgresql?
- if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
+ if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
- # Unique index on lower(path) across both types of redirect_routes:
- execute("CREATE UNIQUE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_UNIQUE} ON redirect_routes (lower(path) varchar_pattern_ops);")
+ # Unique index on lower(path) across both types of redirect_routes:
+ execute("CREATE UNIQUE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_UNIQUE} ON redirect_routes (lower(path) varchar_pattern_ops);")
- # Make two indexes on path -- one for permanent and one for temporary routes:
- execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
- execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+ # Make two indexes on path -- one for permanent and one for temporary routes:
+ execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
+ execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
- # Remove the old indexes:
+ # Remove the old indexes:
- # This one needed to be on lower(path) but wasn't so it's replaced with the two above
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_TPOPS};"
+ # This one needed to be on lower(path) but wasn't so it's replaced with the two above
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_TPOPS};"
- # This one isn't needed because we only ever do = and LIKE on this
- # column so the varchar_pattern_ops index is sufficient
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_LOWER};"
+ # This one isn't needed because we only ever do = and LIKE on this
+ # column so the varchar_pattern_ops index is sufficient
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_LOWER};"
+ end
end
def down
- disable_statement_timeout
+ disable_statement_timeout do
+ add_concurrent_index(:redirect_routes, :permanent)
- add_concurrent_index(:redirect_routes, :permanent)
+ break unless Gitlab::Database.postgresql?
- return unless Gitlab::Database.postgresql?
+ execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
+ execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
- execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
- execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
-
- execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_UNIQUE};")
- execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};")
- execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_UNIQUE};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};")
+ execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};")
+ end
end
end
diff --git a/db/migrate/20180403035759_create_project_ci_cd_settings.rb b/db/migrate/20180403035759_create_project_ci_cd_settings.rb
index 06856af6204..173e662cffc 100644
--- a/db/migrate/20180403035759_create_project_ci_cd_settings.rb
+++ b/db/migrate/20180403035759_create_project_ci_cd_settings.rb
@@ -13,16 +13,16 @@ class CreateProjectCiCdSettings < ActiveRecord::Migration
end
end
- disable_statement_timeout
+ disable_statement_timeout do
+ # This particular INSERT will take between 10 and 20 seconds.
+ execute 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects'
- # This particular INSERT will take between 10 and 20 seconds.
- execute 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects'
+ # We add the index and foreign key separately so the above INSERT statement
+ # takes as little time as possible.
+ add_concurrent_index(:project_ci_cd_settings, :project_id, unique: true)
- # We add the index and foreign key separately so the above INSERT statement
- # takes as little time as possible.
- add_concurrent_index(:project_ci_cd_settings, :project_id, unique: true)
-
- add_foreign_key_with_retry
+ add_foreign_key_with_retry
+ end
end
def down
diff --git a/db/migrate/20180420010616_cleanup_build_stage_migration.rb b/db/migrate/20180420010616_cleanup_build_stage_migration.rb
index 24777294101..5e9fe756efd 100644
--- a/db/migrate/20180420010616_cleanup_build_stage_migration.rb
+++ b/db/migrate/20180420010616_cleanup_build_stage_migration.rb
@@ -14,48 +14,50 @@ class CleanupBuildStageMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
-
- ##
- # We steal from the background migrations queue to catch up with the
- # scheduled migrations set.
- #
- Gitlab::BackgroundMigration.steal('MigrateBuildStage')
-
- ##
- # We add temporary index, to make iteration over batches more performant.
- # Conditional here is to avoid the need of doing that in a separate
- # migration file to make this operation idempotent.
- #
- unless index_exists_by_name?(:ci_builds, TMP_INDEX)
- add_concurrent_index(:ci_builds, :id, where: 'stage_id IS NULL', name: TMP_INDEX)
- end
-
- ##
- # We check if there are remaining rows that should be migrated (for example
- # if Sidekiq / Redis fails / is restarted, what could result in not all
- # background migrations being executed correctly.
- #
- # We migrate remaining rows synchronously in a blocking way, to make sure
- # that when this migration is done we are confident that all rows are
- # already migrated.
- #
- Build.where('stage_id IS NULL').each_batch(of: 50) do |batch|
- range = batch.pluck('MIN(id)', 'MAX(id)').first
-
- Gitlab::BackgroundMigration::MigrateBuildStage.new.perform(*range)
+ disable_statement_timeout do
+ ##
+ # We steal from the background migrations queue to catch up with the
+ # scheduled migrations set.
+ #
+ Gitlab::BackgroundMigration.steal('MigrateBuildStage')
+
+ ##
+ # We add temporary index, to make iteration over batches more performant.
+ # Conditional here is to avoid the need of doing that in a separate
+ # migration file to make this operation idempotent.
+ #
+ unless index_exists_by_name?(:ci_builds, TMP_INDEX)
+ add_concurrent_index(:ci_builds, :id, where: 'stage_id IS NULL', name: TMP_INDEX)
+ end
+
+ ##
+ # We check if there are remaining rows that should be migrated (for example
+ # if Sidekiq / Redis fails / is restarted, what could result in not all
+ # background migrations being executed correctly.
+ #
+ # We migrate remaining rows synchronously in a blocking way, to make sure
+ # that when this migration is done we are confident that all rows are
+ # already migrated.
+ #
+ Build.where('stage_id IS NULL').each_batch(of: 50) do |batch|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ Gitlab::BackgroundMigration::MigrateBuildStage.new.perform(*range)
+ end
+
+ ##
+ # We remove temporary index, because it is not required during standard
+ # operations and runtime.
+ #
+ remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
end
-
- ##
- # We remove temporary index, because it is not required during standard
- # operations and runtime.
- #
- remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
end
def down
if index_exists_by_name?(:ci_builds, TMP_INDEX)
- remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
+ disable_statement_timeout do
+ remove_concurrent_index_by_name(:ci_builds, TMP_INDEX)
+ end
end
end
end
diff --git a/db/migrate/20180504195842_project_name_lower_index.rb b/db/migrate/20180504195842_project_name_lower_index.rb
index d6f25d3d4ab..74f3673bb03 100644
--- a/db/migrate/20180504195842_project_name_lower_index.rb
+++ b/db/migrate/20180504195842_project_name_lower_index.rb
@@ -13,20 +13,20 @@ class ProjectNameLowerIndex < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON projects (LOWER(name))"
+ disable_statement_timeout do
+ execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON projects (LOWER(name))"
+ end
end
def down
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- if supports_drop_index_concurrently?
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME}"
- else
- execute "DROP INDEX IF EXISTS #{INDEX_NAME}"
+ disable_statement_timeout do
+ if supports_drop_index_concurrently?
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME}"
+ else
+ execute "DROP INDEX IF EXISTS #{INDEX_NAME}"
+ end
end
end
end
diff --git a/db/migrate/20180702124358_remove_orphaned_routes.rb b/db/migrate/20180702124358_remove_orphaned_routes.rb
index 6f6e289ba87..4068e479b6c 100644
--- a/db/migrate/20180702124358_remove_orphaned_routes.rb
+++ b/db/migrate/20180702124358_remove_orphaned_routes.rb
@@ -28,16 +28,16 @@ class RemoveOrphanedRoutes < ActiveRecord::Migration
# which is pretty close to our 15 second statement timeout. To ensure a
# smooth deployment procedure we disable the statement timeouts for this
# migration, just in case.
- disable_statement_timeout
-
- # On GitLab.com there are around 4000 orphaned project routes, and around
- # 150 orphaned namespace routes.
- [
- Route.orphaned_project_routes,
- Route.orphaned_namespace_routes
- ].each do |relation|
- relation.each_batch(of: 1_000) do |batch|
- batch.delete_all
+ disable_statement_timeout do
+ # On GitLab.com there are around 4000 orphaned project routes, and around
+ # 150 orphaned namespace routes.
+ [
+ Route.orphaned_project_routes,
+ Route.orphaned_namespace_routes
+ ].each do |relation|
+ relation.each_batch(of: 1_000) do |batch|
+ batch.delete_all
+ end
end
end
end
diff --git a/db/optional_migrations/composite_primary_keys.rb b/db/optional_migrations/composite_primary_keys.rb
index d45705021b0..b330da13d43 100644
--- a/db/optional_migrations/composite_primary_keys.rb
+++ b/db/optional_migrations/composite_primary_keys.rb
@@ -29,18 +29,20 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
- TABLES.each do |index|
- add_primary_key(index)
+ disable_statement_timeout do
+ TABLES.each do |index|
+ add_primary_key(index)
+ end
end
end
def down
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
- TABLES.each do |index|
- remove_primary_key(index)
+ disable_statement_timeout do
+ TABLES.each do |index|
+ remove_primary_key(index)
+ end
end
end
diff --git a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
index bba37e32c01..845c6f0557f 100644
--- a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
+++ b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
@@ -8,9 +8,9 @@ class EnableAutoCancelPendingPipelinesForAll < ActiveRecord::Migration
DOWNTIME = false
def up
- disable_statement_timeout
-
- update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
+ disable_statement_timeout do
+ update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
+ end
end
def down
diff --git a/db/post_migrate/20170503004427_update_retried_for_ci_build.rb b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
index b0b58ab3011..079f0e7511f 100644
--- a/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
+++ b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
@@ -7,12 +7,12 @@ class UpdateRetriedForCiBuild < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
if Gitlab::Database.mysql?
up_mysql
else
- up_postgres
+ disable_statement_timeout do
+ up_postgres
+ end
end
end
diff --git a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
index 81e9d050668..5df3ab71648 100644
--- a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
+++ b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
@@ -7,20 +7,20 @@ class AddHeadPipelineForEachMergeRequest < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
pipelines = Arel::Table.new(:ci_pipelines)
merge_requests = Arel::Table.new(:merge_requests)
- head_id = pipelines
- .project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]]))
- .from(pipelines)
- .where(pipelines[:ref].eq(merge_requests[:source_branch]))
- .where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
+ disable_statement_timeout do
+ head_id = pipelines
+ .project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]]))
+ .from(pipelines)
+ .where(pipelines[:ref].eq(merge_requests[:source_branch]))
+ .where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
- sub_query = Arel::Nodes::SqlLiteral.new(Arel::Nodes::Grouping.new(head_id).to_sql)
+ sub_query = Arel::Nodes::SqlLiteral.new(Arel::Nodes::Grouping.new(head_id).to_sql)
- update_column_in_batches(:merge_requests, :head_pipeline_id, sub_query)
+ update_column_in_batches(:merge_requests, :head_pipeline_id, sub_query)
+ end
end
def down
diff --git a/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb b/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb
index 2125cc046e5..c996ddbec84 100644
--- a/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb
+++ b/db/post_migrate/20170525140254_rename_all_reserved_paths_again.rb
@@ -87,16 +87,16 @@ class RenameAllReservedPathsAgain < ActiveRecord::Migration
].freeze
def up
- disable_statement_timeout
-
- TOP_LEVEL_ROUTES.each { |route| rename_root_paths(route) }
- PROJECT_WILDCARD_ROUTES.each { |route| rename_wildcard_paths(route) }
- GROUP_ROUTES.each { |route| rename_child_paths(route) }
+ disable_statement_timeout do
+ TOP_LEVEL_ROUTES.each { |route| rename_root_paths(route) }
+ PROJECT_WILDCARD_ROUTES.each { |route| rename_wildcard_paths(route) }
+ GROUP_ROUTES.each { |route| rename_child_paths(route) }
+ end
end
def down
- disable_statement_timeout
-
- revert_renames
+ disable_statement_timeout do
+ revert_renames
+ end
end
end
diff --git a/db/post_migrate/20170526185842_migrate_pipeline_stages.rb b/db/post_migrate/20170526185842_migrate_pipeline_stages.rb
index afd4db183c2..736aff77f02 100644
--- a/db/post_migrate/20170526185842_migrate_pipeline_stages.rb
+++ b/db/post_migrate/20170526185842_migrate_pipeline_stages.rb
@@ -6,17 +6,17 @@ class MigratePipelineStages < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
- execute <<-SQL.strip_heredoc
- INSERT INTO ci_stages (project_id, pipeline_id, name)
- SELECT project_id, commit_id, stage FROM ci_builds
- WHERE stage IS NOT NULL
- AND stage_id IS NULL
- AND EXISTS (SELECT 1 FROM projects WHERE projects.id = ci_builds.project_id)
- AND EXISTS (SELECT 1 FROM ci_pipelines WHERE ci_pipelines.id = ci_builds.commit_id)
- GROUP BY project_id, commit_id, stage
- ORDER BY MAX(stage_idx)
- SQL
+ disable_statement_timeout do
+ execute <<-SQL.strip_heredoc
+ INSERT INTO ci_stages (project_id, pipeline_id, name)
+ SELECT project_id, commit_id, stage FROM ci_builds
+ WHERE stage IS NOT NULL
+ AND stage_id IS NULL
+ AND EXISTS (SELECT 1 FROM projects WHERE projects.id = ci_builds.project_id)
+ AND EXISTS (SELECT 1 FROM ci_pipelines WHERE ci_pipelines.id = ci_builds.commit_id)
+ GROUP BY project_id, commit_id, stage
+ ORDER BY MAX(stage_idx)
+ SQL
+ end
end
end
diff --git a/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
index 31a73bb3b27..a7bfba0ab2b 100644
--- a/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
+++ b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
@@ -7,22 +7,22 @@ class MigrateBuildStageReferenceAgain < ActiveRecord::Migration
disable_ddl_transaction!
def up
- disable_statement_timeout
-
stage_id = Arel.sql <<-SQL.strip_heredoc
(SELECT id FROM ci_stages
WHERE ci_stages.pipeline_id = ci_builds.commit_id
AND ci_stages.name = ci_builds.stage)
SQL
- update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
- query.where(table[:stage_id].eq(nil))
+ disable_statement_timeout do
+ update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
+ query.where(table[:stage_id].eq(nil))
+ end
end
end
def down
- disable_statement_timeout
-
- update_column_in_batches(:ci_builds, :stage_id, nil)
+ disable_statement_timeout do
+ update_column_in_batches(:ci_builds, :stage_id, nil)
+ end
end
end
diff --git a/db/post_migrate/20170711145558_migrate_stages_statuses.rb b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
index 65755c0e824..265f7317b9b 100644
--- a/db/post_migrate/20170711145558_migrate_stages_statuses.rb
+++ b/db/post_migrate/20170711145558_migrate_stages_statuses.rb
@@ -26,9 +26,9 @@ class MigrateStagesStatuses < ActiveRecord::Migration
end
def down
- disable_statement_timeout
-
- # rubocop:disable Migration/UpdateLargeTable
- update_column_in_batches(:ci_stages, :status, nil)
+ disable_statement_timeout do
+ # rubocop:disable Migration/UpdateLargeTable
+ update_column_in_batches(:ci_stages, :status, nil)
+ end
end
end
diff --git a/db/post_migrate/20171207150343_remove_soft_removed_objects.rb b/db/post_migrate/20171207150343_remove_soft_removed_objects.rb
index 3e2dedfdd6a..3109b6dbf8e 100644
--- a/db/post_migrate/20171207150343_remove_soft_removed_objects.rb
+++ b/db/post_migrate/20171207150343_remove_soft_removed_objects.rb
@@ -78,12 +78,12 @@ class RemoveSoftRemovedObjects < ActiveRecord::Migration
MODELS = [Issue, MergeRequest, CiPipelineSchedule, CiTrigger].freeze
def up
- disable_statement_timeout
-
- remove_personal_routes
- remove_personal_namespaces
- remove_group_namespaces
- remove_simple_soft_removed_rows
+ disable_statement_timeout do
+ remove_personal_routes
+ remove_personal_namespaces
+ remove_group_namespaces
+ remove_simple_soft_removed_rows
+ end
end
def down
diff --git a/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb b/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb
index 61ea85eb2a7..269f1287f91 100644
--- a/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb
+++ b/db/post_migrate/20180119121225_remove_redundant_pipeline_stages.rb
@@ -38,29 +38,29 @@ class RemoveRedundantPipelineStages < ActiveRecord::Migration
end
def remove_redundant_pipeline_stages!
- disable_statement_timeout
-
- redundant_stages_ids = <<~SQL
- SELECT id FROM ci_stages WHERE (pipeline_id, name) IN (
- SELECT pipeline_id, name FROM ci_stages
- GROUP BY pipeline_id, name HAVING COUNT(*) > 1
- )
- SQL
-
- execute <<~SQL
- UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
- SQL
-
- if Gitlab::Database.postgresql?
- execute <<~SQL
- DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
+ disable_statement_timeout do
+ redundant_stages_ids = <<~SQL
+ SELECT id FROM ci_stages WHERE (pipeline_id, name) IN (
+ SELECT pipeline_id, name FROM ci_stages
+ GROUP BY pipeline_id, name HAVING COUNT(*) > 1
+ )
SQL
- else # We can't modify a table we are selecting from on MySQL
+
execute <<~SQL
- DELETE a FROM ci_stages AS a, ci_stages AS b
- WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
- AND a.id <> b.id
+ UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
SQL
+
+ if Gitlab::Database.postgresql?
+ execute <<~SQL
+ DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
+ SQL
+ else # We can't modify a table we are selecting from on MySQL
+ execute <<~SQL
+ DELETE a FROM ci_stages AS a, ci_stages AS b
+ WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
+ AND a.id <> b.id
+ SQL
+ end
end
end
end
diff --git a/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb b/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb
index db5165dbe70..aa19732ca1c 100644
--- a/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb
+++ b/db/post_migrate/20180305100050_remove_permanent_from_redirect_routes.rb
@@ -15,10 +15,10 @@ class RemovePermanentFromRedirectRoutes < ActiveRecord::Migration
# ReworkRedirectRoutesIndexes:
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/16211
if Gitlab::Database.postgresql?
- disable_statement_timeout
-
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};"
- execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};"
+ disable_statement_timeout do
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};"
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};"
+ end
end
remove_column(:redirect_routes, :permanent)
@@ -28,10 +28,10 @@ class RemovePermanentFromRedirectRoutes < ActiveRecord::Migration
add_column(:redirect_routes, :permanent, :boolean)
if Gitlab::Database.postgresql?
- disable_statement_timeout
-
- execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
- execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+ disable_statement_timeout do
+ execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
+ execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")
+ end
end
end
end
diff --git a/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb b/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb
index d6fb4f06695..ca9212fae27 100644
--- a/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb
+++ b/db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb
@@ -20,10 +20,10 @@ class AddPathIndexToRedirectRoutes < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
- disable_statement_timeout
-
- unless index_exists_by_name?(:redirect_routes, INDEX_NAME)
- execute("CREATE UNIQUE INDEX CONCURRENTLY #{INDEX_NAME} ON redirect_routes (lower(path) varchar_pattern_ops);")
+ disable_statement_timeout do
+ unless index_exists_by_name?(:redirect_routes, INDEX_NAME)
+ execute("CREATE UNIQUE INDEX CONCURRENTLY #{INDEX_NAME} ON redirect_routes (lower(path) varchar_pattern_ops);")
+ end
end
end
diff --git a/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb b/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb
index e19387bce1e..c32123454f9 100644
--- a/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb
+++ b/db/post_migrate/20180405101928_reschedule_builds_stages_migration.rb
@@ -17,13 +17,13 @@ class RescheduleBuildsStagesMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
-
- Build.where('stage_id IS NULL').tap do |relation|
- queue_background_migration_jobs_by_range_at_intervals(relation,
- MIGRATION,
- 5.minutes,
- batch_size: BATCH_SIZE)
+ disable_statement_timeout do
+ Build.where('stage_id IS NULL').tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
end
end
diff --git a/db/post_migrate/20180420080616_schedule_stages_index_migration.rb b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
index 1d0daad002f..eb82f098639 100644
--- a/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
+++ b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
@@ -13,13 +13,13 @@ class ScheduleStagesIndexMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
-
- Stage.all.tap do |relation|
- queue_background_migration_jobs_by_range_at_intervals(relation,
- MIGRATION,
- 5.minutes,
- batch_size: BATCH_SIZE)
+ disable_statement_timeout do
+ Stage.all.tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
end
end
diff --git a/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb b/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb
index 73c23dffca0..5418f442e79 100644
--- a/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb
+++ b/db/post_migrate/20180604123514_cleanup_stages_position_migration.rb
@@ -12,32 +12,34 @@ class CleanupStagesPositionMigration < ActiveRecord::Migration
end
def up
- disable_statement_timeout
+ disable_statement_timeout do
+ Gitlab::BackgroundMigration.steal('MigrateStageIndex')
- Gitlab::BackgroundMigration.steal('MigrateStageIndex')
-
- unless index_exists_by_name?(:ci_stages, TMP_INDEX_NAME)
- add_concurrent_index(:ci_stages, :id, where: 'position IS NULL', name: TMP_INDEX_NAME)
- end
+ unless index_exists_by_name?(:ci_stages, TMP_INDEX_NAME)
+ add_concurrent_index(:ci_stages, :id, where: 'position IS NULL', name: TMP_INDEX_NAME)
+ end
- migratable = <<~SQL
- position IS NULL AND EXISTS (
- SELECT 1 FROM ci_builds WHERE stage_id = ci_stages.id AND stage_idx IS NOT NULL
- )
- SQL
+ migratable = <<~SQL
+ position IS NULL AND EXISTS (
+ SELECT 1 FROM ci_builds WHERE stage_id = ci_stages.id AND stage_idx IS NOT NULL
+ )
+ SQL
- Stages.where(migratable).each_batch(of: 1000) do |batch|
- batch.pluck(:id).each do |stage|
- Gitlab::BackgroundMigration::MigrateStageIndex.new.perform(stage, stage)
+ Stages.where(migratable).each_batch(of: 1000) do |batch|
+ batch.pluck(:id).each do |stage|
+ Gitlab::BackgroundMigration::MigrateStageIndex.new.perform(stage, stage)
+ end
end
- end
- remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ end
end
def down
if index_exists_by_name?(:ci_stages, TMP_INDEX_NAME)
- remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ disable_statement_timeout do
+ remove_concurrent_index_by_name(:ci_stages, TMP_INDEX_NAME)
+ end
end
end
end
diff --git a/doc/api/README.md b/doc/api/README.md
index 4566319ad45..45e926d3b6b 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -401,14 +401,13 @@ GET /api/v4/projects/1/branches/my%2Fbranch/commits
## Encoding API parameters of `array` and `hash` types
-When making an API call with parameters of type `array` and/or `hash`, the parameters may be
-specified as shown below.
+We can call the API with `array` and `hash` types parameters as shown below:
### `array`
`import_sources` is a parameter of type `array`:
-```
+```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
-d "import_sources[]=github" \
-d "import_sources[]=bitbucket" \
@@ -419,7 +418,7 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
`override_params` is a parameter of type `hash`:
-```
+```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
--form "namespace=email" \
--form "path=impapi" \
@@ -429,6 +428,20 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
https://gitlab.example.com/api/v4/projects/import
```
+### Array of hashes
+
+`variables` is a parameter of type `array` containing hash key/value pairs `[{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }]`:
+
+```bash
+curl --globoff --request POST --header "PRIVATE-TOKEN: ********************" \
+"https://gitlab.example.com/api/v4/projects/169/pipeline?ref=master&variables[][key]=VAR1&variables[][value]=hello&variables[][key]=VAR2&variables[][value]=world"
+
+curl --request POST --header "PRIVATE-TOKEN: ********************" \
+--header "Content-Type: application/json" \
+--data '{ "ref": "master", "variables": [ {"key": "VAR1", "value": "hello"}, {"key": "VAR2", "value": "world"} ] }' \
+"https://gitlab.example.com/api/v4/projects/169/pipeline"
+```
+
## `id` vs `iid`
When you work with the API, you may notice two similar fields in API entities:
diff --git a/doc/api/boards.md b/doc/api/boards.md
index 246de50323e..5f006f4f012 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -144,7 +144,7 @@ Example response:
## List board lists
Get a list of the board's lists.
-Does not include `backlog` and `closed` lists
+Does not include `open` and `closed` lists
```
GET /projects/:id/boards/:board_id/lists
diff --git a/doc/api/events.md b/doc/api/events.md
index f4d26c4de1c..1b6c4d437dd 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -255,7 +255,7 @@ Example response:
Get a list of visible events for a particular project.
```
-GET /:project_id/events
+GET /projects/:project_id/events
```
Parameters:
diff --git a/doc/api/group_boards.md b/doc/api/group_boards.md
index 45a8544d6b1..373904e50c4 100644
--- a/doc/api/group_boards.md
+++ b/doc/api/group_boards.md
@@ -119,7 +119,7 @@ Example response:
## List board lists
Get a list of the board's lists.
-Does not include `backlog` and `closed` lists
+Does not include `open` and `closed` lists
```
GET /groups/:id/boards/:board_id/lists
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 9a950097675..4bf65a8fafd 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -33,7 +33,6 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.727Z",
- "artifacts_file": null,
"finished_at": "2015-12-24T17:54:24.921Z",
"artifacts_expire_at": "2016-01-23T17:54:24.921Z",
"id": 6,
@@ -45,6 +44,7 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": "2015-12-24T17:54:24.729Z",
@@ -82,6 +82,12 @@ Example of response
"filename": "artifacts.zip",
"size": 1000
},
+ "artifacts": [
+ {"file_type": "archive", "size": 1000, "filename": "artifacts.zip", "file_format": "zip"},
+ {"file_type": "metadata", "size": 186, "filename": "metadata.gz", "file_format": "gzip"},
+ {"file_type": "trace", "size": 1500, "filename": "job.log", "file_format": "raw"},
+ {"file_type": "junit", "size": 750, "filename": "junit.xml.gz", "file_format": "gzip"}
+ ],
"finished_at": "2015-12-24T17:54:27.895Z",
"artifacts_expire_at": "2016-01-23T17:54:27.895Z",
"id": 7,
@@ -93,6 +99,7 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": "2015-12-24T17:54:27.722Z",
@@ -151,7 +158,6 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.727Z",
- "artifacts_file": null,
"finished_at": "2015-12-24T17:54:24.921Z",
"artifacts_expire_at": "2016-01-23T17:54:24.921Z",
"id": 6,
@@ -163,6 +169,7 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": "2015-12-24T17:54:24.729Z",
@@ -200,6 +207,12 @@ Example of response
"filename": "artifacts.zip",
"size": 1000
},
+ "artifacts": [
+ {"file_type": "archive", "size": 1000, "filename": "artifacts.zip", "file_format": "zip"},
+ {"file_type": "metadata", "size": 186, "filename": "metadata.gz", "file_format": "gzip"},
+ {"file_type": "trace", "size": 1500, "filename": "job.log", "file_format": "raw"},
+ {"file_type": "junit", "size": 750, "filename": "junit.xml.gz", "file_format": "gzip"}
+ ],
"finished_at": "2015-12-24T17:54:27.895Z",
"artifacts_expire_at": "2016-01-23T17:54:27.895Z",
"id": 7,
@@ -211,6 +224,7 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": "2015-12-24T17:54:27.722Z",
@@ -267,7 +281,6 @@ Example of response
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.880Z",
- "artifacts_file": null,
"finished_at": "2015-12-24T17:54:31.198Z",
"artifacts_expire_at": "2016-01-23T17:54:31.198Z",
"id": 8,
@@ -279,6 +292,7 @@ Example of response
"status": "pending"
},
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": "2015-12-24T17:54:30.733Z",
@@ -458,11 +472,11 @@ Example of response
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
- "artifacts_file": null,
"finished_at": "2016-01-11T10:14:09.526Z",
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": null,
@@ -505,11 +519,11 @@ Example of response
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
- "artifacts_file": null,
"finished_at": null,
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": null,
@@ -559,6 +573,7 @@ Example of response
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"created_at": "2016-01-11T10:13:33.506Z",
@@ -610,6 +625,7 @@ Example response:
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"created_at": "2016-01-11T10:13:33.506Z",
@@ -654,11 +670,11 @@ Example of response
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
- "artifacts_file": null,
"finished_at": null,
"id": 42,
"name": "rubocop",
"ref": "master",
+ "artifacts": [],
"runner": null,
"stage": "test",
"started_at": null,
diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md
index 682b90361bd..165b9a11c7a 100644
--- a/doc/api/notification_settings.md
+++ b/doc/api/notification_settings.md
@@ -15,7 +15,7 @@ mention
custom
```
-If the `custom` level is used, specific email events can be controlled. Notification email events are defined in the `NotificationSetting::EMAIL_EVENTS` model variable. Currently, these events are recognized:
+If the `custom` level is used, specific email events can be controlled. Available events are returned by `NotificationSetting.email_events`. Currently, these events are recognized:
```
new_note
diff --git a/doc/api/projects.md b/doc/api/projects.md
index f360b49c293..bda4164ee92 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -258,8 +258,7 @@ GET /projects?custom_attributes[key]=value&custom_attributes[other_key]=other_va
## List user projects
-Get a list of visible projects for the given user. When accessed without
-authentication, only public projects are returned.
+Get a list of visible projects owned by the given user. When accessed without authentication, only public projects are returned.
```
GET /users/:user_id/projects
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index cb816bbd712..a4fdeca162e 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -204,3 +204,39 @@ Response:
"deletions": 244
}]
```
+
+## Merge Base
+
+Get the common ancestor for 2 refs (commit SHAs, branch names or tags).
+
+```
+GET /projects/:id/repository/merge_base
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `refs` | array | yes | The refs to find the common ancestor of, for now only 2 refs are supported |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/merge_base?refs[]=304d257dcb821665ab5110318fc58a007bd104ed&refs[]=0031876facac3f2b2702a0e53a26e89939a42209"
+```
+
+Example response:
+
+```json
+{
+ "id": "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863",
+ "short_id": "1a0b36b3",
+ "title": "Initial commit",
+ "created_at": "2014-02-27T08:03:18.000Z",
+ "parent_ids": [],
+ "message": "Initial commit\n",
+ "author_name": "Dmitriy Zaporozhets",
+ "author_email": "dmitriy.zaporozhets@gmail.com",
+ "authored_date": "2014-02-27T08:03:18.000Z",
+ "committer_name": "Dmitriy Zaporozhets",
+ "committer_email": "dmitriy.zaporozhets@gmail.com",
+ "committed_date": "2014-02-27T08:03:18.000Z"
+}
+```
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index dd424470b67..7b8db6cfa8f 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -34,6 +34,7 @@ Example response:
"push_events":true,
"tag_push_events":false,
"merge_requests_events": true,
+ "repository_update_events": true,
"enable_ssl_verification":true
}
]
@@ -56,6 +57,7 @@ POST /hooks
| `push_events` | boolean | no | When true, the hook will fire on push events |
| `tag_push_events` | boolean | no | When true, the hook will fire on new tags being pushed |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
+| `repository_update_events` | boolean | no | Trigger hook on repository update events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
Example request:
@@ -75,6 +77,7 @@ Example response:
"push_events":true,
"tag_push_events":false,
"merge_requests_events": true,
+ "repository_update_events": true,
"enable_ssl_verification":true
}
]
@@ -127,4 +130,4 @@ Example request:
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/hooks/2
-```
+``` \ No newline at end of file
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
new file mode 100644
index 00000000000..57ae318e821
--- /dev/null
+++ b/doc/development/contributing/design.md
@@ -0,0 +1,63 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Implement design & UI elements](#implement-design--ui-elements)
+- [Style guides](#style-guides)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Implement design & UI elements
+
+For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).
+
+The UX team uses labels to manage their workflow.
+
+The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
+To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
+
+Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
+
+There is a special type label called ~"product discovery". It represents a discovery issue intended for UX, PM, FE, and BE to discuss the problem and potential solutions. The final output for this issue could be a doc of requirements, a design artifact, or even a prototype. The solution will be developed in a subsequent milestone.
+
+~"product discovery" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
+
+The initial issue should be about the problem we are solving. If a separate [product discovery issue](#product-discovery-issues) is needed for additional research and design work, it will be created by a PM or UX person. Assign the ~UX, ~"product discovery" and ~"Deliverable" labels, add a milestone and use a title that makes it clear that the scheduled issue is product discovery
+(e.g. `Product discovery for XYZ`).
+
+When the ~"product discovery" issue has been completed, the UXer removes the ~UX
+label, adds the ~"UX ready" label and closes the issue. This indicates the
+UX work for the issue is complete. The UXer will also copy any designs to related
+issues for implementation in an upcoming milestone.
+
+## Style guides
+
+1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
+ Important sections include [Source Code Layout][rss-source] and
+ [Naming][rss-naming]. Use:
+ - multi-line method chaining style **Option A**: dot `.` on the second line
+ - string literal quoting style **Option A**: single quoted by default
+1. [Rails](https://github.com/bbatsov/rails-style-guide)
+1. [Newlines styleguide][newlines-styleguide]
+1. [Testing][testing]
+1. [JavaScript styleguide][js-styleguide]
+1. [SCSS styleguide][scss-styleguide]
+1. [Shell commands](../shell_commands.md) created by GitLab
+ contributors to enhance security
+1. [Database Migrations](../migration_style_guide.md)
+1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
+1. [Documentation styleguide](https://docs.gitlab.com/ee/development/documentation/styleguide.html)
+1. Interface text should be written subjectively instead of objectively. It
+ should be the GitLab core team addressing a person. It should be written in
+ present time and never use past tense (has been/was). For example instead
+ of _prohibited this user from being saved due to the following errors:_ the
+ text should be _sorry, we could not create your account because:_
+1. Code should be written in [US English][us-english]
+
+This is also the style used by linting tools such as
+[RuboCop](https://github.com/bbatsov/rubocop),
+[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
new file mode 100644
index 00000000000..64f5a2c8022
--- /dev/null
+++ b/doc/development/contributing/index.md
@@ -0,0 +1,246 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Contribute to GitLab](#contribute-to-gitlab)
+- [Security vulnerability disclosure](#security-vulnerability-disclosure)
+- [Code of conduct](#code-of-conduct)
+- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
+- [Helping others](#helping-others)
+- [I want to contribute!](#i-want-to-contribute)
+- [Contribution Flow](#contribution-flow)
+- [Workflow labels](#workflow-labels)
+ - [Type labels](#type-labels)
+ - [Subject labels](#subject-labels)
+ - [Team labels](#team-labels)
+ - [Milestone labels](#milestone-labels)
+ - [Bug Priority labels](#bug-priority-labels)
+ - [Bug Severity labels](#bug-severity-labels)
+ - [Severity impact guidance](#severity-impact-guidance)
+ - [Label for community contributors](#label-for-community-contributors)
+- [Implement design & UI elements](#implement-design--ui-elements)
+- [Issue tracker](#issue-tracker)
+ - [Issue triaging](#issue-triaging)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical and UX debt](#technical-and-ux-debt)
+ - [Stewardship](#stewardship)
+- [Merge requests](#merge-requests)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+- [Definition of done](#definition-of-done)
+- [Style guides](#style-guides)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Contribute to GitLab
+
+For a first-time step-by-step guide to the contribution process, see
+["Contributing to GitLab"](https://about.gitlab.com/contributing/).
+
+Thank you for your interest in contributing to GitLab. This guide details how
+to contribute to GitLab in a way that is efficient for everyone.
+
+Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute).
+
+GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
+source edition, and GitLab Enterprise Edition (EE) which is our commercial
+edition. Throughout this guide you will see references to CE and EE for
+abbreviation.
+
+If you have read this guide and want to know how the GitLab [core team]
+operates please see [the GitLab contributing process](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md).
+
+- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
+
+## Security vulnerability disclosure
+
+Please report suspected security vulnerabilities in private to
+`support@gitlab.com`, also see the
+[disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/).
+Please do **NOT** create publicly viewable issues for suspected security
+vulnerabilities.
+
+## Code of conduct
+
+As contributors and maintainers of this project, we pledge to respect all
+people who contribute through reporting issues, posting feature requests,
+updating documentation, submitting pull requests or patches, and other
+activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, or religion.
+
+Examples of unacceptable behavior by participants include the use of sexual
+language or imagery, derogatory comments or personal attacks, trolling, public
+or private harassment, insults, or other unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct. Project maintainers who do not
+follow the Code of Conduct may be removed from the project team.
+
+This code of conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior can be
+reported by emailing `contact@gitlab.com`.
+
+This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
+available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+
+## Closing policy for issues and merge requests
+
+GitLab is a popular open source project and the capacity to deal with issues
+and merge requests is limited. Out of respect for our volunteers, issues and
+merge requests not in line with the guidelines listed in this document may be
+closed without notice.
+
+Please treat our volunteers with courtesy and respect, it will go a long way
+towards getting your issue resolved.
+
+Issues and merge requests should be in English and contain appropriate language
+for audiences of all ages.
+
+If a contributor is no longer actively working on a submitted merge request
+we can decide that the merge request will be finished by one of our
+[Merge request coaches][team] or close the merge request. We make this decision
+based on how important the change is for our product vision. If a Merge request
+coach is going to finish the merge request we assign the
+~"coach will finish" label.
+
+## Helping others
+
+Please help other GitLab users when you can.
+The methods people will use to seek help can be found on the [getting help page][getting-help].
+
+Sign up for the mailing list, answer GitLab questions on StackOverflow or
+respond in the IRC channel. You can also sign up on [CodeTriage][codetriage] to help with
+the remaining issues on the GitHub issue tracker.
+
+## I want to contribute!
+
+If you want to contribute to GitLab [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight]
+is a great place to start. Issues with a lower weight (1 or 2) are deemed
+suitable for beginners. These issues will be of reasonable size and challenge,
+for anyone to start contributing to GitLab. If you have any questions or need help visit [Getting Help](https://about.gitlab.com/getting-help/#discussion) to
+learn how to communicate with GitLab. If you're looking for a Gitter or Slack channel
+please consider we favor
+[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication. Thanks for your contribution!
+
+## Contribution Flow
+
+When contributing to GitLab, your merge request is subject to review by merge request maintainers of a particular specialty.
+
+When you submit code to GitLab, we really want it to get merged, but there will be times when it will not be merged.
+
+When maintainers are reading through a merge request they may request guidance from other maintainers. If merge request maintainers conclude that the code should not be merged, our reasons will be fully disclosed. If it has been decided that the code quality is not up to GitLab’s standards, the merge request maintainer will refer the author to our docs and code style guides, and provide some guidance.
+
+Sometimes style guides will be followed but the code will lack structural integrity, or the maintainer will have reservations about the code’s overall quality. When there is a reservation the maintainer will inform the author and provide some guidance. The author may then choose to update the merge request. Once the merge request has been updated and reassigned to the maintainer, they will review the code again. Once the code has been resubmitted any number of times, the maintainer may choose to close the merge request with a summary of why it will not be merged, as well as some guidance. If the merge request is closed the maintainer will be open to discussion as to how to improve the code so it can be approved in the future.
+
+GitLab will do its best to review community contributions as quickly as possible. Specially appointed developers review community contributions daily. You may take a look at the [team page](https://about.gitlab.com/team/) for the merge request coach who specializes in the type of code you have written and mention them in the merge request. For example, if you have written some JavaScript in your code then you should mention the frontend merge request coach. If your code has multiple disciplines you may mention multiple merge request coaches.
+
+GitLab receives a lot of community contributions, so if your code has not been reviewed within 4 days of its initial submission feel free to re-mention the appropriate merge request coach.
+
+When submitting code to GitLab, you may feel that your contribution requires the aid of an external library. If your code includes an external library please provide a link to the library, as well as reasons for including it.
+
+When your code contains more than 500 changes, any major breaking changes, or an external library, `@mention` a maintainer in the merge request. If you are not sure who to mention, the reviewer will add one early in the merge request process.
+
+## Workflow labels
+
+This [documentation](issue_workflow.md) outlines the current workflow labels.
+
+### Type labels
+
+This [documentation](issue_workflow.md) outlines the current type labels.
+
+### Subject labels
+
+This [documentation](issue_workflow.md) outlines the current subject labels.
+
+### Team labels
+
+This [documentation](issue_workflow.md) outlines the current team labels.
+
+### Milestone labels
+
+This [documentation](issue_workflow.md) outlines the current milestone labels.
+
+### Bug Priority labels
+
+This [documentation](issue_workflow.md) outlines the current bug priority labels.
+
+### Bug Severity labels
+
+This [documentation](issue_workflow.md) outlines the current severity labels.
+
+#### Severity impact guidance
+
+This [documentation](issue_workflow.md) outlines the current severity impact guidance.
+
+### Label for community contributors
+
+This [documentation](issue_workflow.md) outlines the current policy regarding community contributor issues.
+
+## Implement design & UI elements
+
+This [documentation](design.md) outlines the current design and UI guidelines.
+
+## Issue tracker
+
+This [documentation](issue_workflow.md) outlines the issue tracker process.
+
+### Issue triaging
+
+This [documentation](issue_workflow.md) outlines the current issue triaging process.
+
+### Feature proposals
+
+This [documentation](issue_workflow.md) outlines the feature proposal process.
+
+### Issue tracker guidelines
+
+This [documentation](issue_workflow.md) outlines the issue tracker guidelines.
+
+### Issue weight
+
+This [documentation](issue_workflow.md) outlines the issue weight guidelines.
+
+### Regression issues
+
+This [documentation](issue_workflow.md) outlines the regression issue process.
+
+### Technical and UX debt
+
+This [documentation](issue_workflow.md) about technical and UX debt has been moved.
+
+### Stewardship
+
+This [documentation](issue_workflow.md) outlines the stewardship process.
+
+## Merge requests
+
+This [documentation](merge_request_workflow.md) outlines the current merge request process.
+
+### Merge request guidelines
+
+This [documentation](merge_request_workflow.md) outlines the current merge request guidelines.
+
+### Contribution acceptance criteria
+
+This [documentation](merge_request_workflow.md) outlines the current acceptance criteria for contributions.
+
+## Definition of done
+
+This [documentation](merge_request_workflow.md) outlines the definition of done.
+
+## Style guides
+This [documentation](design.md) outlines the current style guidelines.
+
+---
+
+[Return to Development documentation](../README.md)
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
new file mode 100644
index 00000000000..6a334e9b17d
--- /dev/null
+++ b/doc/development/contributing/issue_workflow.md
@@ -0,0 +1,357 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Workflow labels](#workflow-labels)
+ - [Type labels](#type-labels)
+ - [Subject labels](#subject-labels)
+ - [Team labels](#team-labels)
+ - [Release Scoping labels](#release-scoping-labels)
+ - [Priority labels](#priority-labels)
+ - [Severity labels](#severity-labels)
+ - [Severity impact guidance](#severity-impact-guidance)
+ - [Label for community contributors](#label-for-community-contributors)
+ - [Issue triaging](#issue-triaging)
+ - [Feature proposals](#feature-proposals)
+ - [Issue tracker guidelines](#issue-tracker-guidelines)
+ - [Issue weight](#issue-weight)
+ - [Regression issues](#regression-issues)
+ - [Technical and UX debt](#technical-and-ux-debt)
+ - [Stewardship](#stewardship)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Workflow labels
+
+To allow for asynchronous issue handling, we use [milestones][milestones-page]
+and [labels][labels-page]. Leads and product managers handle most of the
+scheduling into milestones. Labelling is a task for everyone.
+
+Most issues will have labels for at least one of the following:
+
+- Type: ~"feature proposal", ~bug, ~customer, etc.
+- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
+- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc.
+- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
+- Priority: ~P1, ~P2, ~P3, ~P4
+- Severity: ~S1, ~S2, ~S3, ~S4
+
+All labels, their meaning and priority are defined on the
+[labels page][labels-page].
+
+If you come across an issue that has none of these, and you're allowed to set
+labels, you can _always_ add the team and type, and often also the subject.
+
+[milestones-page]: https://gitlab.com/gitlab-org/gitlab-ce/milestones
+[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
+
+### Type labels
+
+Type labels are very important. They define what kind of issue this is. Every
+issue should have one or more.
+
+Examples of type labels are ~"feature proposal", ~bug, ~customer, ~security,
+and ~"direction".
+
+A number of type labels have a priority assigned to them, which automatically
+makes them float to the top, depending on their importance.
+
+Type labels are always lowercase, and can have any color, besides blue (which is
+already reserved for subject labels).
+
+The descriptions on the [labels page][labels-page] explain what falls under each type label.
+
+### Subject labels
+
+Subject labels are labels that define what area or feature of GitLab this issue
+hits. They are not always necessary, but very convenient.
+
+Examples of subject labels are ~wiki, ~ldap, ~api,
+~issues, ~"merge requests", ~labels, and ~"container registry".
+
+If you are an expert in a particular area, it makes it easier to find issues to
+work on. You can also subscribe to those labels to receive an email each time an
+issue is labeled with a subject label corresponding to your expertise.
+
+Subject labels are always all-lowercase.
+
+### Team labels
+
+Team labels specify what team is responsible for this issue.
+Assigning a team label makes sure issues get the attention of the appropriate
+people.
+
+The current team labels are:
+
+- ~Configuration
+- ~"CI/CD"
+- ~Create
+- ~Distribution
+- ~Documentation
+- ~Geo
+- ~Gitaly
+- ~Manage
+- ~Monitoring
+- ~Plan
+- ~Quality
+- ~Release
+- ~Secure
+- ~UX
+
+The descriptions on the [labels page][labels-page] explain what falls under the
+responsibility of each team.
+
+Within those team labels, we also have the ~backend and ~frontend labels to
+indicate if an issue needs backend work, frontend work, or both.
+
+Team labels are always capitalized so that they show up as the first label for
+any issue.
+
+### Release Scoping labels
+
+Release Scoping labels help us clearly communicate expectations of the work for the
+release. There are three levels of Release Scoping labels:
+
+- ~Deliverable: Issues that are expected to be delivered in the current
+ milestone.
+- ~Stretch: Issues that are a stretch goal for delivering in the current
+ milestone. If these issues are not done in the current release, they will
+ strongly be considered for the next release.
+- ~"Next Patch Release": Issues to put in the next patch release. Work on these
+ first, and add the "Pick Into X" label to the merge request, along with the
+ appropriate milestone.
+
+Each issue scheduled for the current milestone should be labeled ~Deliverable
+or ~"Stretch". Any open issue for a previous milestone should be labeled
+~"Next Patch Release", or otherwise rescheduled to a different milestone.
+
+### Priority labels
+
+Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
+If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
+This label documents the planned timeline & urgency which is used to measure against our actual SLA on delivering ~bug fixes.
+
+| Label | Meaning | Estimate time to fix |
+|-------|-----------------|------------------------------------------------------------------|
+| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com |
+| ~P2 | High Priority | The next release |
+| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) |
+| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) |
+
+### Severity labels
+
+Severity labels help us clearly communicate the impact of a ~bug on users.
+
+| Label | Meaning | Impact on Functionality | Example |
+|-------|-------------------|-------------------------------------------------------|---------|
+| ~S1 | Blocker | Outage, broken feature with no workaround | Unable to create an issue. Data corruption/loss. Security breach. |
+| ~S2 | Critical Severity | Broken Feature, workaround too complex & unacceptable | Can push commits, but only via the command line. |
+| ~S3 | Major Severity | Broken Feature, workaround acceptable | Can create merge requests only from the Merge Requests page, not through the Issue. |
+| ~S4 | Low Severity | Functionality inconvenience or cosmetic issue | Label colors are incorrect / not being displayed. |
+
+#### Severity impact guidance
+
+Severity levels can be applied further depending on the facet of the impact; e.g. Affected customers, GitLab.com availability, performance and etc. The below is a guideline.
+
+| Severity | Affected Customers/Users | GitLab.com Availability | Performance Degradation |
+|----------|---------------------------------------------------------------------|----------------------------------------------------|------------------------------|
+| ~S1 | >50% users affected (possible company extinction level event) | Significant impact on all of GitLab.com | |
+| ~S2 | Many users or multiple paid customers affected (but not apocalyptic)| Significant impact on large portions of GitLab.com | Degradation is guaranteed to occur in the near future |
+| ~S3 | A few users or a single paid customer affected | Limited impact on important portions of GitLab.com | Degradation is likely to occur in the near future |
+| ~S4 | No paid users/customer affected, or expected to in the near future | Minor impact on on GitLab.com | Degradation _may_ occur but it's not likely |
+
+### Label for community contributors
+
+Issues that are beneficial to our users, 'nice to haves', that we currently do
+not have the capacity for or want to give the priority to, are labeled as
+~"Accepting Merge Requests", so the community can make a contribution.
+
+Community contributors can submit merge requests for any issue they want, but
+the ~"Accepting Merge Requests" label has a special meaning. It points to
+changes that:
+
+1. We already agreed on,
+1. Are well-defined,
+1. Are likely to get accepted by a maintainer.
+
+We want to avoid a situation when a contributor picks an
+~"Accepting Merge Requests" issue and then their merge request gets closed,
+because we realize that it does not fit our vision, or we want to solve it in a
+different way.
+
+We add the ~"Accepting Merge Requests" label to:
+
+- Low priority ~bug issues (i.e. we do not add it to the bugs that we want to
+solve in the ~"Next Patch Release")
+- Small ~"feature proposal"
+- Small ~"technical debt" issues
+
+After adding the ~"Accepting Merge Requests" label, we try to estimate the
+[weight](#issue-weight) of the issue. We use issue weight to let contributors
+know how difficult the issue is. Additionally:
+
+- We advertise ["Accepting Merge Requests" issues with weight < 5][up-for-grabs]
+ as suitable for people that have never contributed to GitLab before on the
+ [Up For Grabs campaign](http://up-for-grabs.net)
+- We encourage people that have never contributed to any open source project to
+ look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
+
+If you've decided that you would like to work on an issue, please @-mention
+the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
+as soon as possible. The product manager will then pull in appropriate GitLab team
+members to further discuss scope, design, and technical considerations. This will
+ensure that that your contribution is aligned with the GitLab product and minimize
+any rework and delay in getting it merged into master.
+
+GitLab team members who apply the ~"Accepting Merge Requests" label to an issue
+should update the issue description with a responsible product manager, inviting
+any potential community contributor to @-mention per above.
+
+[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
+[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
+
+
+### Issue triaging
+
+Our issue triage policies are [described in our handbook]. You are very welcome
+to help the GitLab team triage issues. We also organize [issue bash events] once
+every quarter.
+
+The most important thing is making sure valid issues receive feedback from the
+development team. Therefore the priority is mentioning developers that can help
+on those issues. Please select someone with relevant experience from the
+[GitLab team][team]. If there is nobody mentioned with that expertise look in
+the commit history for the affected files to find someone.
+
+We also use [GitLab Triage] to automate some triaging policies. This is
+currently setup as a [scheduled pipeline] running on [quality/triage-ops]
+project.
+
+[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
+[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
+[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
+[scheduled pipeline]: https://gitlab.com/gitlab-org/quality/triage-ops/pipeline_schedules/10512/edit
+[quality/triage-ops]: https://gitlab.com/gitlab-org/quality/triage-ops
+
+### Feature proposals
+
+To create a feature proposal for CE, open an issue on the
+[issue tracker of CE][ce-tracker].
+
+For feature proposals for EE, open an issue on the
+[issue tracker of EE][ee-tracker].
+
+In order to help track the feature proposals, we have created a
+[`feature proposal`][fpl] label. For the time being, users that are not members
+of the project cannot add labels. You can instead ask one of the [core team]
+members to add the label ~"feature proposal" to the issue or add the following
+code snippet right after your description in a new line: `~"feature proposal"`.
+
+Please keep feature proposals as small and simple as possible, complex ones
+might be edited to make them small and simple.
+
+Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
+
+For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
+be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
+need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
+
+If you want to create something yourself, consider opening an issue first to
+discuss whether it is interesting to include this in GitLab.
+
+### Issue tracker guidelines
+
+**[Search the issue tracker][ce-tracker]** for similar entries before
+submitting your own, there's a good chance somebody else had the same issue or
+feature proposal. Show your support with an award emoji and/or join the
+discussion.
+
+Please submit bugs using the ['Bug' issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Bug.md) provided on the issue tracker.
+The text in the parenthesis is there to help you with what to include. Omit it
+when submitting the actual issue. You can copy-paste it and then edit as you
+see fit.
+
+### Issue weight
+
+Issue weight allows us to get an idea of the amount of work required to solve
+one or multiple issues. This makes it possible to schedule work more accurately.
+
+You are encouraged to set the weight of any issue. Following the guidelines
+below will make it easy to manage this, without unnecessary overhead.
+
+1. Set weight for any issue at the earliest possible convenience
+1. If you don't agree with a set weight, discuss with other developers until
+consensus is reached about the weight
+1. Issue weights are an abstract measurement of complexity of the issue. Do not
+relate issue weight directly to time. This is called [anchoring](https://en.wikipedia.org/wiki/Anchoring)
+and something you want to avoid.
+1. Something that has a weight of 1 (or no weight) is really small and simple.
+Something that is 9 is rewriting a large fundamental part of GitLab,
+which might lead to many hard problems to solve. Changing some text in GitLab
+is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
+1. If something is very large, it should probably be split up in multiple
+issues or chunks. You can simply not set the weight of a parent issue and set
+weights to children issues.
+
+### Regression issues
+
+Every monthly release has a corresponding issue on the CE issue tracker to keep
+track of functionality broken by that release and any fixes that need to be
+included in a patch release (see [8.3 Regressions] as an example).
+
+As outlined in the issue description, the intended workflow is to post one note
+with a reference to an issue describing the regression, and then to update that
+note with a reference to the merge request that fixes it as it becomes available.
+
+If you're a contributor who doesn't have the required permissions to update
+other users' notes, please post a new note with a reference to both the issue
+and the merge request.
+
+The release manager will [update the notes] in the regression issue as fixes are
+addressed.
+
+[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
+[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
+
+### Technical and UX debt
+
+In order to track things that can be improved in GitLab's codebase,
+we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
+For user experience improvements, we use the ~"UX debt" label.
+
+These labels should be added to issues that describe things that can be improved,
+shortcuts that have been taken, features that need additional attention, and all
+other things that have been left behind due to high velocity of development.
+For example, code that needs refactoring should use the ~"technical debt" label,
+user experience refinements should use the ~"UX debt" label.
+
+Everyone can create an issue, though you may need to ask for adding a specific
+label, if you do not have permissions to do it by yourself. Additional labels
+can be combined with these labels, to make it easier to schedule
+the improvements for a release.
+
+Issues tagged with these labels have the same priority like issues
+that describe a new feature to be introduced in GitLab, and should be scheduled
+for a release by the appropriate person.
+
+Make sure to mention the merge request that the ~"technical debt" issue or
+~"UX debt" issue is associated with in the description of the issue.
+
+### Stewardship
+
+For issues related to the open source stewardship of GitLab,
+there is the ~"stewardship" label.
+
+This label is to be used for issues in which the stewardship of GitLab
+is a topic of discussion. For instance if GitLab Inc. is planning to add
+features from GitLab EE to GitLab CE, related issues would be labelled with
+~"stewardship".
+
+A recent example of this was the issue for
+[bringing the time tracking API to GitLab CE][time-tracking-issue].
+
+[time-tracking-issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/25517#note_20019084
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
new file mode 100644
index 00000000000..9b1da4e7bc1
--- /dev/null
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -0,0 +1,191 @@
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Merge requests](#merge-requests)
+ - [Merge request guidelines](#merge-request-guidelines)
+ - [Contribution acceptance criteria](#contribution-acceptance-criteria)
+- [Definition of done](#definition-of-done)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Merge requests
+
+We welcome merge requests with fixes and improvements to GitLab code, tests,
+and/or documentation. The issues that are specifically suitable for
+community contributions are listed with the label
+[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
+and [EE][accepting-mrs-ee], but you are free to contribute to any other issue
+you want.
+
+Please note that if an issue is marked for the current milestone either before
+or while you are working on it, a team member may take over the merge request
+in order to ensure the work is finished before the release date.
+
+If you want to add a new feature that is not labeled it is best to first create
+a feedback issue (if there isn't one already) and leave a comment asking for it
+to be marked as `Accepting Merge Requests`. Please include screenshots or
+wireframes if the feature will also change the UI.
+
+Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
+
+If you are new to GitLab development (or web development in general), see the
+[I want to contribute!](#i-want-to-contribute) section to get you started with
+some potentially easy issues.
+
+To start with GitLab development download the [GitLab Development Kit][gdk] and
+see the [Development section](../README.md) for some guidelines.
+
+### Merge request guidelines
+
+If you can, please submit a merge request with the fix or improvements
+including tests. If you don't know how to fix the issue but can write a test
+that exposes the issue we will accept that as well. In general bug fixes that
+include a regression test are merged quickly while new features without proper
+tests are least likely to receive timely feedback. The workflow to make a merge
+request is as follows:
+
+1. Fork the project into your personal space on GitLab.com
+1. Create a feature branch, branch away from `master`
+1. Write [tests](https://docs.gitlab.com/ee/development/rake_tasks.html#run-tests) and code
+1. [Generate a changelog entry with `bin/changelog`][changelog]
+1. If you are writing documentation, make sure to follow the
+ [documentation guidelines][doc-guidelines]
+1. If you have multiple commits please combine them into a few logically
+ organized commits by [squashing them][git-squash]
+1. Push the commit(s) to your fork
+1. Submit a merge request (MR) to the `master` branch
+ 1. Your merge request needs at least 1 approval but feel free to require more.
+ For instance if you're touching backend and frontend code, it's a good idea
+ to require 2 approvals: 1 from a backend maintainer and 1 from a frontend
+ maintainer
+ 1. You don't have to select any approvers, but you can if you really want
+ specific people to approve your merge request
+1. The MR title should describe the change you want to make
+1. The MR description should give a motive for your change and the method you
+ used to achieve it.
+ 1. If you are contributing code, fill in the template already provided in the
+ "Description" field.
+ 1. If you are contributing documentation, choose `Documentation` from the
+ "Choose a template" menu and fill in the template.
+ 1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
+ `Closes #XXX` syntax to auto-close the issue(s) once the merge request will
+ be merged.
+1. If you're allowed to, set a relevant milestone and labels
+1. If the MR changes the UI it should include *Before* and *After* screenshots
+1. If the MR changes CSS classes please include the list of affected pages,
+ `grep css-class ./app -R`
+1. Be prepared to answer questions and incorporate feedback even if requests
+ for this arrive weeks or months after your MR submission
+ 1. If a discussion has been addressed, select the "Resolve discussion" button
+ beneath it to mark it resolved.
+1. If your MR touches code that executes shell commands, reads or opens files or
+ handles paths to files on disk, make sure it adheres to the
+ [shell command guidelines](../shell_commands.md)
+1. If your code creates new files on disk please read the
+ [shared files guidelines](../shared_files.md).
+1. When writing commit messages please follow
+ [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
+ [guidelines](http://chris.beams.io/posts/git-commit/).
+1. If your merge request adds one or more migrations, make sure to execute all
+ migrations on a fresh database before the MR is reviewed. If the review leads
+ to large changes in the MR, do this again once the review is complete.
+1. For more complex migrations, write tests.
+1. Merge requests **must** adhere to the [merge request performance
+ guidelines](../merge_request_performance_guidelines.md).
+1. For tests that use Capybara or PhantomJS, see this [article on how
+ to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
+
+Please keep the change in a single MR **as small as possible**. If you want to
+contribute a large feature think very hard what the minimum viable change is.
+Can you split the functionality? Can you only submit the backend/API code? Can
+you start with a very simple UI? Can you do part of the refactor? The increased
+reviewability of small MRs that leads to higher code quality is more important
+to us than having a minimal commit log. The smaller an MR is the more likely it
+is it will be merged (quickly). After that you can send more MRs to enhance it.
+The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this.
+
+For examples of feedback on merge requests please look at already
+[closed merge requests][closed-merge-requests]. If you would like quick feedback
+on your merge request feel free to mention someone from the [core team] or one
+of the [Merge request coaches][team].
+Please ensure that your merge request meets the contribution acceptance criteria.
+
+When having your code reviewed and when reviewing merge requests please take the
+[code review guidelines](../code_review.md) into account.
+
+### Contribution acceptance criteria
+
+1. The change is as small as possible
+1. Include proper tests and make all tests pass (unless it contains a test
+ exposing a bug in existing code). Every new class should have corresponding
+ unit tests, even if the class is exercised at a higher level, such as a feature test.
+1. If you suspect a failing CI build is unrelated to your contribution, you may
+ try and restart the failing CI job or ask a developer to fix the
+ aforementioned failing test
+1. Your MR initially contains a single commit (please use `git rebase -i` to
+ squash commits)
+1. Your changes can merge without problems (if not please rebase if you're the
+ only one working on your feature branch, otherwise, merge `master`)
+1. Does not break any existing functionality
+1. Fixes one specific issue or implements one specific feature (do not combine
+ things, send separate merge requests if needed)
+1. Migrations should do only one thing (e.g., either create a table, move data
+ to a new table or remove an old table) to aid retrying on failure
+1. Keeps the GitLab code base clean and well structured
+1. Contains functionality we think other users will benefit from too
+1. Doesn't add configuration options or settings options since they complicate
+ making and testing future changes
+1. Changes do not adversely degrade performance.
+ - Avoid repeated polling of endpoints that require a significant amount of overhead
+ - Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/mer ge_request_performance_guidelines.html)
+ - Avoid repeated access of filesystem
+1. If you need polling to support real-time features, please use
+ [polling with ETag caching][polling-etag].
+1. Changes after submitting the merge request should be in separate commits
+ (no squashing).
+1. It conforms to the [style guides](#style-guides) and the following:
+ - If your change touches a line that does not follow the style, modify the
+ entire line to follow it. This prevents linting tools from generating warnings.
+ - Don't touch neighbouring lines. As an exception, automatic mass
+ refactoring modifications may leave style non-compliant.
+1. If the merge request adds any new libraries (gems, JavaScript libraries,
+ etc.), they should conform to our [Licensing guidelines][license-finder-doc].
+ See the instructions in that document for help if your MR fails the
+ "license-finder" test with a "Dependencies that need approval" error.
+1. The merge request meets the [definition of done](#definition-of-done).
+
+## Definition of done
+
+If you contribute to GitLab please know that changes involve more than just
+code. We have the following [definition of done][definition-of-done]. Please ensure you support
+the feature you contribute through all of these steps.
+
+1. Description explaining the relevancy (see following item)
+1. Working and clean code that is commented where needed
+1. [Unit, integration, and system tests][testing] that pass on the CI server
+1. Performance/scalability implications have been considered, addressed, and tested
+1. [Documented][doc-guidelines] in the `/doc` directory
+1. [Changelog entry added][changelog], if necessary
+1. Reviewed and any concerns are addressed
+1. Merged by a project maintainer
+1. Added to the release blog article, if relevant
+1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/), if relevant
+1. Community questions answered
+1. Answers to questions radiated (in docs/wiki/support etc.)
+
+If you add a dependency in GitLab (such as an operating system package) please
+consider updating the following and note the applicability of each in your
+merge request:
+
+1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
+1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
+1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
+1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
+1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
+1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh
+1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
+
+---
+
+[Return to Contributing documentation](index.md)
diff --git a/doc/development/diffs.md b/doc/development/diffs.md
index 55fc16e0b33..d06339480b1 100644
--- a/doc/development/diffs.md
+++ b/doc/development/diffs.md
@@ -1,6 +1,6 @@
-# Working with Merge Request diffs
+# Working with diffs
-Currently we rely on different sources to present merge request diffs, these include:
+Currently we rely on different sources to present diffs, these include:
- Rugged gem
- Gitaly service
@@ -11,6 +11,8 @@ We're constantly moving Rugged calls to Gitaly and the progress can be followed
## Architecture overview
+### Merge request diffs
+
When refreshing a Merge Request (pushing to a source branch, force-pushing to target branch, or if the target branch now contains any commits from the MR)
we fetch the comparison information using `Gitlab::Git::Compare`, which fetches `base` and `head` data using Gitaly and diff between them through
`Gitlab::Git::Diff.between` (which uses _Gitaly_ if it's enabled, otherwise _Rugged_).
@@ -32,6 +34,17 @@ In order to present diffs information on the Merge Request diffs page, we:
3. If the diff file is cacheable (text-based), it's cached on Redis
using `Gitlab::Diff::FileCollection::MergeRequestDiff`
+### Note diffs
+
+When commenting on a diff (any comparison), we persist a truncated diff version
+on `NoteDiffFile` (which is associated with the actual `DiffNote`). So instead
+of hitting the repository every time we need the diff of the file, we:
+
+1. Check whether we have the `NoteDiffFile#diff` persisted and use it
+2. Otherwise, if it's a current MR revision, use the persisted
+`MergeRequestDiffFile#diff`
+3. In the last scenario, go the the repository and fetch the diff
+
## Diff limits
As explained above, we limit single diff files and the size of the whole diff. There are scenarios where we collapse the diff file,
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index a211effdfa7..6f31e5b82e5 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -182,6 +182,34 @@ class MyMigration < ActiveRecord::Migration
end
```
+## Adding foreign-key constraints
+
+When adding a foreign-key constraint to either an existing or new
+column remember to also add a index on the column.
+
+This is _required_ if the foreign-key constraint specifies
+`ON DELETE CASCADE` or `ON DELETE SET NULL` behavior. On a cascading
+delete, the [corresponding record needs to be retrieved using an
+index](https://www.cybertec-postgresql.com/en/postgresql-indexes-and-foreign-keys/)
+(otherwise, we'd need to scan the whole table) for subsequent update or
+deletion.
+
+Here's an example where we add a new column with a foreign key
+constraint. Note it includes `index: true` to create an index for it.
+
+```ruby
+class Migration < ActiveRecord::Migration
+
+ def change
+ add_reference :model, :other_model, index: true, foreign_key: { on_delete: :cascade }
+ end
+end
+```
+
+When adding a foreign-key constraint to an existing column, we
+have to employ `add_concurrent_foreign_key` and `add_concurrent_index`
+instead of `add_reference`.
+
## Adding Columns With Default Values
When adding columns with default values you must use the method
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index e1e13474b75..53dfe6774e9 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -133,3 +133,27 @@ afterEach(() => {
vm.$destroy();
});
```
+## Testing with older browsers
+
+Some regressions only affect a specific browser version. We can install and test in particular browsers with either Firefox or Browserstack using the following steps:
+
+
+### Browserstack
+
+[Browserstack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
+You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access.
+You can find the credentials on 1Password, under `frontendteam@gitlab.com`.
+
+### Firefox
+
+#### macOS
+You can download any older version of Firefox from the releases FTP server, https://ftp.mozilla.org/pub/firefox/releases/
+
+1. From the website, select a version, in this case `50.0.1`.
+2. Go to the mac folder.
+3. Select your preferred language, you will find the dmg package inside, download it.
+4. Drag and drop the application to any other folder but the `Applications` folder.
+5. Rename the application to something like `Firefox_Old`.
+6. Move the application to the `Applications` folder.
+7. Open up a terminal and run `/Applications/Firefox_Old.app/Contents/MacOS/firefox-bin -profilemanager` to create a new profile specific to that Firefox version.
+8. Once the profile has been created, quit the app, and run it again like normal. You now have a working older Firefox version.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index ea01d88d85f..a310f12b29e 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -153,7 +153,7 @@ page](https://golang.org/dl).
# Remove former Go installation folder
sudo rm -rf /usr/local/go
-
+
curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
@@ -494,11 +494,11 @@ Make GitLab start on boot:
### Install Gitaly
# Fetch Gitaly source with Git and compile with Go
- sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production
+ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories]" RAILS_ENV=production
You can specify a different Git repository by providing it as an extra parameter:
- sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,https://example.com/gitaly.git]" RAILS_ENV=production
+ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories,https://example.com/gitaly.git]" RAILS_ENV=production
Next, make sure gitaly configured:
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index 2afcb052536..bf587b5b296 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -1,4 +1,4 @@
-# Integrate your GitLab server with Bitbucket
+# Integrate your GitLab server with Bitbucket Cloud
NOTE: **Note:**
You need to [enable OmniAuth](omniauth.md) in order to use this.
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 8ba2e8731c8..acc9db15826 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -78,8 +78,8 @@ in the **Authorized applications** section under **Profile Settings > Applicatio
---
GitLab's OAuth applications support scopes, which allow various actions that any given
-application can perform. Although there are only two scopes available at the
-moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
+application can perform such as `read_user` and `api`. There are many more scopes
+available.
At any time you can revoke any access by just clicking **Revoke**.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index bd199b55a61..6856544ae1b 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -234,6 +234,8 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+ Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+
Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
:zap: You can use emoji anywhere GFM is supported. :v:
@@ -244,6 +246,8 @@ If you are new to this, don't be :fearful:. You can easily join the emoji :famil
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
+Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support. On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/help/emoji/) to get full native emoji support.
+
### Special GitLab References
GFM recognizes special references.
@@ -259,6 +263,7 @@ GFM will recognize the following:
| `@user_name` | specific user |
| `@group_name` | specific group |
| `@all` | entire team |
+| `namespace/project>` | project |
| `#12345` | issue |
| `!123` | merge request |
| `$123` | snippet |
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index 324a7fa6603..4261293b06f 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -1,17 +1,25 @@
-# Bulk Editing
+# Bulk editing issues and merge requests
->**Note:**
+>
+**Notes:**
- A permission level of `Reporter` or higher is required in order to manage
issues.
- A permission level of `Developer` or higher is required in order to manage
merge requests.
-Fields across multiple issues or merge requests can be updated simutaneously by using the bulk edit feature.
+Attributes can be updated simultaneously across multiple issues or merge requests
+by using the bulk editing feature.
->**Note:**
-- Bulk editing of issues and merge requests is only available at the project level.
+![Bulk editing](img/bulk-editing.png)
-To access the feature, navigate to either the issue or merge request list for the project and click 'Edit Issues' or 'Edit Merge Requests'. This will cause a sidebar to be shown on the right-hand side of the screen, where the available, editable fields are displayed. Checkboxes will also appear to the left-hand side of each issue or merge request, ready to be selected.
+NOTE: **Note:**
+Bulk editing of issues and merge requests is only available at the project level.
-Once all items have been selected, choose the appropriate fields and their values from the sidebar and click 'Update All' to apply these changes.
+To update multiple project issues or merge requests at the same time, navigate to
+their respective lists and click **Edit issues** or **Edit merge requests** available
+in the tab bar. This will open a sidebar on the right-hand side of your screen
+where editable fields will be displayed. Checkboxes will also appear to the left-hand
+side of eachissue or merge request for you to select the items you want to update.
+Once you have selected all relevant items, choose the appropriate fields and their
+values from the sidebar and click **Update all** to apply your changes.
diff --git a/doc/user/project/img/bulk-editing.png b/doc/user/project/img/bulk-editing.png
new file mode 100644
index 00000000000..f6b163f55d9
--- /dev/null
+++ b/doc/user/project/img/bulk-editing.png
Binary files differ
diff --git a/doc/user/project/img/issue_board.png b/doc/user/project/img/issue_board.png
index 50e051e25a0..925b969eebe 100644
--- a/doc/user/project/img/issue_board.png
+++ b/doc/user/project/img/issue_board.png
Binary files differ
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index e3d625cc621..1cc3bd3363d 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -1,17 +1,13 @@
-# Import your project from Bitbucket to GitLab
+# Import your project from Bitbucket Cloud to GitLab
-Import your projects from Bitbucket to GitLab with minimal effort.
+NOTE: **Note:**
+The Bitbucket Cloud importer works only with Bitbucket.org, not with Bitbucket
+Server (aka Stash). If you are trying to import projects from Bitbucket Server, use
+[the Bitbucket Server importer](bitbucket_server.md).
-## Overview
-
->**Note:**
-The [Bitbucket integration][bb-import] must be first enabled in order to be
-able to import your projects from Bitbucket. Ask your GitLab administrator
-to enable this if not already.
+Import your projects from Bitbucket Cloud to GitLab with minimal effort.
->**Note:**
-The BitBucket importer currently only works with BitBucket's cloud offering
-(bitbucket.org) and does not work with BitBucket Server (aka Stash).
+## Overview
- At its current state, the Bitbucket importer can import:
- the repository description (GitLab 7.7+)
@@ -26,6 +22,11 @@ The BitBucket importer currently only works with BitBucket's cloud offering
- Repository public access is retained. If a repository is private in Bitbucket
it will be created as private in GitLab as well.
+## Requirements
+
+The [Bitbucket Cloud integration][bb-import] must be first enabled in order to be
+able to import your projects from Bitbucket Cloud. Ask your GitLab administrator
+to enable this if not already.
## How it works
@@ -46,9 +47,7 @@ namespace that started the import process.
1. Sign in to GitLab and go to your dashboard.
1. Click on **New project**.
- ![New project in GitLab](img/bitbucket_import_new_project.png)
-
-1. Click on the "Bitbucket" button
+1. Click on the "Bitbucket Cloud" button.
![Bitbucket](img/import_projects_from_new_project_page.png)
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
new file mode 100644
index 00000000000..dc985e87a96
--- /dev/null
+++ b/doc/user/project/import/bitbucket_server.md
@@ -0,0 +1,75 @@
+# Import your project from Bitbucket Server to GitLab
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20164)
+in GitLab 11.2.
+
+NOTE: **Note:**
+The Bitbucket Server importer does not work with Bitbucket Cloud (aka bitbucket.org).
+Use the [Bitbucket Cloud importer](bitbucket.md) for that.
+
+Import your projects from Bitbucket Server to GitLab with minimal effort.
+
+## Overview
+
+- In its current state, the Bitbucket importer can import:
+ - the repository description (GitLab 11.2+)
+ - the Git repository data (GitLab 11.2+)
+ - the pull requests (GitLab 11.2+)
+ - the pull request comments (GitLab 11.2+)
+- Repository public access is retained. If a repository is private in Bitbucket
+ it will be created as private in GitLab as well.
+
+## Limitations
+
+1. Currently GitLab doesn't allow comments on arbitrary lines of code, so any
+Bitbucket comments out of bounds will be inserted as comments in the merge
+request.
+1. Bitbucket Server allows multiple levels of threading. GitLab
+import will collapse this into one discussion and quote part of the original
+comment.
+1. Declined pull requests have unrecahable commits, which prevents the GitLab
+importer from generating a proper diff. These pull requests will show up as
+empty changes.
+1. Attachments in Markdown are currently not imported.
+1. Task lists are not imported.
+1. Emoji reactions are not imported
+
+## How it works
+
+The Bitbucket Server importer works as follows:
+
+1. The user will be prompted to enter the URl, username, and password or personal access token to login to Bitbucket.
+ These credentials are preserved only as long as the importer is running.
+1. The importer will attempt to list all the current repositories on the Bitbucket Server.
+1. Upon selection, the importer will clone the repository and import pull requests and comments.
+
+### User assignment
+
+When issues/pull requests are being imported, the Bitbucket importer tries to
+find the author's e-mail address with a confirmed e-mail address in the GitLab
+user database. If no such user is available, the project creator is set as
+the author. The importer will append a note in the comment to mark the original
+creator.
+
+The importer will create any new namespaces (groups) if they don't exist or in
+the case the namespace is taken, the repository will be imported under the user's
+namespace that started the import process.
+
+## Importing your Bitbucket repositories
+
+1. Sign in to GitLab and go to your dashboard.
+1. Click on **New project**.
+1. Click on the "Bitbucket Server" button. If the button is not present, enable the importer in
+ **Admin > Application Settings > Visibility and access controls > Import sources**.
+
+ ![Bitbucket](img/import_projects_from_new_project_page.png)
+
+1. Enter your Bitbucket Server credentials.
+
+ ![Grant access](img/bitbucket_server_import_credentials.png)
+
+1. Click on the projects that you'd like to import or **Import all projects**.
+ You can also select the namespace under which each project will be
+ imported.
+
+ ![Import projects](img/bitbucket_server_import_select_project.png)
diff --git a/doc/user/project/import/img/bitbucket_import_new_project.png b/doc/user/project/import/img/bitbucket_import_new_project.png
deleted file mode 100644
index 8ed528c2f09..00000000000
--- a/doc/user/project/import/img/bitbucket_import_new_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/bitbucket_server_import_credentials.png b/doc/user/project/import/img/bitbucket_server_import_credentials.png
new file mode 100644
index 00000000000..70b26e89d49
--- /dev/null
+++ b/doc/user/project/import/img/bitbucket_server_import_credentials.png
Binary files differ
diff --git a/doc/user/project/import/img/bitbucket_server_import_select_project.png b/doc/user/project/import/img/bitbucket_server_import_select_project.png
new file mode 100644
index 00000000000..e5b1b89e6a3
--- /dev/null
+++ b/doc/user/project/import/img/bitbucket_server_import_select_project.png
Binary files differ
diff --git a/doc/user/project/import/img/import_projects_from_new_project_page.png b/doc/user/project/import/img/import_projects_from_new_project_page.png
index 97ca30b2087..40402eae226 100644
--- a/doc/user/project/import/img/import_projects_from_new_project_page.png
+++ b/doc/user/project/import/img/import_projects_from_new_project_page.png
Binary files differ
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 8e486318980..77fa517b5b1 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1122,7 +1122,6 @@ X-Gitlab-Event: Build Hook
},
"repository": {
"name": "gitlab_test",
- "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
"description": "Atque in sunt eos similique dolores voluptatem.",
"homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 49b49271cff..0e847be79c2 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -119,10 +119,10 @@ Issue Board, that is, create or delete lists and drag issues from one list to an
## Issue Board terminology
- **Issue Board** - Each board represents a unique view for your issues. It can have multiple lists with each list consisting of issues represented by cards.
-- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Backlog' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee. On the top of that list you can see the number of issues that belong to it.
+- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Open' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee. On the top of that list you can see the number of issues that belong to it.
- **Label list**: a list based on a label. It shows all opened issues with that label.
- **Assignee list**: a list which includes all issues assigned to a user.
- - **Backlog** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
+ - **Open** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
- **Closed** (default): shows all closed issues. Always appears as the rightmost list.
- **Card** - A box in the list that represents an individual issue. The information you can see on a card consists of the issue number, the issue title, the assignee, and the labels associated with the issue. You can drag cards from one list to another to change their label or assignee from that of the source list to that of the destination list.
@@ -353,9 +353,9 @@ To remove an assignee list, just as with a label list, click the trash icon.
When dragging issues between lists, different behavior occurs depending on the source list and the target list.
-| | To Backlog | To Closed | To label `B` list | To assignee `Bob` list |
+| | To Open | To Closed | To label `B` list | To assignee `Bob` list |
| --- | --- | --- | --- | --- |
-| From Backlog | - | Issue closed | `B` added | `Bob` assigned |
+| From Open | - | Issue closed | `B` added | `Bob` assigned |
| From Closed | Issue reopened | - | Issue reopened<br/>`B` added | Issue reopened<br/>`Bob` assigned |
| From label `A` list | `A` removed | Issue closed | `A` removed<br/>`B` added | `Bob` assigned |
| From assignee `Alice` list | `Alice` unassigned | Issue closed | `B` added | `Alice` unassigned<br/>`Bob` assigned |
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 0ef8eddad20..8fdfd2a6f4d 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -1,7 +1,7 @@
# GitLab quick actions
-Quick actions are textual shortcuts for common actions on issues or merge
-requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
+Quick actions are textual shortcuts for common actions on issues, merge requests
+or commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
You can enter these commands while creating a new issue or merge request, and
in comments. Each command should be on a separate line in order to be properly
detected and executed. The commands are removed from the issue, merge request or
@@ -39,7 +39,8 @@ do.
| `/board_move ~column` | Move issue to column on the board |
| `/duplicate #issue` | Closes this issue and marks it as a duplicate of another issue |
| `/move path/to/project` | Moves issue to another project |
+| `/tag v1.2.3 <message>` | Tags a commit with a given tag name and optional message |
| `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` |
| `/shrug` | Append the comment with `¯\_(ツ)_/¯` |
| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request |
-| `/confidential` | Makes the issue confidential | \ No newline at end of file
+| `/confidential` | Makes the issue confidential |
diff --git a/doc/user/project/web_ide/img/admin_clientside_evaluation.png b/doc/user/project/web_ide/img/admin_clientside_evaluation.png
new file mode 100644
index 00000000000..a930490398b
--- /dev/null
+++ b/doc/user/project/web_ide/img/admin_clientside_evaluation.png
Binary files differ
diff --git a/doc/user/project/web_ide/img/clientside_evaluation.png b/doc/user/project/web_ide/img/clientside_evaluation.png
new file mode 100644
index 00000000000..bd04d3d644b
--- /dev/null
+++ b/doc/user/project/web_ide/img/clientside_evaluation.png
Binary files differ
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 511ac2d7e79..16969b2c527 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -72,5 +72,39 @@ leaving the Web IDE. Click the dropdown in the top of the sidebar to open a list
of branches. You will need to commit or discard all your changes before
switching to a different branch.
+## Client Side Evaluation
+
+> [Introduced in](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19764) [GitLab Core][ce] 11.2.
+
+The Web IDE can be used to preview JavaScript projects right in the browser.
+This feature uses CodeSandbox to compile and bundle the JavaScript used to
+preview the web application. On public projects, an `Open in CodeSandbox`
+button is visible which will transfer the contents of the project into a
+CodeSandbox project to share with others.
+**Note** this button is not visible on private or internal projects.
+
+![Web IDE Client Side Evaluation](img/clientside_evaluation.png)
+
+### Enabling Client Side Evaluation
+
+The Client Side Evaluation feature needs to be enabled in the GitLab instances
+admin settings. Client Side Evaluation is enabled for all projects on
+GitLab.com
+
+![Admin Client Side Evaluation setting](img/admin_clientside_evaluation.png)
+
+Once it has been enabled in application settings, projects with a
+`package.json` file and a `main` entry point can be previewed inside of the Web
+IDE. An example `package.json` is below.
+
+```json
+{
+ "main": "index.js",
+ "dependencies": {
+ "vue": "latest"
+ }
+}
+```
+
[ce]: https://about.gitlab.com/pricing/
[ee]: https://about.gitlab.com/pricing/
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 27f28e1df93..458ee320099 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -856,7 +856,7 @@ module API
class NotificationSetting < Grape::Entity
expose :level
expose :events, if: ->(notification_setting, _) { notification_setting.custom? } do
- ::NotificationSetting::EMAIL_EVENTS.each do |event|
+ ::NotificationSetting.email_events.each do |event|
expose event
end
end
@@ -1080,6 +1080,10 @@ module API
expose :filename, :size
end
+ class JobArtifact < Grape::Entity
+ expose :file_type, :size, :filename, :file_format
+ end
+
class JobBasic < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
@@ -1094,7 +1098,9 @@ module API
end
class Job < JobBasic
+ # artifacts_file is included in job_artifacts, but kept for backward compatibility (remove in api/v5)
expose :artifacts_file, using: JobArtifactFile, if: -> (job, opts) { job.artifacts? }
+ expose :job_artifacts, as: :artifacts, using: JobArtifact
expose :runner, with: Runner
expose :artifacts_expire_at
end
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index 10c6e565f09..fc8c52085ab 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -38,7 +38,7 @@ module API
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
- builds = builds.preload(:user, :job_artifacts_archive, :runner, pipeline: :project)
+ builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, pipeline: :project)
present paginate(builds), with: Entities::Job
end
@@ -54,7 +54,7 @@ module API
pipeline = user_project.pipelines.find(params[:pipeline_id])
builds = pipeline.builds
builds = filter_builds(builds, params[:scope])
- builds = builds.preload(:job_artifacts_archive, project: [:namespace])
+ builds = builds.preload(:job_artifacts_archive, :job_artifacts, project: [:namespace])
present paginate(builds), with: Entities::Job
end
diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb
index 0266bf2f717..bf0d6b9e434 100644
--- a/lib/api/notification_settings.rb
+++ b/lib/api/notification_settings.rb
@@ -23,7 +23,7 @@ module API
params do
optional :level, type: String, desc: 'The global notification level'
optional :notification_email, type: String, desc: 'The email address to send notifications'
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ NotificationSetting.email_events.each do |event|
optional event, type: Boolean, desc: 'Enable/disable this notification'
end
end
@@ -50,7 +50,9 @@ module API
end
end
- %w[group project].each do |source_type|
+ [Group, Project].each do |source_class|
+ source_type = source_class.name.underscore
+
params do
requires :id, type: String, desc: "The #{source_type} ID"
end
@@ -73,7 +75,7 @@ module API
end
params do
optional :level, type: String, desc: "The #{source_type} notification level"
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ NotificationSetting.email_events(source_class).each do |event|
optional event, type: Boolean, desc: 'Enable/disable this notification'
end
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 33a9646ac3b..79736107bbb 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -123,6 +123,39 @@ module API
not_found!
end
end
+
+ desc 'Get the common ancestor between commits' do
+ success Entities::Commit
+ end
+ params do
+ # For now we just support 2 refs passed, but `merge-base` supports
+ # multiple defining this as an Array instead of 2 separate params will
+ # make sure we don't need to deprecate this API in favor of one
+ # supporting multiple commits when this functionality gets added to
+ # Gitaly
+ requires :refs, type: Array[String]
+ end
+ get ':id/repository/merge_base' do
+ refs = params[:refs]
+
+ unless refs.size == 2
+ render_api_error!('Provide exactly 2 refs', 400)
+ end
+
+ merge_base = Gitlab::Git::MergeBase.new(user_project.repository, refs)
+
+ if merge_base.unknown_refs.any?
+ ref_noun = 'ref'.pluralize(merge_base.unknown_refs.size)
+ message = "Could not find #{ref_noun}: #{merge_base.unknown_refs.join(', ')}"
+ render_api_error!(message, 400)
+ end
+
+ if merge_base.commit
+ present merge_base.commit, with: Entities::Commit
+ else
+ not_found!("Merge Base")
+ end
+ end
end
end
end
diff --git a/lib/banzai/filter/project_reference_filter.rb b/lib/banzai/filter/project_reference_filter.rb
new file mode 100644
index 00000000000..83cf45097ed
--- /dev/null
+++ b/lib/banzai/filter/project_reference_filter.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # HTML filter that replaces project references with links.
+ class ProjectReferenceFilter < ReferenceFilter
+ self.reference_type = :project
+
+ # Public: Find `namespace/project>` project references in text
+ #
+ # ProjectReferenceFilter.references_in(text) do |match, project|
+ # "<a href=...>#{project}></a>"
+ # end
+ #
+ # text - String text to search.
+ #
+ # Yields the String match, and the String project name.
+ #
+ # Returns a String replaced with the return of the block.
+ def self.references_in(text)
+ text.gsub(Project.markdown_reference_pattern) do |match|
+ yield match, "#{$~[:namespace]}/#{$~[:project]}"
+ end
+ end
+
+ def call
+ ref_pattern = Project.markdown_reference_pattern
+ ref_pattern_start = /\A#{ref_pattern}\z/
+
+ nodes.each do |node|
+ if text_node?(node)
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ project_link_filter(content)
+ end
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, inner_html|
+ if link =~ ref_pattern_start
+ replace_link_node_with_href(node, link) do
+ project_link_filter(link, link_content: inner_html)
+ end
+ end
+ end
+ end
+ end
+
+ doc
+ end
+
+ # Replace `namespace/project>` project references in text with links to the referenced
+ # project page.
+ #
+ # text - String text to replace references in.
+ # link_content - Original content of the link being replaced.
+ #
+ # Returns a String with `namespace/project>` references replaced with links. All links
+ # have `gfm` and `gfm-project` class names attached for styling.
+ def project_link_filter(text, link_content: nil)
+ self.class.references_in(text) do |match, project_path|
+ cached_call(:banzai_url_for_object, match, path: [Project, project_path.downcase]) do
+ if project = projects_hash[project_path.downcase]
+ link_to_project(project, link_content: link_content) || match
+ else
+ match
+ end
+ end
+ end
+ end
+
+ # Returns a Hash containing all Project objects for the project
+ # references in the current document.
+ #
+ # The keys of this Hash are the project paths, the values the
+ # corresponding Project objects.
+ def projects_hash
+ @projects ||= Project.eager_load(:route, namespace: [:route])
+ .where_full_path_in(projects)
+ .index_by(&:full_path)
+ .transform_keys(&:downcase)
+ end
+
+ # Returns all projects referenced in the current document.
+ def projects
+ refs = Set.new
+
+ nodes.each do |node|
+ node.to_html.scan(Project.markdown_reference_pattern) do
+ refs << "#{$~[:namespace]}/#{$~[:project]}"
+ end
+ end
+
+ refs.to_a
+ end
+
+ private
+
+ def urls
+ Gitlab::Routing.url_helpers
+ end
+
+ def link_class
+ reference_class(:project)
+ end
+
+ def link_to_project(project, link_content: nil)
+ url = urls.project_url(project, only_path: context[:only_path])
+ data = data_attribute(project: project.id)
+ content = link_content || project.to_reference_with_postfix
+
+ link_tag(url, data, content, project.name)
+ end
+
+ def link_tag(url, data, link_content, title)
+ %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 5dab80dd3eb..e9be05e174e 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -36,6 +36,7 @@ module Banzai
def self.reference_filters
[
Filter::UserReferenceFilter,
+ Filter::ProjectReferenceFilter,
Filter::IssueReferenceFilter,
Filter::ExternalIssueReferenceFilter,
Filter::MergeRequestReferenceFilter,
diff --git a/lib/banzai/reference_parser/project_parser.rb b/lib/banzai/reference_parser/project_parser.rb
new file mode 100644
index 00000000000..b4e3a55b4f1
--- /dev/null
+++ b/lib/banzai/reference_parser/project_parser.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Banzai
+ module ReferenceParser
+ class ProjectParser < BaseParser
+ include Gitlab::Utils::StrongMemoize
+
+ self.reference_type = :project
+
+ def references_relation
+ Project
+ end
+
+ private
+
+ # Returns an Array of Project ids that can be read by the given user.
+ #
+ # user - The User for which to check the projects
+ def readable_project_ids_for(user)
+ @project_ids_by_user ||= {}
+ @project_ids_by_user[user] ||=
+ Project.public_or_visible_to_user(user).where("projects.id IN (?)", @projects_for_nodes.values.map(&:id)).pluck(:id)
+ end
+
+ def can_read_reference?(user, ref_project, node)
+ readable_project_ids_for(user).include?(ref_project.try(:id))
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index f3999e690fa..fa0186c854c 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -188,7 +188,8 @@ module Gitlab
end
def import_inline_comments(inline_comments, pull_request, merge_request)
- line_code_map = {}
+ position_map = {}
+ discussion_map = {}
children, parents = inline_comments.partition(&:has_parent?)
@@ -196,22 +197,28 @@ module Gitlab
# relationships. We assume that the child can appear in any order in
# the JSON.
parents.each do |comment|
- line_code_map[comment.iid] = generate_line_code(comment)
+ position_map[comment.iid] = build_position(merge_request, comment)
end
children.each do |comment|
- line_code_map[comment.iid] = line_code_map.fetch(comment.parent_id, nil)
+ position_map[comment.iid] = position_map.fetch(comment.parent_id, nil)
end
inline_comments.each do |comment|
begin
attributes = pull_request_comment_attributes(comment)
+ attributes[:discussion_id] = discussion_map[comment.parent_id] if comment.has_parent?
+
attributes.merge!(
- position: build_position(merge_request, comment),
- line_code: line_code_map.fetch(comment.iid),
+ position: position_map[comment.iid],
type: 'DiffNote')
- merge_request.notes.create!(attributes)
+ note = merge_request.notes.create!(attributes)
+
+ # We can't store a discussion ID until a note is created, so if
+ # replies are created before the parent the discussion ID won't be
+ # linked properly.
+ discussion_map[comment.iid] = note.discussion_id
rescue StandardError => e
errors << { type: :pull_request, iid: comment.iid, errors: e.message }
end
@@ -240,10 +247,6 @@ module Gitlab
end
end
- def generate_line_code(pr_comment)
- Gitlab::Git.diff_line_code(pr_comment.file_path, pr_comment.new_pos, pr_comment.old_pos)
- end
-
def pull_request_comment_attributes(comment)
{
project: project,
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index f39b3b6eb5b..7f012312819 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -58,7 +58,6 @@ module Gitlab
if Database.postgresql?
options = options.merge({ algorithm: :concurrently })
- disable_statement_timeout
end
if index_exists?(table_name, column_name, options)
@@ -66,7 +65,9 @@ module Gitlab
return
end
- add_index(table_name, column_name, options)
+ disable_statement_timeout do
+ add_index(table_name, column_name, options)
+ end
end
# Removes an existed index, concurrently when supported
@@ -87,7 +88,6 @@ module Gitlab
if supports_drop_index_concurrently?
options = options.merge({ algorithm: :concurrently })
- disable_statement_timeout
end
unless index_exists?(table_name, column_name, options)
@@ -95,7 +95,9 @@ module Gitlab
return
end
- remove_index(table_name, options.merge({ column: column_name }))
+ disable_statement_timeout do
+ remove_index(table_name, options.merge({ column: column_name }))
+ end
end
# Removes an existing index, concurrently when supported
@@ -116,7 +118,6 @@ module Gitlab
if supports_drop_index_concurrently?
options = options.merge({ algorithm: :concurrently })
- disable_statement_timeout
end
unless index_exists_by_name?(table_name, index_name)
@@ -124,7 +125,9 @@ module Gitlab
return
end
- remove_index(table_name, options.merge({ name: index_name }))
+ disable_statement_timeout do
+ remove_index(table_name, options.merge({ name: index_name }))
+ end
end
# Only available on Postgresql >= 9.2
@@ -171,8 +174,6 @@ module Gitlab
on_delete = 'SET NULL' if on_delete == :nullify
end
- disable_statement_timeout
-
key_name = concurrent_foreign_key_name(source, column)
unless foreign_key_exists?(source, target, column: column)
@@ -199,7 +200,9 @@ module Gitlab
# while running.
#
# Note this is a no-op in case the constraint is VALID already
- execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};")
+ disable_statement_timeout do
+ execute("ALTER TABLE #{source} VALIDATE CONSTRAINT #{key_name};")
+ end
end
def foreign_key_exists?(source, target = nil, column: nil)
@@ -224,8 +227,48 @@ module Gitlab
# Long-running migrations may take more than the timeout allowed by
# the database. Disable the session's statement timeout to ensure
# migrations don't get killed prematurely. (PostgreSQL only)
+ #
+ # There are two possible ways to disable the statement timeout:
+ #
+ # - Per transaction (this is the preferred and default mode)
+ # - Per connection (requires a cleanup after the execution)
+ #
+ # When using a per connection disable statement, code must be inside
+ # a block so we can automatically execute `RESET ALL` after block finishes
+ # otherwise the statement will still be disabled until connection is dropped
+ # or `RESET ALL` is executed
def disable_statement_timeout
- execute('SET statement_timeout TO 0') if Database.postgresql?
+ # bypass disabled_statement logic when not using postgres, but still execute block when one is given
+ unless Database.postgresql?
+ if block_given?
+ yield
+ end
+
+ return
+ end
+
+ if block_given?
+ begin
+ execute('SET statement_timeout TO 0')
+
+ yield
+ ensure
+ execute('RESET ALL')
+ end
+ else
+ unless transaction_open?
+ raise <<~ERROR
+ Cannot call disable_statement_timeout() without a transaction open or outside of a transaction block.
+ If you don't want to use a transaction wrap your code in a block call:
+
+ disable_statement_timeout { # code that requires disabled statement here }
+
+ This will make sure statement_timeout is disabled before and reset after the block execution is finished.
+ ERROR
+ end
+
+ execute('SET LOCAL statement_timeout TO 0')
+ end
end
def true_value
@@ -367,30 +410,30 @@ module Gitlab
'in the body of your migration class'
end
- disable_statement_timeout
-
- transaction do
- if limit
- add_column(table, column, type, default: nil, limit: limit)
- else
- add_column(table, column, type, default: nil)
+ disable_statement_timeout do
+ transaction do
+ if limit
+ add_column(table, column, type, default: nil, limit: limit)
+ else
+ add_column(table, column, type, default: nil)
+ end
+
+ # Changing the default before the update ensures any newly inserted
+ # rows already use the proper default value.
+ change_column_default(table, column, default)
end
- # Changing the default before the update ensures any newly inserted
- # rows already use the proper default value.
- change_column_default(table, column, default)
- end
-
- begin
- update_column_in_batches(table, column, default, &block)
+ begin
+ update_column_in_batches(table, column, default, &block)
- change_column_null(table, column, false) unless allow_null
- # We want to rescue _all_ exceptions here, even those that don't inherit
- # from StandardError.
- rescue Exception => error # rubocop: disable all
- remove_column(table, column)
+ change_column_null(table, column, false) unless allow_null
+ # We want to rescue _all_ exceptions here, even those that don't inherit
+ # from StandardError.
+ rescue Exception => error # rubocop: disable all
+ remove_column(table, column)
- raise error
+ raise error
+ end
end
end
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 55236a1122f..2913a3e416d 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -10,9 +10,11 @@ module Gitlab
TAG_REF_PREFIX = "refs/tags/".freeze
BRANCH_REF_PREFIX = "refs/heads/".freeze
- CommandError = Class.new(StandardError)
- CommitError = Class.new(StandardError)
- OSError = Class.new(StandardError)
+ BaseError = Class.new(StandardError)
+ CommandError = Class.new(BaseError)
+ CommitError = Class.new(BaseError)
+ OSError = Class.new(BaseError)
+ UnknownRef = Class.new(BaseError)
class << self
include Gitlab::EncodingHelper
diff --git a/lib/gitlab/git/merge_base.rb b/lib/gitlab/git/merge_base.rb
new file mode 100644
index 00000000000..b27f7038c26
--- /dev/null
+++ b/lib/gitlab/git/merge_base.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ class MergeBase
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(repository, refs)
+ @repository, @refs = repository, refs
+ end
+
+ # Returns the SHA of the first common ancestor
+ def sha
+ if unknown_refs.any?
+ raise UnknownRef, "Can't find merge base for unknown refs: #{unknown_refs.inspect}"
+ end
+
+ strong_memoize(:sha) do
+ @repository.merge_base(*commits_for_refs)
+ end
+ end
+
+ # Returns the merge base as a Gitlab::Git::Commit
+ def commit
+ return unless sha
+
+ @commit ||= @repository.commit_by(oid: sha)
+ end
+
+ # Returns the refs passed on initialization that aren't found in
+ # the repository, and thus cannot be used to find a merge base.
+ def unknown_refs
+ @unknown_refs ||= Hash[@refs.zip(commits_for_refs)]
+ .select { |ref, commit| commit.nil? }.keys
+ end
+
+ private
+
+ def commits_for_refs
+ @commits_for_refs ||= @repository.commits_by(oids: @refs)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index de189ac6dfc..e9c901f8592 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -366,23 +366,14 @@ module Gitlab
end
end
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
def new_commits(newrev)
- gitaly_migrate(:new_commits) do |is_enabled|
- if is_enabled
- gitaly_ref_client.list_new_commits(newrev)
- else
- refs = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- rev_list(including: newrev, excluding: :all).split("\n").map(&:strip)
- end
-
- Gitlab::Git::Commit.batch_by_oid(self, refs)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.list_new_commits(newrev)
end
end
def new_blobs(newrev)
- return [] if newrev == ::Gitlab::Git::BLANK_SHA
+ return [] if newrev.blank? || newrev == ::Gitlab::Git::BLANK_SHA
strong_memoize("new_blobs_#{newrev}") do
wrapped_gitaly_errors do
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 091e81028bb..81807ed659c 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -47,7 +47,7 @@ module Gitlab
end
def initialize(relation_sym:, relation_hash:, members_mapper:, user:, project:, excluded_keys: [])
- @relation_name = OVERRIDES[relation_sym] || relation_sym
+ @relation_name = self.class.overrides[relation_sym] || relation_sym
@relation_hash = relation_hash.except('noteable_id')
@members_mapper = members_mapper
@user = user
@@ -76,6 +76,10 @@ module Gitlab
generate_imported_object
end
+ def self.overrides
+ OVERRIDES
+ end
+
private
def setup_models
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index b2d75aac1d0..5b68e4470cd 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -1,3 +1,5 @@
+require 'toml-rb'
+
module Gitlab
module SetupHelper
class << self
@@ -9,7 +11,7 @@ module Gitlab
# because it uses a Unix socket.
# For development and testing purposes, an extra storage is added to gitaly,
# which is not known to Rails, but must be explicitly stubbed.
- def gitaly_configuration_toml(gitaly_dir, gitaly_ruby: true)
+ def gitaly_configuration_toml(gitaly_dir, storage_paths, gitaly_ruby: true)
storages = []
address = nil
@@ -24,10 +26,7 @@ module Gitlab
address = val['gitaly_address']
end
- # https://gitlab.com/gitlab-org/gitaly/issues/1238
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- storages << { name: key, path: val.legacy_disk_path }
- end
+ storages << { name: key, path: storage_paths[key] }
end
if Rails.env.test?
@@ -44,12 +43,12 @@ module Gitlab
end
# rubocop:disable Rails/Output
- def create_gitaly_configuration(dir, force: false)
+ def create_gitaly_configuration(dir, storage_paths, force: false)
config_path = File.join(dir, 'config.toml')
FileUtils.rm_f(config_path) if force
File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f|
- f.puts gitaly_configuration_toml(dir)
+ f.puts gitaly_configuration_toml(dir, storage_paths)
end
rescue Errno::EEXIST
puts "Skipping config.toml generation:"
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index e9ca6404fe8..80de3d2ef51 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -1,13 +1,12 @@
namespace :gitlab do
namespace :gitaly do
desc "GitLab | Install or upgrade gitaly"
- task :install, [:dir, :repo] => :gitlab_environment do |t, args|
- require 'toml-rb'
-
+ task :install, [:dir, :storage_path, :repo] => :gitlab_environment do |t, args|
warn_user_is_not_gitlab
- unless args.dir.present?
- abort %(Please specify the directory where you want to install gitaly:\n rake "gitlab:gitaly:install[/home/git/gitaly]")
+ unless args.dir.present? && args.storage_path.present?
+ abort %(Please specify the directory where you want to install gitaly and the path for the default storage
+Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
end
args.with_defaults(repo: 'https://gitlab.com/gitlab-org/gitaly.git')
@@ -27,7 +26,8 @@ namespace :gitlab do
"BUNDLE_PATH=#{Bundler.bundle_path}")
end
- Gitlab::SetupHelper.create_gitaly_configuration(args.dir)
+ storage_paths = { 'default' => args.storage_path }
+ Gitlab::SetupHelper.create_gitaly_configuration(args.dir, storage_paths)
Dir.chdir(args.dir) do
# In CI we run scripts/gitaly-test-build instead of this command
unless ENV['CI'].present?
@@ -35,17 +35,5 @@ namespace :gitlab do
end
end
end
-
- desc "GitLab | Print storage configuration in TOML format"
- task storage_config: :environment do
- require 'toml-rb'
-
- puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}"
- puts "# This is in TOML format suitable for use in Gitaly's config.toml file."
-
- # Exclude gitaly-ruby configuration because that depends on the gitaly
- # installation directory.
- puts Gitlab::SetupHelper.gitaly_configuration_toml('', gitaly_ruby: false)
- end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bec60cf592a..e5e818f57b9 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -101,6 +101,9 @@ msgstr[1] ""
msgid "%{filePath} deleted"
msgstr ""
+msgid "%{firstLabel} +%{labelCount} more"
+msgstr ""
+
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
@@ -640,6 +643,9 @@ msgstr ""
msgid "Authentication log"
msgstr ""
+msgid "Authentication method"
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -1987,6 +1993,9 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
+msgid "Debug"
+msgstr ""
+
msgid "Dec"
msgstr ""
@@ -2169,6 +2178,9 @@ msgstr ""
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
+msgid "Direction"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -2391,6 +2403,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Error"
+msgstr ""
+
msgid "Error Reporting and Logging"
msgstr ""
@@ -2439,6 +2454,9 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error while loading the merge request. Please try again."
+msgstr ""
+
msgid "Estimated"
msgstr ""
@@ -2517,6 +2535,9 @@ msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr ""
+msgid "Failed to remove mirror."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
@@ -2717,6 +2738,9 @@ msgstr ""
msgid "Go back"
msgstr ""
+msgid "Go to"
+msgstr ""
+
msgid "Go to %{link_to_google_takeout}."
msgstr ""
@@ -2947,6 +2971,9 @@ msgstr ""
msgid "IDE|Review"
msgstr ""
+msgid "IP Address"
+msgstr ""
+
msgid "Identifier"
msgstr ""
@@ -3031,12 +3058,21 @@ msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
+msgstr ""
+
msgid "Incompatible Project"
msgstr ""
+msgid "Indicates whether this runner can pick jobs without tags"
+msgstr ""
+
msgid "Inline"
msgstr ""
+msgid "Input your repository URL"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -3103,6 +3139,30 @@ msgstr ""
msgid "Jobs"
msgstr ""
+msgid "Job|Browse"
+msgstr ""
+
+msgid "Job|Download"
+msgstr ""
+
+msgid "Job|Job artifacts"
+msgstr ""
+
+msgid "Job|Job has been erased"
+msgstr ""
+
+msgid "Job|Job has been erased by"
+msgstr ""
+
+msgid "Job|Keep"
+msgstr ""
+
+msgid "Job|The artifacts were removed"
+msgstr ""
+
+msgid "Job|The artifacts will be removed"
+msgstr ""
+
msgid "Jul"
msgstr ""
@@ -3354,6 +3414,9 @@ msgstr ""
msgid "Maximum git storage failures"
msgstr ""
+msgid "Maximum job timeout"
+msgstr ""
+
msgid "May"
msgstr ""
@@ -3468,6 +3531,21 @@ msgstr ""
msgid "Milestones|Promote Milestone"
msgstr ""
+msgid "Mirror a repository"
+msgstr ""
+
+msgid "Mirror direction"
+msgstr ""
+
+msgid "Mirror repository"
+msgstr ""
+
+msgid "Mirrored repositories"
+msgstr ""
+
+msgid "Mirroring repositories"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -3528,6 +3606,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Never"
+msgstr ""
+
msgid "New"
msgstr ""
@@ -3791,12 +3872,18 @@ msgstr ""
msgid "Only comments from the following commit are shown below"
msgstr ""
+msgid "Only mirror protected branches"
+msgstr ""
+
msgid "Only project members can comment."
msgstr ""
msgid "Oops, are you sure?"
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Open in Xcode"
msgstr ""
@@ -3872,6 +3959,9 @@ msgstr ""
msgid "Pause"
msgstr ""
+msgid "Paused Runners don't accept new jobs"
+msgstr ""
+
msgid "Pending"
msgstr ""
@@ -4364,6 +4454,9 @@ msgstr ""
msgid "Promote to group label"
msgstr ""
+msgid "Protected"
+msgstr ""
+
msgid "Protip:"
msgstr ""
@@ -4379,6 +4472,9 @@ msgstr ""
msgid "Public pipelines"
msgstr ""
+msgid "Push"
+msgstr ""
+
msgid "Push events"
msgstr ""
@@ -4576,6 +4672,9 @@ msgstr ""
msgid "Revoke"
msgstr ""
+msgid "Run untagged jobs"
+msgstr ""
+
msgid "Runner token"
msgstr ""
@@ -4588,6 +4687,9 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
+msgid "Runners page"
+msgstr ""
+
msgid "Running"
msgstr ""
@@ -4780,6 +4882,9 @@ msgstr ""
msgid "Set up Koding"
msgstr ""
+msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
+msgstr ""
+
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
@@ -5189,6 +5294,9 @@ msgstr ""
msgid "Test coverage parsing"
msgstr ""
+msgid "The Git LFS objects will <strong>not</strong> be synced."
+msgstr ""
+
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
@@ -5246,6 +5354,9 @@ msgstr ""
msgid "The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>."
msgstr ""
+msgid "The repository must be accessible over <code>http://</code>, <code>https://</code>, <code>ssh://</code> and <code>git://</code>."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -5270,6 +5381,9 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after 15 minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -5402,12 +5516,21 @@ msgstr ""
msgid "This repository"
msgstr ""
+msgid "This runner will only run on pipelines triggered on protected branches"
+msgstr ""
+
msgid "This source diff could not be displayed because it is too large."
msgstr ""
+msgid "This timeout will take precedence when lower than Project-defined timeout"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
+msgid "This user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
+msgstr ""
+
msgid "Time before an issue gets scheduled"
msgstr ""
@@ -5643,6 +5766,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
+msgid "Token"
+msgstr ""
+
msgid "Too many changes to show."
msgstr ""
@@ -5721,9 +5847,15 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update now"
+msgstr ""
+
msgid "Update your group name, description, avatar, and other general settings."
msgstr ""
+msgid "Updating"
+msgstr ""
+
msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr ""
@@ -6063,6 +6195,9 @@ msgstr ""
msgid "You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}"
msgstr ""
+msgid "You can setup jobs to only use Runners with specific tags. Separate tags with commas."
+msgstr ""
+
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
diff --git a/locale/unfound_translations.rb b/locale/unfound_translations.rb
new file mode 100644
index 00000000000..0826d64049b
--- /dev/null
+++ b/locale/unfound_translations.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# Dynamic translations which needs to be marked by `N_` so they can be found by `rake gettext:find`, see:
+# https://github.com/grosser/gettext_i18n_rails#unfound-translations-with-rake-gettextfind
+
+# NotificationSetting.email_events
+N_('NotificationEvent|New note')
+N_('NotificationEvent|New issue')
+N_('NotificationEvent|Reopen issue')
+N_('NotificationEvent|Close issue')
+N_('NotificationEvent|Reassign issue')
+N_('NotificationEvent|New merge request')
+N_('NotificationEvent|Close merge request')
+N_('NotificationEvent|Reassign merge request')
+N_('NotificationEvent|Merge merge request')
+N_('NotificationEvent|Failed pipeline')
diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb
index bc713b46d81..c2c3bef98e4 100644
--- a/qa/qa/specs/features/project/auto_devops_spec.rb
+++ b/qa/qa/specs/features/project/auto_devops_spec.rb
@@ -50,7 +50,7 @@ module QA
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to have_build('build', status: :success, wait: 600)
expect(pipeline).to have_build('test', status: :success, wait: 600)
- expect(pipeline).to have_build('production', status: :success, wait: 600)
+ expect(pipeline).to have_build('production', status: :success, wait: 1200)
end
end
end
diff --git a/rubocop/cop/migration/add_reference.rb b/rubocop/cop/migration/add_reference.rb
new file mode 100644
index 00000000000..4b67270c97a
--- /dev/null
+++ b/rubocop/cop/migration/add_reference.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if a foreign key constraint is added and require a index for it
+ class AddReference < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = '`add_reference` requires `index: true`'
+
+ def on_send(node)
+ return unless in_migration?(node)
+
+ name = node.children[1]
+
+ return unless name == :add_reference
+
+ opts = node.children.last
+
+ add_offense(node, location: :selector) unless opts && opts.type == :hash
+
+ index_present = false
+
+ opts.each_node(:pair) do |pair|
+ index_present ||= index_enabled?(pair)
+ end
+
+ add_offense(node, location: :selector) unless index_present
+ end
+
+ private
+
+ def index_enabled?(pair)
+ hash_key_type(pair) == :sym && hash_key_name(pair) == :index && pair.children[1].true_type?
+ end
+
+ def hash_key_type(pair)
+ pair.children[0].type
+ end
+
+ def hash_key_name(pair)
+ pair.children[0].children[0]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index aa7ae601f75..a427208cdab 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -11,6 +11,7 @@ require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
require_relative 'cop/migration/add_index'
+require_relative 'cop/migration/add_reference'
require_relative 'cop/migration/add_timestamps'
require_relative 'cop/migration/datetime'
require_relative 'cop/migration/hash_index'
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 421ab006792..fbf116e533b 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -162,6 +162,10 @@ describe ApplicationController do
describe 'session expiration' do
controller(described_class) do
+ # The anonymous controller will report 401 and fail to run any actions.
+ # Normally, GitLab will just redirect you to sign in.
+ skip_before_action :authenticate_user!, only: :index
+
def index
render text: 'authenticated'
end
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
index e133950e684..a3356a86d4b 100644
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ b/spec/controllers/notification_settings_controller_spec.rb
@@ -21,10 +21,11 @@ describe NotificationSettingsController do
end
context 'when authorized' do
+ let(:notification_setting) { user.notification_settings_for(source) }
let(:custom_events) do
events = {}
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ NotificationSetting.email_events(source).each do |event|
events[event.to_s] = true
end
@@ -36,7 +37,7 @@ describe NotificationSettingsController do
end
context 'for projects' do
- let(:notification_setting) { user.notification_settings_for(project) }
+ let(:source) { project }
it 'creates notification setting' do
post :create,
@@ -67,7 +68,7 @@ describe NotificationSettingsController do
end
context 'for groups' do
- let(:notification_setting) { user.notification_settings_for(group) }
+ let(:source) { group }
it 'creates notification setting' do
post :create,
@@ -145,7 +146,7 @@ describe NotificationSettingsController do
let(:custom_events) do
events = {}
- NotificationSetting::EMAIL_EVENTS.each do |event|
+ notification_setting.email_events.each do |event|
events[event] = "true"
end
end
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index a81b2169b89..81c485fba1a 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -46,6 +46,13 @@ FactoryBot.define do
secret SecureRandom.hex
end
+ trait :favicon_upload do
+ model { build(:appearance) }
+ path { File.join(secret, filename) }
+ uploader "FaviconUploader"
+ secret SecureRandom.hex
+ end
+
trait :attachment_upload do
transient do
mount_point :attachment
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 6c194c9a646..1db6c75b85b 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -68,10 +68,12 @@ describe "Admin::Users" do
end
describe "GET /admin/users/new" do
+ let(:user_username) { 'bang' }
+
before do
visit new_admin_user_path
fill_in "user_name", with: "Big Bang"
- fill_in "user_username", with: "bang"
+ fill_in "user_username", with: user_username
fill_in "user_email", with: "bigbang@mail.com"
end
@@ -112,6 +114,17 @@ describe "Admin::Users" do
expect(email.text_part.body).to have_content(user.email)
expect(email.text_part.body).to have_content('password')
end
+
+ context 'username contains spaces' do
+ let(:user_username) { 'Bing bang' }
+
+ it "doesn't create the user and shows an error message" do
+ expect { click_button "Create user" }.to change {User.count}.by(0)
+
+ expect(page).to have_content('The form contains the following error')
+ expect(page).to have_content('Username can contain only letters, digits')
+ end
+ end
end
describe "GET /admin/users/:id" do
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index a0af2dea3ad..baa2b1d8af5 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -44,7 +44,7 @@ describe 'Issue Boards', :js do
end
it 'creates default lists' do
- lists = ['Backlog', 'To Do', 'Doing', 'Closed']
+ lists = ['Open', 'To Do', 'Doing', 'Closed']
page.within(find('.board-blank-state')) do
click_button('Add default lists')
diff --git a/spec/features/commits/user_uses_slash_commands_spec.rb b/spec/features/commits/user_uses_slash_commands_spec.rb
new file mode 100644
index 00000000000..9a4b7bd2444
--- /dev/null
+++ b/spec/features/commits/user_uses_slash_commands_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'Commit > User uses quick actions', :js do
+ include Spec::Support::Helpers::Features::NotesHelpers
+ include RepoHelpers
+
+ let(:project) { create(:project, :public, :repository) }
+ let(:user) { project.creator }
+ let(:commit) { project.commit }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit project_commit_path(project, commit.id)
+ end
+
+ describe 'Tagging a commit' do
+ let(:tag_name) { 'v1.2.3' }
+ let(:tag_message) { 'Stable release' }
+ let(:truncated_commit_sha) { Commit.truncate_sha(commit.sha) }
+
+ it 'tags this commit' do
+ add_note("/tag #{tag_name} #{tag_message}")
+
+ expect(page).to have_content 'Commands applied'
+ expect(page).to have_content "tagged commit #{truncated_commit_sha}"
+ expect(page).to have_content tag_name
+
+ visit project_tag_path(project, tag_name)
+ expect(page).to have_content tag_name
+ expect(page).to have_content tag_message
+ expect(page).to have_content truncated_commit_sha
+ end
+
+ describe 'preview', :js do
+ it 'removes quick action from note and explains it' do
+ preview_note("/tag #{tag_name} #{tag_message}")
+
+ expect(page).not_to have_content '/tag'
+ expect(page).to have_content %{Tags this commit to #{tag_name} with "#{tag_message}"}
+ expect(page).to have_content tag_name
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
index c1608be402a..fd4175d5227 100644
--- a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
+++ b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb
@@ -28,7 +28,7 @@ describe 'Merge request > User sees MR with deleted source branch', :js do
click_on 'Changes'
wait_for_requests
- expect(page).to have_selector('.diffs.tab-pane .nothing-here-block')
+ expect(page).to have_selector('.diffs.tab-pane .file-holder')
expect(page).to have_content('Source branch does not exist.')
end
end
diff --git a/spec/features/projects/blobs/shortcuts_blob_spec.rb b/spec/features/projects/blobs/shortcuts_blob_spec.rb
index aeed38aeb76..3925de6cfb9 100644
--- a/spec/features/projects/blobs/shortcuts_blob_spec.rb
+++ b/spec/features/projects/blobs/shortcuts_blob_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Blob shortcuts' do
+describe 'Blob shortcuts', :js do
include TreeHelper
let(:project) { create(:project, :public, :repository) }
let(:path) { project.repository.ls_files(project.repository.root_ref)[0] }
@@ -18,6 +18,7 @@ describe 'Blob shortcuts' do
describe 'pressing "y"' do
it 'redirects to permalink with commit sha' do
visit_blob
+ wait_for_requests
find('body').native.send_key('y')
@@ -27,6 +28,7 @@ describe 'Blob shortcuts' do
it 'maintains fragment hash when redirecting' do
fragment = "L1"
visit_blob(fragment)
+ wait_for_requests
find('body').native.send_key('y')
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index f56174fc85c..f3cf3a282e5 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -210,9 +210,10 @@ describe "User browses files" do
end
end
- context "when browsing a file content" do
+ context "when browsing a file content", :js do
before do
visit(tree_path_root_ref)
+ wait_for_requests
click_link(".gitignore")
end
diff --git a/spec/features/projects/files/user_deletes_files_spec.rb b/spec/features/projects/files/user_deletes_files_spec.rb
index 0e9f83a16ce..dcb7b947c61 100644
--- a/spec/features/projects/files/user_deletes_files_spec.rb
+++ b/spec/features/projects/files/user_deletes_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Projects > Files > User deletes files' do
+describe 'Projects > Files > User deletes files', :js do
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
@@ -19,6 +19,7 @@ describe 'Projects > Files > User deletes files' do
before do
project.add_maintainer(user)
visit(project_tree_path_root_ref)
+ wait_for_requests
end
it 'deletes the file', :js do
@@ -35,10 +36,11 @@ describe 'Projects > Files > User deletes files' do
end
end
- context 'when an user does not have write access' do
+ context 'when an user does not have write access', :js do
before do
project2.add_reporter(user)
visit(project2_tree_path_root_ref)
+ wait_for_requests
end
it 'deletes the file in a forked project', :js do
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index ccc1bc1bc10..9eb65ec159c 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Projects > Files > User edits files' do
+describe 'Projects > Files > User edits files', :js do
include ProjectForksHelper
let(:project) { create(:project, :repository, name: 'Shop') }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
@@ -29,13 +29,14 @@ describe 'Projects > Files > User edits files' do
end
end
- context 'when an user has write access' do
+ context 'when an user has write access', :js do
before do
project.add_maintainer(user)
visit(project_tree_path_root_ref)
+ wait_for_requests
end
- it 'inserts a content of a file', :js do
+ it 'inserts a content of a file' do
click_link('.gitignore')
find('.js-edit-blob').click
find('.file-editor', match: :first)
@@ -49,13 +50,14 @@ describe 'Projects > Files > User edits files' do
it 'does not show the edit link if a file is binary' do
binary_file = File.join(project.repository.root_ref, 'files/images/logo-black.png')
visit(project_blob_path(project, binary_file))
+ wait_for_requests
page.within '.content' do
expect(page).not_to have_link('edit')
end
end
- it 'commits an edited file', :js do
+ it 'commits an edited file' do
click_link('.gitignore')
find('.js-edit-blob').click
find('.file-editor', match: :first)
@@ -72,7 +74,7 @@ describe 'Projects > Files > User edits files' do
expect(page).to have_content('*.rbca')
end
- it 'commits an edited file to a new branch', :js do
+ it 'commits an edited file to a new branch' do
click_link('.gitignore')
find('.js-edit-blob').click
@@ -91,7 +93,7 @@ describe 'Projects > Files > User edits files' do
expect(page).to have_content('*.rbca')
end
- it 'shows the diff of an edited file', :js do
+ it 'shows the diff of an edited file' do
click_link('.gitignore')
find('.js-edit-blob').click
find('.file-editor', match: :first)
@@ -106,13 +108,14 @@ describe 'Projects > Files > User edits files' do
it_behaves_like 'unavailable for an archived project'
end
- context 'when an user does not have write access' do
+ context 'when an user does not have write access', :js do
before do
project2.add_reporter(user)
visit(project2_tree_path_root_ref)
+ wait_for_requests
end
- it 'inserts a content of a file in a forked project', :js do
+ it 'inserts a content of a file in a forked project' do
click_link('.gitignore')
find('.js-edit-blob').click
@@ -134,7 +137,7 @@ describe 'Projects > Files > User edits files' do
expect(evaluate_script('ace.edit("editor").getValue()')).to eq('*.rbca')
end
- it 'commits an edited file in a forked project', :js do
+ it 'commits an edited file in a forked project' do
click_link('.gitignore')
find('.js-edit-blob').click
@@ -163,6 +166,7 @@ describe 'Projects > Files > User edits files' do
let!(:forked_project) { fork_project(project2, user, namespace: user.namespace, repository: true) }
before do
visit(project2_tree_path_root_ref)
+ wait_for_requests
end
it 'links to the forked project for editing' do
diff --git a/spec/features/projects/files/user_replaces_files_spec.rb b/spec/features/projects/files/user_replaces_files_spec.rb
index 3a81e77c4ba..e3da28d73c3 100644
--- a/spec/features/projects/files/user_replaces_files_spec.rb
+++ b/spec/features/projects/files/user_replaces_files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Projects > Files > User replaces files' do
+describe 'Projects > Files > User replaces files', :js do
include DropzoneHelper
let(:fork_message) do
@@ -21,9 +21,10 @@ describe 'Projects > Files > User replaces files' do
before do
project.add_maintainer(user)
visit(project_tree_path_root_ref)
+ wait_for_requests
end
- it 'replaces an existed file with a new one', :js do
+ it 'replaces an existed file with a new one' do
click_link('.gitignore')
expect(page).to have_content('.gitignore')
@@ -47,9 +48,10 @@ describe 'Projects > Files > User replaces files' do
before do
project2.add_reporter(user)
visit(project2_tree_path_root_ref)
+ wait_for_requests
end
- it 'replaces an existed file with a new one in a forked project', :js do
+ it 'replaces an existed file with a new one in a forked project' do
click_link('.gitignore')
expect(page).to have_content('.gitignore')
diff --git a/spec/features/projects/remote_mirror_spec.rb b/spec/features/projects/remote_mirror_spec.rb
index 5259a8942dc..33e9b73efe8 100644
--- a/spec/features/projects/remote_mirror_spec.rb
+++ b/spec/features/projects/remote_mirror_spec.rb
@@ -17,7 +17,7 @@ describe 'Project remote mirror', :feature do
visit project_mirror_path(project)
- expect(page).to have_content('The remote repository failed to update.')
+ expect_mirror_to_have_error_and_timeago('Never')
end
end
@@ -27,8 +27,14 @@ describe 'Project remote mirror', :feature do
visit project_mirror_path(project)
- expect(page).to have_content('The remote repository failed to update 5 minutes ago.')
+ expect_mirror_to_have_error_and_timeago('5 minutes ago')
end
end
+
+ def expect_mirror_to_have_error_and_timeago(timeago)
+ row = first('.js-mirrors-table-body tr')
+ expect(row).to have_content('Error')
+ expect(row).to have_content(timeago)
+ end
end
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index a0f5b234ebc..377a75cbcb3 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -129,9 +129,8 @@ describe 'Projects > Settings > Repository settings' do
visit project_settings_repository_path(project)
end
- it 'shows push mirror settings' do
- expect(page).to have_selector('#project_remote_mirrors_attributes_0_enabled')
- expect(page).to have_selector('#project_remote_mirrors_attributes_0_url')
+ it 'shows push mirror settings', :js do
+ expect(page).to have_selector('#mirror_direction')
end
end
end
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 5856bccb5b8..55ee87163f9 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -5,12 +5,67 @@ describe AvatarsHelper do
let(:user) { create(:user) }
- describe '#project_icon' do
- it 'returns an url for the avatar' do
- project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
+ describe '#project_icon & #group_icon' do
+ shared_examples 'resource with a default avatar' do |source_type|
+ it 'returns a default avatar div' do
+ expect(public_send("#{source_type}_icon", *helper_args))
+ .to match(%r{<div class="identicon bg\d+">F</div>})
+ end
+ end
+
+ shared_examples 'resource with a custom avatar' do |source_type|
+ it 'returns a custom avatar image' do
+ expect(public_send("#{source_type}_icon", *helper_args))
+ .to eq "<img src=\"#{resource.avatar.url}\" alt=\"Banana sample\" />"
+ end
+ end
+
+ context 'when providing a project' do
+ it_behaves_like 'resource with a default avatar', 'project' do
+ let(:resource) { create(:project, name: 'foo') }
+ let(:helper_args) { [resource] }
+ end
+
+ it_behaves_like 'resource with a custom avatar', 'project' do
+ let(:resource) { create(:project, :public, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource] }
+ end
+ end
+
+ context 'when providing a project path' do
+ it_behaves_like 'resource with a default avatar', 'project' do
+ let(:resource) { create(:project, name: 'foo') }
+ let(:helper_args) { [resource.full_path] }
+ end
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ it_behaves_like 'resource with a custom avatar', 'project' do
+ let(:resource) { create(:project, :public, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource.full_path] }
+ end
+ end
+
+ context 'when providing a group' do
+ it_behaves_like 'resource with a default avatar', 'group' do
+ let(:resource) { create(:group, name: 'foo') }
+ let(:helper_args) { [resource] }
+ end
+
+ it_behaves_like 'resource with a custom avatar', 'group' do
+ let(:resource) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource] }
+ end
+ end
+
+ context 'when providing a group path' do
+ it_behaves_like 'resource with a default avatar', 'group' do
+ let(:resource) { create(:group, name: 'foo') }
+ let(:helper_args) { [resource.full_path] }
+ end
+
+ it_behaves_like 'resource with a custom avatar', 'group' do
+ let(:resource) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
+ let(:helper_args) { [resource.full_path] }
+ end
end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 115807f954b..540a8674ec2 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -3,19 +3,6 @@ require 'spec_helper'
describe GroupsHelper do
include ApplicationHelper
- describe 'group_icon' do
- it 'returns an url for the avatar' do
- avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif')
-
- group = create(:group)
- group.avatar = fixture_file_upload(avatar_file_path)
- group.save!
-
- expect(helper.group_icon(group).to_s)
- .to eq "<img data-src=\"#{group.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- end
- end
-
describe 'group_icon_url' do
it 'returns an url for the avatar' do
avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif')
diff --git a/spec/helpers/icons_helper_spec.rb b/spec/helpers/icons_helper_spec.rb
index 82f588d1a08..4b40d523287 100644
--- a/spec/helpers/icons_helper_spec.rb
+++ b/spec/helpers/icons_helper_spec.rb
@@ -80,6 +80,26 @@ describe IconsHelper do
end
end
+ describe 'audit icon' do
+ it 'returns right icon name for standard auth' do
+ icon_name = 'standard'
+ expect(audit_icon(icon_name).to_s)
+ .to eq '<i class="fa fa-key"></i>'
+ end
+
+ it 'returns right icon name for two-factor auth' do
+ icon_name = 'two-factor'
+ expect(audit_icon(icon_name).to_s)
+ .to eq '<i class="fa fa-key"></i>'
+ end
+
+ it 'returns right icon name for google_oauth2 auth' do
+ icon_name = 'google_oauth2'
+ expect(audit_icon(icon_name).to_s)
+ .to eq '<i class="fa fa-google"></i>'
+ end
+ end
+
describe 'file_type_icon_class' do
it 'returns folder class' do
expect(file_type_icon_class('folder', 0, 'folder_name')).to eq 'folder'
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 7a4616ec8eb..44a38f7ca82 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -22,11 +22,18 @@ describe('DiffFile', () => {
expect(el.id).toEqual(fileHash);
expect(el.classList.contains('diff-file')).toEqual(true);
+
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
expect(el.querySelector('.js-file-title')).toBeDefined();
expect(el.querySelector('.file-title-name').innerText.indexOf(filePath) > -1).toEqual(true);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
- expect(el.querySelectorAll('.line_content').length > 5).toEqual(true);
+
+ expect(vm.file.renderIt).toEqual(false);
+ vm.file.renderIt = true;
+
+ vm.$nextTick(() => {
+ expect(el.querySelectorAll('.line_content').length > 5).toEqual(true);
+ });
});
describe('collapsed', () => {
@@ -34,6 +41,7 @@ describe('DiffFile', () => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1);
expect(vm.file.collapsed).toEqual(false);
vm.file.collapsed = true;
+ vm.file.renderIt = true;
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0);
diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js
index d3bf9525924..cce36ecc91f 100644
--- a/spec/javascripts/diffs/mock_data/diff_file.js
+++ b/spec/javascripts/diffs/mock_data/diff_file.js
@@ -39,6 +39,7 @@ export default {
viewPath: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG',
replacedViewPath: null,
collapsed: false,
+ renderIt: false,
tooLarge: false,
contextLinesPath:
'/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff',
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 1af49f4985c..8f89984c6e5 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -1,6 +1,7 @@
import mutations from '~/diffs/store/mutations';
import * as types from '~/diffs/store/mutation_types';
import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
+import diffFileMockData from '../mock_data/diff_file';
describe('DiffsStoreMutations', () => {
describe('SET_BASE_CONFIG', () => {
@@ -24,6 +25,23 @@ describe('DiffsStoreMutations', () => {
});
});
+ describe('SET_DIFF_DATA', () => {
+ it('should set diff data type properly', () => {
+ const state = {};
+ const diffMock = {
+ diff_files: [diffFileMockData],
+ };
+
+ mutations[types.SET_DIFF_DATA](state, diffMock);
+
+ const firstLine = state.diffFiles[0].parallelDiffLines[0];
+
+ expect(firstLine.right.text).toBeUndefined();
+ expect(state.diffFiles[0].renderIt).toEqual(true);
+ expect(state.diffFiles[0].collapsed).toEqual(false);
+ });
+ });
+
describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => {
const state = {};
diff --git a/spec/javascripts/ide/components/new_dropdown/button_spec.js b/spec/javascripts/ide/components/new_dropdown/button_spec.js
index ef083d06ba7..6a326b5bd92 100644
--- a/spec/javascripts/ide/components/new_dropdown/button_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/button_spec.js
@@ -46,4 +46,20 @@ describe('IDE new entry dropdown button component', () => {
done();
});
});
+
+ describe('tooltipTitle', () => {
+ it('returns empty string when showLabel is true', () => {
+ expect(vm.tooltipTitle).toBe('');
+ });
+
+ it('returns label', done => {
+ vm.showLabel = false;
+
+ vm.$nextTick(() => {
+ expect(vm.tooltipTitle).toBe('Testing');
+
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/preview/clientside_spec.js b/spec/javascripts/ide/components/preview/clientside_spec.js
index 3ec65882418..d6983f5a3b8 100644
--- a/spec/javascripts/ide/components/preview/clientside_spec.js
+++ b/spec/javascripts/ide/components/preview/clientside_spec.js
@@ -292,6 +292,8 @@ describe('IDE clientside preview', () => {
describe('update', () => {
beforeEach(() => {
jasmine.clock().install();
+
+ vm.sandpackReady = true;
vm.manager.updatePreview = jasmine.createSpy('updatePreview');
vm.manager.listener = jasmine.createSpy('updatePreview');
});
@@ -306,7 +308,7 @@ describe('IDE clientside preview', () => {
vm.update();
- jasmine.clock().tick(500);
+ jasmine.clock().tick(250);
expect(vm.initPreview).toHaveBeenCalled();
});
@@ -314,7 +316,7 @@ describe('IDE clientside preview', () => {
it('calls updatePreview', () => {
vm.update();
- jasmine.clock().tick(500);
+ jasmine.clock().tick(250);
expect(vm.manager.updatePreview).toHaveBeenCalledWith(vm.sandboxOpts);
});
diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js
index 90c28c769f7..8564f04ce8a 100644
--- a/spec/javascripts/ide/stores/actions/merge_request_spec.js
+++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js
@@ -1,12 +1,14 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
-import {
+import actions, {
getMergeRequestData,
getMergeRequestChanges,
getMergeRequestVersions,
+ openMergeRequest,
} from '~/ide/stores/actions/merge_request';
import service from '~/ide/services';
+import { activityBarViews } from '~/ide/constants';
import { resetStore } from '../../helpers';
describe('IDE store merge request actions', () => {
@@ -238,4 +240,101 @@ describe('IDE store merge request actions', () => {
});
});
});
+
+ describe('openMergeRequest', () => {
+ const mr = {
+ projectId: 'abcproject',
+ targetProjectId: 'defproject',
+ mergeRequestId: 2,
+ };
+ let testMergeRequest;
+ let testMergeRequestChanges;
+
+ beforeEach(() => {
+ testMergeRequest = {
+ source_branch: 'abcbranch',
+ };
+ testMergeRequestChanges = {
+ changes: [],
+ };
+ store.state.entries = {
+ foo: {},
+ bar: {},
+ };
+
+ spyOn(store, 'dispatch').and.callFake((type) => {
+ switch (type) {
+ case 'getMergeRequestData':
+ return Promise.resolve(testMergeRequest);
+ case 'getMergeRequestChanges':
+ return Promise.resolve(testMergeRequestChanges);
+ default:
+ return Promise.resolve();
+ }
+ });
+ });
+
+ it('dispatch actions for merge request data', done => {
+ openMergeRequest(store, mr)
+ .then(() => {
+ expect(store.dispatch.calls.allArgs()).toEqual([
+ ['getMergeRequestData', mr],
+ ['setCurrentBranchId', testMergeRequest.source_branch],
+ ['getBranchData', {
+ projectId: mr.projectId,
+ branchId: testMergeRequest.source_branch,
+ }],
+ ['getFiles', {
+ projectId: mr.projectId,
+ branchId: testMergeRequest.source_branch,
+ }],
+ ['getMergeRequestVersions', mr],
+ ['getMergeRequestChanges', mr],
+ ]);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('updates activity bar view and gets file data, if changes are found', done => {
+ testMergeRequestChanges.changes = [
+ { new_path: 'foo' },
+ { new_path: 'bar' },
+ ];
+
+ openMergeRequest(store, mr)
+ .then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('updateActivityBarView', activityBarViews.review);
+
+ testMergeRequestChanges.changes.forEach((change, i) => {
+ expect(store.dispatch).toHaveBeenCalledWith('setFileMrChange', {
+ file: store.state.entries[change.new_path],
+ mrChange: change,
+ });
+ expect(store.dispatch).toHaveBeenCalledWith('getFileData', {
+ path: change.new_path,
+ makeFileActive: i === 0,
+ });
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('flashes message, if error', done => {
+ const flashSpy = spyOnDependency(actions, 'flash');
+ store.dispatch.and.returnValue(Promise.reject());
+
+ openMergeRequest(store, mr)
+ .then(() => {
+ fail('Expected openMergeRequest to throw an error');
+ })
+ .catch(() => {
+ expect(flashSpy).toHaveBeenCalledWith(jasmine.any(String));
+ })
+ .then(done)
+ .catch(done.fail);
+
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
index 6a85968e199..667e3e0a7ef 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -5,6 +5,7 @@ import {
showBranchNotFoundError,
createNewBranchFromDefault,
getBranchData,
+ openBranch,
} from '~/ide/stores/actions';
import store from '~/ide/stores';
import service from '~/ide/services';
@@ -224,4 +225,55 @@ describe('IDE store project actions', () => {
});
});
});
+
+ describe('openBranch', () => {
+ const branch = {
+ projectId: 'feature/lorem-ipsum',
+ branchId: '123-lorem',
+ };
+
+ beforeEach(() => {
+ store.state.entries = {
+ foo: { pending: false },
+ 'foo/bar-pending': { pending: true },
+ 'foo/bar': { pending: false },
+ };
+
+ spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
+ });
+
+ it('dispatches branch actions', done => {
+ openBranch(store, branch)
+ .then(() => {
+ expect(store.dispatch.calls.allArgs()).toEqual([
+ ['setCurrentBranchId', branch.branchId],
+ ['getBranchData', branch],
+ ['getFiles', branch],
+ ]);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('handles tree entry action, if basePath is given', done => {
+ openBranch(store, { ...branch, basePath: 'foo/bar/' })
+ .then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith(
+ 'handleTreeEntryAction',
+ store.state.entries['foo/bar'],
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not handle tree entry action, if entry is pending', done => {
+ openBranch(store, { ...branch, basePath: 'foo/bar-pending' })
+ .then(() => {
+ expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything());
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/modules/branches/actions_spec.js b/spec/javascripts/ide/stores/modules/branches/actions_spec.js
index a0fce578958..010f56af03b 100644
--- a/spec/javascripts/ide/stores/modules/branches/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/branches/actions_spec.js
@@ -9,7 +9,6 @@ import {
receiveBranchesSuccess,
fetchBranches,
resetBranches,
- openBranch,
} from '~/ide/stores/modules/branches/actions';
import { branches, projectData } from '../../../mock_data';
@@ -174,20 +173,5 @@ describe('IDE branches actions', () => {
);
});
});
-
- describe('openBranch', () => {
- it('dispatches goToRoute action with path', done => {
- const branchId = branches[0].name;
- const expectedPath = `/project/${projectData.name_with_namespace}/edit/${branchId}`;
- testAction(
- openBranch,
- branchId,
- mockedState,
- [],
- [{ type: 'goToRoute', payload: expectedPath }],
- done,
- );
- });
- });
});
});
diff --git a/spec/javascripts/jobs/artifacts_block_spec.js b/spec/javascripts/jobs/artifacts_block_spec.js
new file mode 100644
index 00000000000..c544c6f3e89
--- /dev/null
+++ b/spec/javascripts/jobs/artifacts_block_spec.js
@@ -0,0 +1,120 @@
+import Vue from 'vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+import component from '~/jobs/components/artifacts_block.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Artifacts block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const expireAt = '2018-08-14T09:38:49.157Z';
+ const timeago = getTimeago();
+ const formatedDate = timeago.format(expireAt);
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with expired artifacts', () => {
+ it('renders expired artifact date and info', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ });
+
+ expect(vm.$el.querySelector('.js-artifacts-removed')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).toBeNull();
+ expect(vm.$el.textContent).toContain(formatedDate);
+ });
+ });
+
+ describe('with artifacts that will expire', () => {
+ it('renders will expire artifact date and info', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: false,
+ willArtifactsExpire: true,
+ expireAt,
+ });
+
+ expect(vm.$el.querySelector('.js-artifacts-removed')).toBeNull();
+ expect(vm.$el.querySelector('.js-artifacts-will-be-removed')).not.toBeNull();
+ expect(vm.$el.textContent).toContain(formatedDate);
+ });
+ });
+
+ describe('when the user can keep the artifacts', () => {
+ it('renders the keep button', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ keepArtifactsPath: '/keep',
+ });
+
+ expect(vm.$el.querySelector('.js-keep-artifacts')).not.toBeNull();
+ });
+ });
+
+ describe('when the user can not keep the artifacts', () => {
+ it('does not render the keep button', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ });
+
+ expect(vm.$el.querySelector('.js-keep-artifacts')).toBeNull();
+ });
+ });
+
+ describe('when the user can download the artifacts', () => {
+ it('renders the download button', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ downloadArtifactsPath: '/download',
+ });
+
+ expect(vm.$el.querySelector('.js-download-artifacts')).not.toBeNull();
+ });
+ });
+
+ describe('when the user can not download the artifacts', () => {
+ it('does not render the keep button', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ });
+
+ expect(vm.$el.querySelector('.js-download-artifacts')).toBeNull();
+ });
+ });
+
+ describe('when the user can browse the artifacts', () => {
+ it('does not render the browse button', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ browseArtifactsPath: '/browse',
+ });
+
+ expect(vm.$el.querySelector('.js-browse-artifacts')).not.toBeNull();
+ });
+ });
+
+ describe('when the user can not browse the artifacts', () => {
+ it('does not render the browse button', () => {
+ vm = mountComponent(Component, {
+ haveArtifactsExpired: true,
+ willArtifactsExpire: false,
+ expireAt,
+ });
+
+ expect(vm.$el.querySelector('.js-browse-artifacts')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/erased_block_spec.js b/spec/javascripts/jobs/erased_block_spec.js
new file mode 100644
index 00000000000..7cf32e984a2
--- /dev/null
+++ b/spec/javascripts/jobs/erased_block_spec.js
@@ -0,0 +1,56 @@
+import Vue from 'vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+import component from '~/jobs/components/erased_block.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Erased block', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const erasedAt = '2016-11-07T11:11:16.525Z';
+ const timeago = getTimeago();
+ const formatedDate = timeago.format(erasedAt);
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with job erased by user', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ erasedByUser: true,
+ username: 'root',
+ linkToUser: 'gitlab.com/root',
+ erasedAt,
+ });
+ });
+
+ it('renders username and link', () => {
+ expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('gitlab.com/root');
+
+ expect(vm.$el.textContent).toContain('Job has been erased by');
+ expect(vm.$el.textContent).toContain('root');
+ });
+
+ it('renders erasedAt', () => {
+ expect(vm.$el.textContent).toContain(formatedDate);
+ });
+ });
+
+ describe('with erased job', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ erasedByUser: false,
+ erasedAt,
+ });
+ });
+
+ it('renders username and link', () => {
+ expect(vm.$el.textContent).toContain('Job has been erased');
+ });
+
+ it('renders erasedAt', () => {
+ expect(vm.$el.textContent).toContain(formatedDate);
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/job_log_spec.js b/spec/javascripts/jobs/job_log_spec.js
new file mode 100644
index 00000000000..406f1c4ccc5
--- /dev/null
+++ b/spec/javascripts/jobs/job_log_spec.js
@@ -0,0 +1,45 @@
+import Vue from 'vue';
+import component from '~/jobs/components/job_log.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Job Log', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const trace = 'Running with gitlab-runner 11.1.0 (081978aa)<br> on docker-auto-scale-com d5ae8d25<br>Using Docker executor with image dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29 ...<br>';
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders provided trace', () => {
+ vm = mountComponent(Component, {
+ trace,
+ isReceivingBuildTrace: true,
+ });
+
+ expect(vm.$el.querySelector('code').textContent).toContain('Running with gitlab-runner 11.1.0 (081978aa)');
+ });
+
+ describe('while receiving trace', () => {
+ it('renders animation', () => {
+ vm = mountComponent(Component, {
+ trace,
+ isReceivingBuildTrace: true,
+ });
+
+ expect(vm.$el.querySelector('.js-log-animation')).not.toBeNull();
+ });
+ });
+
+ describe('when build trace has finishes', () => {
+ it('does not render animation', () => {
+ vm = mountComponent(Component, {
+ trace,
+ isReceivingBuildTrace: false,
+ });
+
+ expect(vm.$el.querySelector('.js-log-animation')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/sidebar_details_block_spec.js b/spec/javascripts/jobs/sidebar_details_block_spec.js
index 9c4454252ce..21ef5650b80 100644
--- a/spec/javascripts/jobs/sidebar_details_block_spec.js
+++ b/spec/javascripts/jobs/sidebar_details_block_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import sidebarDetailsBlock from '~/jobs/components/sidebar_details_block.vue';
import job from './mock_data';
+import mountComponent from '../helpers/vue_mount_component_helper';
describe('Sidebar details block', () => {
let SidebarComponent;
@@ -20,39 +21,53 @@ describe('Sidebar details block', () => {
describe('when it is loading', () => {
it('should render a loading spinner', () => {
- vm = new SidebarComponent({
- propsData: {
- job: {},
- isLoading: true,
- },
- }).$mount();
-
+ vm = mountComponent(SidebarComponent, {
+ job: {},
+ isLoading: true,
+ });
expect(vm.$el.querySelector('.fa-spinner')).toBeDefined();
});
});
- describe("when user can't retry", () => {
+ describe('when there is no retry path retry', () => {
it('should not render a retry button', () => {
- vm = new SidebarComponent({
- propsData: {
- job: {},
- canUserRetry: false,
- isLoading: true,
- },
- }).$mount();
+ vm = mountComponent(SidebarComponent, {
+ job: {},
+ isLoading: false,
+ });
expect(vm.$el.querySelector('.js-retry-job')).toBeNull();
});
});
- beforeEach(() => {
- vm = new SidebarComponent({
- propsData: {
+ describe('without terminal path', () => {
+ it('does not render terminal link', () => {
+ vm = mountComponent(SidebarComponent, {
job,
- canUserRetry: true,
isLoading: false,
- },
- }).$mount();
+ });
+
+ expect(vm.$el.querySelector('.js-terminal-link')).toBeNull();
+ });
+ });
+
+ describe('with terminal path', () => {
+ it('renders terminal link', () => {
+ vm = mountComponent(SidebarComponent, {
+ job,
+ isLoading: false,
+ terminalPath: 'job/43123/terminal',
+ });
+
+ expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull();
+ });
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(SidebarComponent, {
+ job,
+ isLoading: false,
+ });
});
describe('actions', () => {
@@ -102,13 +117,15 @@ describe('Sidebar details block', () => {
});
it('should render runner ID', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-runner'))).toEqual('Runner: local ci runner (#1)');
+ expect(trimWhitespace(vm.$el.querySelector('.js-job-runner'))).toEqual(
+ 'Runner: local ci runner (#1)',
+ );
});
it('should render timeout information', () => {
- expect(
- trimWhitespace(vm.$el.querySelector('.js-job-timeout')),
- ).toEqual('Timeout: 1m 40s (from runner)');
+ expect(trimWhitespace(vm.$el.querySelector('.js-job-timeout'))).toEqual(
+ 'Timeout: 1m 40s (from runner)',
+ );
});
it('should render coverage', () => {
diff --git a/spec/javascripts/jobs/stuck_block_spec.js b/spec/javascripts/jobs/stuck_block_spec.js
new file mode 100644
index 00000000000..4e2108dfdfb
--- /dev/null
+++ b/spec/javascripts/jobs/stuck_block_spec.js
@@ -0,0 +1,81 @@
+import Vue from 'vue';
+import component from '~/jobs/components/stuck_block.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
+
+describe('Stuck Block Job component', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('with no runners for project', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ hasNoRunnersForProject: true,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders only information about project not having runners', () => {
+ expect(vm.$el.querySelector('.js-stuck-no-runners')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-with-tags')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-no-active-runner')).toBeNull();
+ });
+
+ it('renders link to runners page', () => {
+ expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+
+ describe('with tags', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ hasNoRunnersForProject: false,
+ tags: ['docker', 'gitlab-org'],
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders information about the tags not being set', () => {
+ expect(vm.$el.querySelector('.js-stuck-no-runners')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-with-tags')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-no-active-runner')).toBeNull();
+ });
+
+ it('renders tags', () => {
+ expect(vm.$el.textContent).toContain('docker');
+ expect(vm.$el.textContent).toContain('gitlab-org');
+ });
+
+ it('renders link to runners page', () => {
+ expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+
+ describe('without active runners', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ hasNoRunnersForProject: false,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders information about project not having runners', () => {
+ expect(vm.$el.querySelector('.js-stuck-no-runners')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-with-tags')).toBeNull();
+ expect(vm.$el.querySelector('.js-stuck-no-active-runner')).not.toBeNull();
+ });
+
+ it('renders link to runners page', () => {
+ expect(vm.$el.querySelector('.js-runners-path').getAttribute('href')).toEqual(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js b/spec/javascripts/reports/components/modal_open_name_spec.js
index 8635203c413..b18b3ef03d1 100644
--- a/spec/javascripts/vue_shared/components/reports/modal_open_name_spec.js
+++ b/spec/javascripts/reports/components/modal_open_name_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Vuex from 'vuex';
-import component from '~/vue_shared/components/reports/modal_open_name.vue';
+import component from '~/reports/components/modal_open_name.vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
describe('Modal open name', () => {
diff --git a/spec/javascripts/vue_shared/components/reports/report_link_spec.js b/spec/javascripts/reports/components/report_link_spec.js
index a4691f3712f..cd6911e2f59 100644
--- a/spec/javascripts/vue_shared/components/reports/report_link_spec.js
+++ b/spec/javascripts/reports/components/report_link_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import component from '~/vue_shared/components/reports/report_link.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import component from '~/reports/components/report_link.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
describe('report link', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/reports/report_section_spec.js b/spec/javascripts/reports/components/report_section_spec.js
index 4e3986acb16..6f6eb161d14 100644
--- a/spec/javascripts/vue_shared/components/reports/report_section_spec.js
+++ b/spec/javascripts/reports/components/report_section_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import reportSection from '~/vue_shared/components/reports/report_section.vue';
+import reportSection from '~/reports/components/report_section.vue';
import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper';
describe('Report section', () => {
diff --git a/spec/javascripts/vue_shared/components/reports/summary_row_spec.js b/spec/javascripts/reports/components/summary_row_spec.js
index ac076f05bc0..fab7693581c 100644
--- a/spec/javascripts/vue_shared/components/reports/summary_row_spec.js
+++ b/spec/javascripts/reports/components/summary_row_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import component from '~/vue_shared/components/reports/summary_row.vue';
+import component from '~/reports/components/summary_row.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Summary row', () => {
diff --git a/spec/lib/banzai/filter/project_reference_filter_spec.rb b/spec/lib/banzai/filter/project_reference_filter_spec.rb
new file mode 100644
index 00000000000..48140305e26
--- /dev/null
+++ b/spec/lib/banzai/filter/project_reference_filter_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Filter::ProjectReferenceFilter do
+ include FilterSpecHelper
+
+ def invalidate_reference(reference)
+ "#{reference.reverse}"
+ end
+
+ def get_reference(project)
+ project.to_reference_with_postfix
+ end
+
+ let(:project) { create(:project, :public) }
+ subject { project }
+ let(:subject_name) { "project" }
+ let(:reference) { get_reference(project) }
+
+ it_behaves_like 'user reference or project reference'
+
+ it 'ignores invalid projects' do
+ exp = act = "Hey #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq(CGI.escapeHTML(exp))
+ end
+
+ it 'allows references with text after the > character' do
+ doc = reference_filter("Hey #{reference}foo")
+ expect(doc.css('a').first.attr('href')).to eq urls.project_url(subject)
+ end
+
+ %w(pre code a style).each do |elem|
+ it "ignores valid references contained inside '#{elem}' element" do
+ exp = act = "<#{elem}>Hey #{CGI.escapeHTML(reference)}</#{elem}>"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ it 'includes default classes' do
+ doc = reference_filter("Hey #{reference}")
+ expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project has-tooltip'
+ end
+
+ context 'in group context' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+
+ let(:nested_group) { create(:group, :nested) }
+ let(:nested_project) { create(:project, group: nested_group) }
+
+ it 'supports mentioning a project' do
+ reference = get_reference(project)
+ doc = reference_filter("Hey #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.project_url(project)
+ end
+
+ it 'supports mentioning a project in a nested group' do
+ reference = get_reference(nested_project)
+ doc = reference_filter("Hey #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.project_url(nested_project)
+ end
+ end
+
+ describe '#projects_hash' do
+ it 'returns a Hash containing all Projects' do
+ document = Nokogiri::HTML.fragment("<p>#{get_reference(project)}</p>")
+ filter = described_class.new(document, project: project)
+
+ expect(filter.projects_hash).to eq({ project.full_path => project })
+ end
+ end
+
+ describe '#projects' do
+ it 'returns the projects mentioned in a document' do
+ document = Nokogiri::HTML.fragment("<p>#{get_reference(project)}</p>")
+ filter = described_class.new(document, project: project)
+
+ expect(filter.projects).to eq([project.full_path])
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index 2f86a046d28..334d29a5368 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -3,9 +3,17 @@ require 'spec_helper'
describe Banzai::Filter::UserReferenceFilter do
include FilterSpecHelper
+ def get_reference(user)
+ user.to_reference
+ end
+
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
- let(:reference) { user.to_reference }
+ subject { user }
+ let(:subject_name) { "user" }
+ let(:reference) { get_reference(user) }
+
+ it_behaves_like 'user reference or project reference'
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
@@ -66,45 +74,6 @@ describe Banzai::Filter::UserReferenceFilter do
end
end
- context 'mentioning a user' do
- it_behaves_like 'a reference containing an element node'
-
- it 'links to a User' do
- doc = reference_filter("Hey #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
- end
-
- it 'links to a User with a period' do
- user = create(:user, name: 'alphA.Beta')
-
- doc = reference_filter("Hey #{user.to_reference}")
- expect(doc.css('a').length).to eq 1
- end
-
- it 'links to a User with an underscore' do
- user = create(:user, name: 'ping_pong_king')
-
- doc = reference_filter("Hey #{user.to_reference}")
- expect(doc.css('a').length).to eq 1
- end
-
- it 'links to a User with different case-sensitivity' do
- user = create(:user, username: 'RescueRanger')
-
- doc = reference_filter("Hey #{user.to_reference.upcase}")
- expect(doc.css('a').length).to eq 1
- expect(doc.css('a').text).to eq(user.to_reference)
- end
-
- it 'includes a data-user attribute' do
- doc = reference_filter("Hey #{reference}")
- link = doc.css('a').first
-
- expect(link).to have_attribute('data-user')
- expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
- end
- end
-
context 'mentioning a group' do
it_behaves_like 'a reference containing an element node'
@@ -154,36 +123,6 @@ describe Banzai::Filter::UserReferenceFilter do
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member has-tooltip'
end
- it 'supports an :only_path context' do
- doc = reference_filter("Hey #{reference}", only_path: true)
- link = doc.css('a').first.attr('href')
-
- expect(link).not_to match %r(https?://)
- expect(link).to eq urls.user_path(user)
- end
-
- context 'referencing a user in a link href' do
- let(:reference) { %Q{<a href="#{user.to_reference}">User</a>} }
-
- it 'links to a User' do
- doc = reference_filter("Hey #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
- end
-
- it 'links with adjacent text' do
- doc = reference_filter("Mention me (#{reference}.)")
- expect(doc.to_html).to match(%r{\(<a.+>User</a>\.\)})
- end
-
- it 'includes a data-user attribute' do
- doc = reference_filter("Hey #{reference}")
- link = doc.css('a').first
-
- expect(link).to have_attribute('data-user')
- expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
- end
- end
-
context 'when a project is not specified' do
let(:project) { nil }
@@ -227,7 +166,7 @@ describe Banzai::Filter::UserReferenceFilter do
end
it 'supports mentioning a single user' do
- reference = group_member.to_reference
+ reference = get_reference(group_member)
doc = reference_filter("Hey #{reference}", context)
expect(doc.css('a').first.attr('href')).to eq urls.user_url(group_member)
@@ -243,7 +182,7 @@ describe Banzai::Filter::UserReferenceFilter do
describe '#namespaces' do
it 'returns a Hash containing all Namespaces' do
- document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>")
+ document = Nokogiri::HTML.fragment("<p>#{get_reference(user)}</p>")
filter = described_class.new(document, project: project)
ns = user.namespace
@@ -253,7 +192,7 @@ describe Banzai::Filter::UserReferenceFilter do
describe '#usernames' do
it 'returns the usernames mentioned in a document' do
- document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>")
+ document = Nokogiri::HTML.fragment("<p>#{get_reference(user)}</p>")
filter = described_class.new(document, project: project)
expect(filter.usernames).to eq([user.username])
diff --git a/spec/lib/banzai/reference_parser/project_parser_spec.rb b/spec/lib/banzai/reference_parser/project_parser_spec.rb
new file mode 100644
index 00000000000..e4936aa9e57
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/project_parser_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::ProjectParser do
+ include ReferenceParserHelpers
+
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ subject { described_class.new(Banzai::RenderContext.new(project, user)) }
+ let(:link) { empty_html_link }
+
+ describe '#referenced_by' do
+ describe 'when the link has a data-project attribute' do
+ context 'using an existing project ID' do
+ it 'returns an Array of projects' do
+ link['data-project'] = project.id.to_s
+
+ expect(subject.gather_references([link])).to eq([project])
+ end
+ end
+
+ context 'using a non-existing project ID' do
+ it 'returns an empty Array' do
+ link['data-project'] = ''
+
+ expect(subject.gather_references([link])).to eq([])
+ end
+ end
+
+ context 'using a private project ID' do
+ it 'returns an empty Array when unauthorized' do
+ private_project = create(:project, :private)
+
+ link['data-project'] = private_project.id.to_s
+
+ expect(subject.gather_references([link])).to eq([])
+ end
+
+ it 'returns an Array when authorized' do
+ private_project = create(:project, :private, namespace: user.namespace)
+
+ link['data-project'] = private_project.id.to_s
+
+ expect(subject.gather_references([link])).to eq([private_project])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 05c232d22cf..7a681bc6610 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -69,6 +69,7 @@ describe Gitlab::BitbucketImport::Importer do
let(:project) do
create(
:project,
+ :repository,
import_source: project_identifier,
import_url: "https://bitbucket.org/#{project_identifier}.git",
import_data_attributes: { credentials: data }
@@ -85,10 +86,84 @@ describe Gitlab::BitbucketImport::Importer do
}
end
+ let(:sample) { RepoHelpers.sample_compare }
+
before do
allow(importer).to receive(:gitlab_shell) { gitlab_shell }
end
+ subject { described_class.new(project) }
+
+ describe '#import_pull_requests' do
+ before do
+ allow(subject).to receive(:import_wiki)
+ allow(subject).to receive(:import_issues)
+
+ pull_request = instance_double(
+ Bitbucket::Representation::PullRequest,
+ iid: 10,
+ source_branch_sha: sample.commits.last,
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: sample.commits.first,
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'other',
+ created_at: Time.now,
+ updated_at: Time.now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ @inline_note = instance_double(
+ Bitbucket::Representation::PullRequestComment,
+ iid: 2,
+ file_path: '.gitmodules',
+ old_pos: nil,
+ new_pos: 4,
+ note: 'Hello world',
+ author: 'root',
+ created_at: Time.now,
+ updated_at: Time.now,
+ inline?: true,
+ has_parent?: false)
+
+ @reply = instance_double(
+ Bitbucket::Representation::PullRequestComment,
+ iid: 3,
+ file_path: '.gitmodules',
+ note: 'Hello world',
+ author: 'root',
+ created_at: Time.now,
+ updated_at: Time.now,
+ inline?: true,
+ has_parent?: true,
+ parent_id: 2)
+
+ comments = [@inline_note, @reply]
+
+ allow(subject.client).to receive(:repo)
+ allow(subject.client).to receive(:pull_requests).and_return([pull_request])
+ allow(subject.client).to receive(:pull_request_comments).with(anything, pull_request.iid).and_return(comments)
+ end
+
+ it 'imports threaded discussions' do
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
+
+ notes = merge_request.notes.order(:id).to_a
+ start_note = notes.first
+ expect(start_note).to be_a(DiffNote)
+ expect(start_note.note).to eq(@inline_note.note)
+
+ reply_note = notes.last
+ expect(reply_note).to be_a(DiffNote)
+ expect(reply_note.note).to eq(@reply.note)
+ end
+ end
+
context 'issues statuses' do
before do
# HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this
diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
index 37b38776775..11e605eece6 100644
--- a/spec/lib/gitlab/cleanup/project_uploads_spec.rb
+++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
@@ -244,9 +244,11 @@ describe Gitlab::Cleanup::ProjectUploads do
orphaned1 = create(:upload, :personal_snippet_upload, :with_file)
orphaned2 = create(:upload, :namespace_upload, :with_file)
orphaned3 = create(:upload, :attachment_upload, :with_file)
+ orphaned4 = create(:upload, :favicon_upload, :with_file)
paths << orphaned1.absolute_path
paths << orphaned2.absolute_path
paths << orphaned3.absolute_path
+ paths << orphaned4.absolute_path
Upload.delete_all
expect(logger).not_to receive(:info).with(/move|fix/i)
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index eb7148ff108..4e83b27e4a5 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -48,10 +48,10 @@ describe Gitlab::Database::MigrationHelpers do
allow(model).to receive(:transaction_open?).and_return(false)
end
- context 'using PostgreSQL' do
+ context 'using PostgreSQL', :postgresql do
before do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- allow(model).to receive(:disable_statement_timeout)
+ allow(model).to receive(:disable_statement_timeout).and_call_original
end
it 'creates the index concurrently' do
@@ -114,12 +114,12 @@ describe Gitlab::Database::MigrationHelpers do
before do
allow(model).to receive(:transaction_open?).and_return(false)
allow(model).to receive(:index_exists?).and_return(true)
+ allow(model).to receive(:disable_statement_timeout).and_call_original
end
context 'using PostgreSQL' do
before do
allow(model).to receive(:supports_drop_index_concurrently?).and_return(true)
- allow(model).to receive(:disable_statement_timeout)
end
describe 'by column name' do
@@ -162,7 +162,7 @@ describe Gitlab::Database::MigrationHelpers do
context 'using MySQL' do
it 'removes an index' do
- expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false).twice
expect(model).to receive(:remove_index)
.with(:users, { column: :foo })
@@ -224,21 +224,26 @@ describe Gitlab::Database::MigrationHelpers do
context 'using PostgreSQL' do
before do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
allow(Gitlab::Database).to receive(:mysql?).and_return(false)
end
it 'creates a concurrent foreign key and validates it' do
- expect(model).to receive(:disable_statement_timeout)
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
end
it 'appends a valid ON DELETE statement' do
- expect(model).to receive(:disable_statement_timeout)
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/ON DELETE SET NULL/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
model.add_concurrent_foreign_key(:projects, :users,
column: :user_id,
@@ -291,13 +296,68 @@ describe Gitlab::Database::MigrationHelpers do
describe '#disable_statement_timeout' do
context 'using PostgreSQL' do
- it 'disables statement timeouts' do
+ it 'disables statement timeouts to current transaction only' do
expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
- expect(model).to receive(:execute).with('SET statement_timeout TO 0')
+ expect(model).to receive(:execute).with('SET LOCAL statement_timeout TO 0')
model.disable_statement_timeout
end
+
+ # this specs runs without an enclosing transaction (:delete truncation method for db_cleaner)
+ context 'with real environment', :postgresql, :delete do
+ before do
+ model.execute("SET statement_timeout TO '20000'")
+ end
+
+ after do
+ model.execute('RESET ALL')
+ end
+
+ it 'defines statement to 0 only for current transaction' do
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('20s')
+
+ model.connection.transaction do
+ model.disable_statement_timeout
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('0')
+ end
+
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('20s')
+ end
+ end
+
+ context 'when passing a blocks' do
+ it 'disables statement timeouts on session level and executes the block' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
+ expect(model).to receive(:execute).with('SET statement_timeout TO 0')
+ expect(model).to receive(:execute).with('RESET ALL')
+
+ expect { |block| model.disable_statement_timeout(&block) }.to yield_control
+ end
+
+ # this specs runs without an enclosing transaction (:delete truncation method for db_cleaner)
+ context 'with real environment', :postgresql, :delete do
+ before do
+ model.execute("SET statement_timeout TO '20000'")
+ end
+
+ after do
+ model.execute('RESET ALL')
+ end
+
+ it 'defines statement to 0 for any code run inside the block' do
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('20s')
+
+ model.disable_statement_timeout do
+ model.connection.transaction do
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('0')
+ end
+
+ expect(model.execute('SHOW statement_timeout').first['statement_timeout']).to eq('0')
+ end
+ end
+ end
+ end
end
context 'using MySQL' do
@@ -308,6 +368,16 @@ describe Gitlab::Database::MigrationHelpers do
model.disable_statement_timeout
end
+
+ context 'when passing a blocks' do
+ it 'executes the block of code' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+
+ expect(model).not_to receive(:execute)
+
+ expect { |block| model.disable_statement_timeout(&block) }.to yield_control
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/git/merge_base_spec.rb b/spec/lib/gitlab/git/merge_base_spec.rb
new file mode 100644
index 00000000000..2f4e043a20f
--- /dev/null
+++ b/spec/lib/gitlab/git/merge_base_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Git::MergeBase do
+ set(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ subject(:merge_base) { described_class.new(repository, refs) }
+
+ shared_context 'existing refs with a merge base', :existing_refs do
+ let(:refs) do
+ %w(304d257dcb821665ab5110318fc58a007bd104ed 0031876facac3f2b2702a0e53a26e89939a42209)
+ end
+ end
+
+ shared_context 'when passing a missing ref', :missing_ref do
+ let(:refs) do
+ %w(304d257dcb821665ab5110318fc58a007bd104ed aaaa)
+ end
+ end
+
+ shared_context 'when passing refs that do not have a common ancestor', :no_common_ancestor do
+ let(:refs) { ['304d257dcb821665ab5110318fc58a007bd104ed', TestEnv::BRANCH_SHA['orphaned-branch']] }
+ end
+
+ describe '#sha' do
+ context 'when the refs exist', :existing_refs do
+ it 'returns the SHA of the merge base' do
+ expect(merge_base.sha).not_to be_nil
+ end
+
+ it 'memoizes the result' do
+ expect(repository).to receive(:merge_base).once.and_call_original
+
+ 2.times { merge_base.sha }
+ end
+ end
+
+ context 'when passing a missing ref', :missing_ref do
+ it 'does not call merge_base on the repository but raises an error' do
+ expect(repository).not_to receive(:merge_base)
+
+ expect { merge_base.sha }.to raise_error(Gitlab::Git::UnknownRef)
+ end
+ end
+
+ it 'returns `nil` when the refs do not have a common ancestor', :no_common_ancestor do
+ expect(merge_base.sha).to be_nil
+ end
+
+ it 'returns a merge base when passing 2 branch names' do
+ merge_base = described_class.new(repository, %w(master feature))
+
+ expect(merge_base.sha).to be_present
+ end
+
+ it 'returns a merge base when passing a tag name' do
+ merge_base = described_class.new(repository, %w(master v1.0.0))
+
+ expect(merge_base.sha).to be_present
+ end
+ end
+
+ describe '#commit' do
+ context 'for existing refs with a merge base', :existing_refs do
+ it 'finds the commit for the merge base' do
+ expect(merge_base.commit).to be_a(Commit)
+ end
+
+ it 'only looks up the commit once' do
+ expect(repository).to receive(:commit_by).once.and_call_original
+
+ 2.times { merge_base.commit }
+ end
+ end
+
+ it 'does not try to find the commit when there is no sha', :no_common_ancestor do
+ expect(repository).not_to receive(:commit_by)
+
+ merge_base.commit
+ end
+ end
+
+ describe '#unknown_refs', :missing_ref do
+ it 'returns the the refs passed that are not part of the repository' do
+ expect(merge_base.unknown_refs).to contain_exactly('aaaa')
+ end
+
+ it 'only looks up the commits once' do
+ expect(merge_base).to receive(:commits_for_refs).once.and_call_original
+
+ 2.times { merge_base.unknown_refs }
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 32b8755ee9a..42b627b6823 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1103,6 +1103,12 @@ describe Ci::Build do
it { is_expected.to be_cancelable }
end
+
+ context 'when build is created' do
+ let(:build) { create(:ci_build, :created) }
+
+ it { is_expected.to be_cancelable }
+ end
end
context 'when build is not cancelable' do
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 77c475b9f52..e545b674b4f 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -94,9 +94,39 @@ RSpec.describe NotificationSetting do
end
end
- context 'email events' do
- it 'includes EXCLUDED_WATCHER_EVENTS in EMAIL_EVENTS' do
- expect(described_class::EMAIL_EVENTS).to include(*described_class::EXCLUDED_WATCHER_EVENTS)
+ describe '.email_events' do
+ subject { described_class.email_events }
+
+ it 'returns email events' do
+ expect(subject).to include(
+ :new_note,
+ :new_issue,
+ :reopen_issue,
+ :close_issue,
+ :reassign_issue,
+ :new_merge_request,
+ :reopen_merge_request,
+ :close_merge_request,
+ :reassign_merge_request,
+ :merge_merge_request,
+ :failed_pipeline,
+ :success_pipeline
+ )
+ end
+
+ it 'includes EXCLUDED_WATCHER_EVENTS' do
+ expect(subject).to include(*described_class::EXCLUDED_WATCHER_EVENTS)
+ end
+ end
+
+ describe '#email_events' do
+ let(:source) { build(:group) }
+
+ subject { build(:notification_setting, source: source) }
+
+ it 'calls email_events' do
+ expect(described_class).to receive(:email_events).with(source)
+ subject.email_events
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 03beb9187ed..076de06cf99 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -364,6 +364,15 @@ describe Project do
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
end
+ describe '#to_reference_with_postfix' do
+ it 'returns the full path with reference_postfix' do
+ namespace = create(:namespace, path: 'sample-namespace')
+ project = create(:project, path: 'sample-project', namespace: namespace)
+
+ expect(project.to_reference_with_postfix).to eq 'sample-namespace/sample-project>'
+ end
+ end
+
describe '#to_reference' do
let(:owner) { create(:user, name: 'Gitlab') }
let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 52ec8dbe25a..2859d5149ec 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -296,41 +296,31 @@ describe Repository do
end
describe '#new_commits' do
- shared_examples 'finding unreferenced commits' do
- set(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ set(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
- subject { repository.new_commits(rev) }
+ subject { repository.new_commits(rev) }
- context 'when there are no new commits' do
- let(:rev) { repository.commit.id }
+ context 'when there are no new commits' do
+ let(:rev) { repository.commit.id }
- it 'returns an empty array' do
- expect(subject).to eq([])
- end
+ it 'returns an empty array' do
+ expect(subject).to eq([])
end
+ end
- context 'when new commits are found' do
- let(:branch) { 'orphaned-branch' }
- let!(:rev) { repository.commit(branch).id }
+ context 'when new commits are found' do
+ let(:branch) { 'orphaned-branch' }
+ let!(:rev) { repository.commit(branch).id }
- it 'returns the commits' do
- repository.delete_branch(branch)
+ it 'returns the commits' do
+ repository.delete_branch(branch)
- expect(subject).not_to be_empty
- expect(subject).to all( be_a(::Commit) )
- expect(subject.size).to eq(1)
- end
+ expect(subject).not_to be_empty
+ expect(subject).to all( be_a(::Commit) )
+ expect(subject.size).to eq(1)
end
end
-
- context 'when Gitaly handles the request' do
- it_behaves_like 'finding unreferenced commits'
- end
-
- context 'when Gitaly is disabled', :disable_gitaly do
- it_behaves_like 'finding unreferenced commits'
- end
end
describe '#commits_by' do
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 5814d834572..6adbbb40489 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -3,6 +3,32 @@ require 'spec_helper'
describe API::Jobs do
include HttpIOHelpers
+ shared_examples 'a job with artifacts and trace' do |result_is_array: true|
+ context 'with artifacts and trace' do
+ let!(:second_job) { create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) }
+
+ it 'returns artifacts and trace data', :skip_before_request do
+ get api(api_endpoint, api_user)
+ json_job = result_is_array ? json_response.select { |job| job['id'] == second_job.id }.first : json_response
+
+ expect(json_job['artifacts_file']).not_to be_nil
+ expect(json_job['artifacts_file']).not_to be_empty
+ expect(json_job['artifacts_file']['filename']).to eq(second_job.artifacts_file.filename)
+ expect(json_job['artifacts_file']['size']).to eq(second_job.artifacts_file.size)
+ expect(json_job['artifacts']).not_to be_nil
+ expect(json_job['artifacts']).to be_an Array
+ expect(json_job['artifacts'].size).to eq(second_job.job_artifacts.length)
+ json_job['artifacts'].each do |artifact|
+ expect(artifact).not_to be_nil
+ file_type = Ci::JobArtifact.file_types[artifact['file_type']]
+ expect(artifact['size']).to eq(second_job.job_artifacts.where(file_type: file_type).first.size)
+ expect(artifact['filename']).to eq(second_job.job_artifacts.where(file_type: file_type).first.filename)
+ expect(artifact['file_format']).to eq(second_job.job_artifacts.where(file_type: file_type).first.file_format)
+ end
+ end
+ end
+ end
+
set(:project) do
create(:project, :repository, public_builds: false)
end
@@ -49,6 +75,20 @@ describe API::Jobs do
expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
end
+ context 'without artifacts and trace' do
+ it 'returns no artifacts nor trace data' do
+ json_job = json_response.first
+
+ expect(json_job['artifacts_file']).to be_nil
+ expect(json_job['artifacts']).to be_an Array
+ expect(json_job['artifacts']).to be_empty
+ end
+ end
+
+ it_behaves_like 'a job with artifacts and trace' do
+ let(:api_endpoint) { "/projects/#{project.id}/jobs" }
+ end
+
it 'returns pipeline data' do
json_job = json_response.first
@@ -60,7 +100,7 @@ describe API::Jobs do
end
it 'avoids N+1 queries', :skip_before_request do
- first_build = create(:ci_build, :artifacts, pipeline: pipeline)
+ first_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline)
first_build.runner = create(:ci_runner)
first_build.user = create(:user)
first_build.save
@@ -68,7 +108,7 @@ describe API::Jobs do
control_count = ActiveRecord::QueryRecorder.new { go }.count
second_pipeline = create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch)
- second_build = create(:ci_build, :artifacts, pipeline: second_pipeline)
+ second_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: second_pipeline)
second_build.runner = create(:ci_runner)
second_build.user = create(:user)
second_build.save
@@ -117,9 +157,11 @@ describe API::Jobs do
describe 'GET /projects/:id/pipelines/:pipeline_id/jobs' do
let(:query) { Hash.new }
- before do
- job
- get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
+ before do |example|
+ unless example.metadata[:skip_before_request]
+ job
+ get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
+ end
end
context 'authorized user' do
@@ -133,6 +175,13 @@ describe API::Jobs do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
+ expect(json_response.first['artifacts_file']).to be_nil
+ expect(json_response.first['artifacts']).to be_an Array
+ expect(json_response.first['artifacts']).to be_empty
+ end
+
+ it_behaves_like 'a job with artifacts and trace' do
+ let(:api_endpoint) { "/projects/#{project.id}/pipelines/#{pipeline.id}/jobs" }
end
it 'returns pipeline data' do
@@ -183,7 +232,7 @@ describe API::Jobs do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
end.count
- 3.times { create(:ci_build, :artifacts, pipeline: pipeline) }
+ 3.times { create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) }
expect do
get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
@@ -201,8 +250,10 @@ describe API::Jobs do
end
describe 'GET /projects/:id/jobs/:job_id' do
- before do
- get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
+ before do |example|
+ unless example.metadata[:skip_before_request]
+ get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
+ end
end
context 'authorized user' do
@@ -219,10 +270,17 @@ describe API::Jobs do
expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at)
expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
+ expect(json_response['artifacts_file']).to be_nil
+ expect(json_response['artifacts']).to be_an Array
+ expect(json_response['artifacts']).to be_empty
expect(json_response['duration']).to eq(job.duration)
expect(json_response['web_url']).to be_present
end
+ it_behaves_like 'a job with artifacts and trace', result_is_array: false do
+ let(:api_endpoint) { "/projects/#{project.id}/jobs/#{second_job.id}" }
+ end
+
it 'returns pipeline data' do
json_job = json_response
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 6063afc213d..519638ebb82 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -465,4 +465,77 @@ describe API::Repositories do
end
end
end
+
+ describe 'GET :id/repository/merge_base' do
+ let(:refs) do
+ %w(304d257dcb821665ab5110318fc58a007bd104ed 0031876facac3f2b2702a0e53a26e89939a42209)
+ end
+
+ subject(:request) do
+ get(api("/projects/#{project.id}/repository/merge_base", current_user), refs: refs)
+ end
+
+ shared_examples 'merge base' do
+ it 'returns the common ancestor' do
+ request
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['id']).to be_present
+ end
+ end
+
+ context 'when unauthenticated', 'and project is public' do
+ it_behaves_like 'merge base' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:current_user) { nil }
+ end
+ end
+
+ context 'when unauthenticated', 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:current_user) { nil }
+ let(:message) { '404 Project Not Found' }
+ end
+ end
+
+ context 'when authenticated', 'as a developer' do
+ it_behaves_like 'merge base' do
+ let(:current_user) { user }
+ end
+ end
+
+ context 'when authenticated', 'as a guest' do
+ it_behaves_like '403 response' do
+ let(:current_user) { guest }
+ end
+ end
+
+ context 'when passing refs that do not exist' do
+ it_behaves_like '400 response' do
+ let(:refs) { %w(304d257dcb821665ab5110318fc58a007bd104ed missing) }
+ let(:current_user) { user }
+ let(:message) { 'Could not find ref: missing' }
+ end
+ end
+
+ context 'when passing refs that do not have a merge base' do
+ it_behaves_like '404 response' do
+ let(:refs) { ['304d257dcb821665ab5110318fc58a007bd104ed', TestEnv::BRANCH_SHA['orphaned-branch']] }
+ let(:current_user) { user }
+ let(:message) { '404 Merge Base Not Found' }
+ end
+ end
+
+ context 'when not enough refs are passed' do
+ let(:refs) { %w(only-one) }
+ let(:current_user) { user }
+
+ it 'renders a bad request error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('Provide exactly 2 refs')
+ end
+ end
+ end
end
diff --git a/spec/rubocop/cop/migration/add_reference_spec.rb b/spec/rubocop/cop/migration/add_reference_spec.rb
new file mode 100644
index 00000000000..8f795bb561e
--- /dev/null
+++ b/spec/rubocop/cop/migration/add_reference_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/add_reference'
+
+describe RuboCop::Cop::Migration::AddReference do
+ include CopHelper
+
+ let(:cop) { described_class.new }
+
+ context 'outside of a migration' do
+ it 'does not register any offenses' do
+ expect_no_offenses(<<~RUBY)
+ def up
+ add_reference(:projects, :users)
+ end
+ RUBY
+ end
+ end
+
+ context 'in a migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ end
+
+ it 'registers an offense when using add_reference without index' do
+ expect_offense(<<~RUBY)
+ call do
+ add_reference(:projects, :users)
+ ^^^^^^^^^^^^^ `add_reference` requires `index: true`
+ end
+ RUBY
+ end
+
+ it 'registers an offense when using add_reference index disabled' do
+ expect_offense(<<~RUBY)
+ def up
+ add_reference(:projects, :users, index: false)
+ ^^^^^^^^^^^^^ `add_reference` requires `index: true`
+ end
+ RUBY
+ end
+
+ it 'does not register an offense when using add_reference with index enabled' do
+ expect_no_offenses(<<~RUBY)
+ def up
+ add_reference(:projects, :users, index: true)
+ end
+ RUBY
+ end
+ end
+end
diff --git a/spec/serializers/project_mirror_serializer_spec.rb b/spec/serializers/project_mirror_serializer_spec.rb
new file mode 100644
index 00000000000..5e47163532a
--- /dev/null
+++ b/spec/serializers/project_mirror_serializer_spec.rb
@@ -0,0 +1,7 @@
+require 'spec_helper'
+
+describe ProjectMirrorSerializer do
+ it 'represents ProjectMirror entities' do
+ expect(described_class.entity_class).to eq(ProjectMirrorEntity)
+ end
+end
diff --git a/spec/services/ci/enqueue_build_service_spec.rb b/spec/services/ci/enqueue_build_service_spec.rb
new file mode 100644
index 00000000000..e41b8e4800b
--- /dev/null
+++ b/spec/services/ci/enqueue_build_service_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Ci::EnqueueBuildService, '#execute' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:ci_build) { create(:ci_build, :created) }
+
+ subject { described_class.new(project, user).execute(ci_build) }
+
+ it 'enqueues the build' do
+ subject
+
+ expect(ci_build.pending?).to be_truthy
+ end
+end
diff --git a/spec/services/commits/tag_service_spec.rb b/spec/services/commits/tag_service_spec.rb
new file mode 100644
index 00000000000..82377a8dace
--- /dev/null
+++ b/spec/services/commits/tag_service_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Commits::TagService do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ let(:commit) { project.commit }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ describe '#execute' do
+ let(:service) { described_class.new(project, user, opts) }
+
+ shared_examples 'tag failure' do
+ it 'returns a hash with the :error status' do
+ result = service.execute(commit)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(error_message)
+ end
+
+ it 'does not add a system note' do
+ service.execute(commit)
+
+ description_notes = find_notes('tag')
+ expect(description_notes).to be_empty
+ end
+ end
+
+ def find_notes(action)
+ commit
+ .notes
+ .joins(:system_note_metadata)
+ .where(system_note_metadata: { action: action })
+ end
+
+ context 'valid params' do
+ let(:opts) do
+ {
+ tag_name: 'v1.2.3',
+ tag_message: 'Release'
+ }
+ end
+
+ def find_notes(action)
+ commit
+ .notes
+ .joins(:system_note_metadata)
+ .where(system_note_metadata: { action: action })
+ end
+
+ context 'when tagging succeeds' do
+ it 'returns a hash with the :success status and created tag' do
+ result = service.execute(commit)
+
+ expect(result[:status]).to eq(:success)
+
+ tag = result[:tag]
+ expect(tag.name).to eq(opts[:tag_name])
+ expect(tag.message).to eq(opts[:tag_message])
+ end
+
+ it 'adds a system note' do
+ service.execute(commit)
+
+ description_notes = find_notes('tag')
+ expect(description_notes.length).to eq(1)
+ end
+ end
+
+ context 'when tagging fails' do
+ let(:tag_error) { 'GitLab: You are not allowed to push code to this project.' }
+
+ before do
+ tag_stub = instance_double(Tags::CreateService)
+ allow(Tags::CreateService).to receive(:new).and_return(tag_stub)
+ allow(tag_stub).to receive(:execute).and_return({
+ status: :error, message: tag_error
+ })
+ end
+
+ it_behaves_like 'tag failure' do
+ let(:error_message) { tag_error }
+ end
+ end
+ end
+
+ context 'invalid params' do
+ let(:opts) do
+ {}
+ end
+
+ it_behaves_like 'tag failure' do
+ let(:error_message) { 'Missing parameter tag_name' }
+ end
+ end
+ end
+end
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index 784dac55454..a8c994c101c 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -11,40 +11,6 @@ describe Notes::QuickActionsService do
end
end
- shared_examples 'note on noteable that does not support quick actions' do
- include_context 'note on noteable'
-
- before do
- note.note = note_text
- end
-
- describe 'note with only command' do
- describe '/close, /label, /assign & /milestone' do
- let(:note_text) { %(/close\n/assign @#{assignee.username}") }
-
- it 'saves the note and does not alter the note text' do
- content, command_params = service.extract_commands(note)
-
- expect(content).to eq note_text
- expect(command_params).to be_empty
- end
- end
- end
-
- describe 'note with command & text' do
- describe '/close, /label, /assign & /milestone' do
- let(:note_text) { %(HELLO\n/close\n/assign @#{assignee.username}\nWORLD) }
-
- it 'saves the note and does not alter the note text' do
- content, command_params = service.extract_commands(note)
-
- expect(content).to eq note_text
- expect(command_params).to be_empty
- end
- end
- end
- end
-
shared_examples 'note on noteable that supports quick actions' do
include_context 'note on noteable'
@@ -147,16 +113,16 @@ describe Notes::QuickActionsService do
expect(described_class.noteable_update_service(note)).to eq(Issues::UpdateService)
end
- it 'returns Issues::UpdateService for a note on a merge request' do
+ it 'returns MergeRequests::UpdateService for a note on a merge request' do
note = create(:note_on_merge_request, project: project)
expect(described_class.noteable_update_service(note)).to eq(MergeRequests::UpdateService)
end
- it 'returns nil for a note on a commit' do
+ it 'returns Commits::TagService for a note on a commit' do
note = create(:note_on_commit, project: project)
- expect(described_class.noteable_update_service(note)).to be_nil
+ expect(described_class.noteable_update_service(note)).to eq(Commits::TagService)
end
end
@@ -175,7 +141,7 @@ describe Notes::QuickActionsService do
let(:note) { create(:note_on_commit, project: project) }
it 'returns false' do
- expect(described_class.supported?(note)).to be_falsy
+ expect(described_class.supported?(note)).to be_truthy
end
end
end
@@ -203,10 +169,6 @@ describe Notes::QuickActionsService do
it_behaves_like 'note on noteable that supports quick actions' do
let(:note) { build(:note_on_merge_request, project: project) }
end
-
- it_behaves_like 'note on noteable that does not support quick actions' do
- let(:note) { build(:note_on_commit, project: project) }
- end
end
context 'CE restriction for issue assignees' do
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index 81dc7c57f4a..507909d9231 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -65,6 +65,31 @@ describe PreviewMarkdownService do
end
end
+ context 'commit description' do
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit }
+ let(:params) do
+ {
+ text: "My work\n/tag v1.2.3 Stable release",
+ quick_actions_target_type: 'Commit',
+ quick_actions_target_id: commit.id
+ }
+ end
+ let(:service) { described_class.new(project, user, params) }
+
+ it 'removes quick actions from text' do
+ result = service.execute
+
+ expect(result[:text]).to eq 'My work'
+ end
+
+ it 'explains quick actions effect' do
+ result = service.execute
+
+ expect(result[:commands]).to eq 'Tags this commit to v1.2.3 with "Stable release".'
+ end
+ end
+
it 'sets correct markdown engine' do
service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION })
result = service.execute
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 743e281183e..bf1c157c4a2 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -6,6 +6,7 @@ describe QuickActions::InterpretService do
let(:developer2) { create(:user) }
let(:issue) { create(:issue, project: project) }
let(:milestone) { create(:milestone, project: project, title: '9.10') }
+ let(:commit) { create(:commit, project: project) }
let(:inprogress) { create(:label, project: project, title: 'In Progress') }
let(:bug) { create(:label, project: project, title: 'Bug') }
let(:note) { build(:note, commit_id: merge_request.diff_head_sha) }
@@ -347,6 +348,14 @@ describe QuickActions::InterpretService do
end
end
+ shared_examples 'tag command' do
+ it 'tags a commit' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(tag_name: tag_name, tag_message: tag_message)
+ end
+ end
+
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@@ -628,16 +637,6 @@ describe QuickActions::InterpretService do
let(:issuable) { merge_request }
end
- it_behaves_like 'todo command' do
- let(:content) { '/todo' }
- let(:issuable) { issue }
- end
-
- it_behaves_like 'todo command' do
- let(:content) { '/todo' }
- let(:issuable) { merge_request }
- end
-
it_behaves_like 'done command' do
let(:content) { '/done' }
let(:issuable) { issue }
@@ -787,6 +786,28 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
+ context '/todo' do
+ let(:content) { '/todo' }
+
+ context 'if issuable is an Issue' do
+ it_behaves_like 'todo command' do
+ let(:issuable) { issue }
+ end
+ end
+
+ context 'if issuable is a MergeRequest' do
+ it_behaves_like 'todo command' do
+ let(:issuable) { merge_request }
+ end
+ end
+
+ context 'if issuable is a Commit' do
+ it_behaves_like 'empty command' do
+ let(:issuable) { commit }
+ end
+ end
+ end
+
context '/copy_metadata command' do
let(:todo_label) { create(:label, project: project, title: 'To Do') }
let(:inreview_label) { create(:label, project: project, title: 'In Review') }
@@ -971,6 +992,12 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
end
+
+ context 'if issuable is a Commit' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { commit }
+ it_behaves_like 'empty command'
+ end
end
context '/shrug command' do
@@ -1102,6 +1129,32 @@ describe QuickActions::InterpretService do
it_behaves_like 'empty command'
end
end
+
+ context '/tag command' do
+ let(:issuable) { commit }
+
+ context 'ignores command with no argument' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/tag' }
+ end
+ end
+
+ context 'tags a commit with a tag name' do
+ it_behaves_like 'tag command' do
+ let(:tag_name) { 'v1.2.3' }
+ let(:tag_message) { nil }
+ let(:content) { "/tag #{tag_name}" }
+ end
+ end
+
+ context 'tags a commit with a tag name and message' do
+ it_behaves_like 'tag command' do
+ let(:tag_name) { 'v1.2.3' }
+ let(:tag_message) { 'Stable release' }
+ let(:content) { "/tag #{tag_name} #{tag_message}" }
+ end
+ end
+ end
end
describe '#explain' do
@@ -1319,5 +1372,39 @@ describe QuickActions::InterpretService do
expect(explanations).to eq(["Moves this issue to test/project."])
end
end
+
+ describe 'tag a commit' do
+ describe 'with a tag name' do
+ context 'without a message' do
+ let(:content) { '/tag v1.2.3' }
+
+ it 'includes the tag name only' do
+ _, explanations = service.explain(content, commit)
+
+ expect(explanations).to eq(["Tags this commit to v1.2.3."])
+ end
+ end
+
+ context 'with an empty message' do
+ let(:content) { '/tag v1.2.3 ' }
+
+ it 'includes the tag name only' do
+ _, explanations = service.explain(content, commit)
+
+ expect(explanations).to eq(["Tags this commit to v1.2.3."])
+ end
+ end
+ end
+
+ describe 'with a tag name and message' do
+ let(:content) { '/tag v1.2.3 Stable release' }
+
+ it 'includes the tag name and message' do
+ _, explanations = service.explain(content, commit)
+
+ expect(explanations).to eq(["Tags this commit to v1.2.3 with \"Stable release\"."])
+ end
+ end
+ end
end
end
diff --git a/spec/services/quick_actions/target_service_spec.rb b/spec/services/quick_actions/target_service_spec.rb
new file mode 100644
index 00000000000..0aeb29cbeec
--- /dev/null
+++ b/spec/services/quick_actions/target_service_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe QuickActions::TargetService do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ describe '#execute' do
+ shared_examples 'no target' do |type_id:|
+ it 'returns nil' do
+ target = service.execute(type, type_id)
+
+ expect(target).to be_nil
+ end
+ end
+
+ shared_examples 'find target' do
+ it 'returns the target' do
+ found_target = service.execute(type, target_id)
+
+ expect(found_target).to eq(target)
+ end
+ end
+
+ shared_examples 'build target' do |type_id:|
+ it 'builds a new target' do
+ target = service.execute(type, type_id)
+
+ expect(target.project).to eq(project)
+ expect(target).to be_new_record
+ end
+ end
+
+ context 'for issue' do
+ let(:target) { create(:issue, project: project) }
+ let(:target_id) { target.iid }
+ let(:type) { 'Issue' }
+
+ it_behaves_like 'find target'
+ it_behaves_like 'build target', type_id: nil
+ it_behaves_like 'build target', type_id: -1
+ end
+
+ context 'for merge request' do
+ let(:target) { create(:merge_request, source_project: project) }
+ let(:target_id) { target.iid }
+ let(:type) { 'MergeRequest' }
+
+ it_behaves_like 'find target'
+ it_behaves_like 'build target', type_id: nil
+ it_behaves_like 'build target', type_id: -1
+ end
+
+ context 'for commit' do
+ let(:project) { create(:project, :repository) }
+ let(:target) { project.commit.parent }
+ let(:target_id) { target.sha }
+ let(:type) { 'Commit' }
+
+ it_behaves_like 'find target'
+ it_behaves_like 'no target', type_id: 'invalid_sha'
+
+ context 'with nil target_id' do
+ let(:target) { project.commit }
+ let(:target_id) { nil }
+
+ it_behaves_like 'find target'
+ end
+ end
+
+ context 'for unknown type' do
+ let(:type) { 'unknown' }
+
+ it_behaves_like 'no target', type_id: :unused
+ end
+ end
+end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 57d081cffb3..442de61f69b 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -108,6 +108,25 @@ describe SystemNoteService do
end
end
+ describe '.tag_commit' do
+ let(:noteable) do
+ project.commit
+ end
+ let(:tag_name) { 'v1.2.3' }
+
+ subject { described_class.tag_commit(noteable, project, author, tag_name) }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'tag' }
+ end
+
+ it 'sets the note text' do
+ link = "http://localhost/#{project.full_path}/tags/#{tag_name}"
+
+ expect(subject.note).to eq "tagged commit #{noteable.sha} to [`#{tag_name}`](#{link})"
+ end
+ end
+
describe '.change_assignee' do
subject { described_class.change_assignee(noteable, project, author, assignee) }
diff --git a/spec/support/banzai/reference_filter_shared_examples.rb b/spec/support/banzai/reference_filter_shared_examples.rb
index eb5da662ab5..476d80f3a93 100644
--- a/spec/support/banzai/reference_filter_shared_examples.rb
+++ b/spec/support/banzai/reference_filter_shared_examples.rb
@@ -11,3 +11,76 @@ shared_examples 'a reference containing an element node' do
expect(doc.children.first.inner_html).to eq(inner_html)
end
end
+
+# Requires a reference, subject and subject_name:
+# subject { create(:user) }
+# let(:reference) { subject.to_reference }
+# let(:subject_name) { 'user' }
+shared_examples 'user reference or project reference' do
+ shared_examples 'it contains a data- attribute' do
+ it 'includes a data- attribute' do
+ doc = reference_filter("Hey #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute("data-#{subject_name}")
+ expect(link.attr("data-#{subject_name}")).to eq subject.id.to_s
+ end
+ end
+
+ context 'mentioning a resource' do
+ it_behaves_like 'a reference containing an element node'
+ it_behaves_like 'it contains a data- attribute'
+
+ it "links to a resource" do
+ doc = reference_filter("Hey #{reference}")
+ expect(doc.css('a').first.attr('href')).to eq urls.send("#{subject_name}_url", subject)
+ end
+
+ it 'links to a resource with a period' do
+ subject = create(subject_name.to_sym, name: 'alphA.Beta')
+
+ doc = reference_filter("Hey #{get_reference(subject)}")
+ expect(doc.css('a').length).to eq 1
+ end
+
+ it 'links to a resource with an underscore' do
+ subject = create(subject_name.to_sym, name: 'ping_pong_king')
+
+ doc = reference_filter("Hey #{get_reference(subject)}")
+ expect(doc.css('a').length).to eq 1
+ end
+
+ it 'links to a resource with different case-sensitivity' do
+ subject = create(subject_name.to_sym, name: 'RescueRanger')
+ reference = get_reference(subject)
+
+ doc = reference_filter("Hey #{reference.upcase}")
+ expect(doc.css('a').length).to eq 1
+ expect(doc.css('a').text).to eq(reference)
+ end
+ end
+
+ it 'supports an :only_path context' do
+ doc = reference_filter("Hey #{reference}", only_path: true)
+ link = doc.css('a').first.attr('href')
+
+ expect(link).not_to match %r(https?://)
+ expect(link).to eq urls.send "#{subject_name}_path", subject
+ end
+
+ context 'referencing a resource in a link href' do
+ let(:reference) { %Q{<a href="#{get_reference(subject)}">Some text</a>} }
+
+ it_behaves_like 'it contains a data- attribute'
+
+ it 'links to the resource' do
+ doc = reference_filter("Hey #{reference}")
+ expect(doc.css('a').first.attr('href')).to eq urls.send "#{subject_name}_url", subject
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Mention me (#{reference}.)")
+ expect(doc.to_html).to match(%r{\(<a.+>Some text</a>\.\)})
+ end
+ end
+end
diff --git a/spec/support/helpers/features/notes_helpers.rb b/spec/support/helpers/features/notes_helpers.rb
index 2b9f8b30c60..89517fde6e2 100644
--- a/spec/support/helpers/features/notes_helpers.rb
+++ b/spec/support/helpers/features/notes_helpers.rb
@@ -20,6 +20,13 @@ module Spec
end
end
end
+
+ def preview_note(text)
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: text)
+ click_on('Preview')
+ end
+ end
end
end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index f392660d2c7..21103771d1f 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -67,6 +67,7 @@ module TestEnv
TMP_TEST_PATH = Rails.root.join('tmp', 'tests', '**')
REPOS_STORAGE = 'default'.freeze
+ BROKEN_STORAGE = 'broken'.freeze
# Test environment
#
@@ -157,10 +158,11 @@ module TestEnv
component_timed_setup('Gitaly',
install_dir: gitaly_dir,
version: Gitlab::GitalyClient.expected_server_version,
- task: "gitlab:gitaly:install[#{gitaly_dir}]") do
+ task: "gitlab:gitaly:install[#{gitaly_dir},#{repos_path}]") do
- # Always re-create config, in case it's outdated. This is fast anyway.
- Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, force: true)
+ # Re-create config, to specify the broken storage path
+ storage_paths = { 'default' => repos_path, 'broken' => broken_path }
+ Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, storage_paths, force: true)
start_gitaly(gitaly_dir)
end
@@ -256,6 +258,10 @@ module TestEnv
@repos_path ||= Gitlab.config.repositories.storages[REPOS_STORAGE].legacy_disk_path
end
+ def broken_path
+ @broken_path ||= Gitlab.config.repositories.storages[BROKEN_STORAGE].legacy_disk_path
+ end
+
def backup_path
Gitlab.config.backup.path
end
diff --git a/spec/support/import_export/configuration_helper.rb b/spec/support/import_export/configuration_helper.rb
index bbac6ca6a9c..b4164cff922 100644
--- a/spec/support/import_export/configuration_helper.rb
+++ b/spec/support/import_export/configuration_helper.rb
@@ -9,7 +9,7 @@ module ConfigurationHelper
end
def relation_class_for_name(relation_name)
- relation_name = Gitlab::ImportExport::RelationFactory::OVERRIDES[relation_name.to_sym] || relation_name
+ relation_name = Gitlab::ImportExport::RelationFactory.overrides[relation_name.to_sym] || relation_name
Gitlab::ImportExport::RelationFactory.relation_class(relation_name)
end
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index 4545226d78c..e6e4d9504d9 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -8,13 +8,23 @@ describe 'gitlab:gitaly namespace rake task' do
describe 'install' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s }
+ let(:storage_path) { Rails.root.join('tmp/tests/repositories').to_s }
let(:version) { File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp }
+ subject { run_rake_task('gitlab:gitaly:install', clone_path, storage_path) }
+
context 'no dir given' do
it 'aborts and display a help message' do
# avoid writing task output to spec progress
allow($stderr).to receive :write
- expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly/
+ expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly and the path for the default storage/
+ end
+ end
+
+ context 'no storage path given' do
+ it 'aborts and display a help message' do
+ allow($stderr).to receive :write
+ expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error /Please specify the directory where you want to install gitaly and the path for the default storage/
end
end
@@ -23,7 +33,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect(main_object)
.to receive(:checkout_or_clone_version).and_raise 'Git error'
- expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
+ expect { subject }.to raise_error 'Git error'
end
end
@@ -36,7 +46,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect(main_object)
.to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
end
@@ -59,7 +69,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
expect(main_object).to receive(:run_command!).with(command_preamble + %w[gmake]).and_return(true)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
end
@@ -72,7 +82,7 @@ describe 'gitlab:gitaly namespace rake task' do
it 'calls make in the gitaly directory' do
expect(main_object).to receive(:run_command!).with(command_preamble + %w[make]).and_return(true)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
context 'when Rails.env is test' do
@@ -89,55 +99,10 @@ describe 'gitlab:gitaly namespace rake task' do
it 'calls make in the gitaly directory with --no-deployment flag for bundle' do
expect(main_object).to receive(:run_command!).with(command_preamble + command).and_return(true)
- run_rake_task('gitlab:gitaly:install', clone_path)
+ subject
end
end
end
end
end
-
- describe 'storage_config' do
- it 'prints storage configuration in a TOML format' do
- config = {
- 'default' => Gitlab::GitalyClient::StorageSettings.new(
- 'path' => '/path/to/default',
- 'gitaly_address' => 'unix:/path/to/my.socket'
- ),
- 'nfs_01' => Gitlab::GitalyClient::StorageSettings.new(
- 'path' => '/path/to/nfs_01',
- 'gitaly_address' => 'unix:/path/to/my.socket'
- )
- }
- allow(Gitlab.config.repositories).to receive(:storages).and_return(config)
- allow(Rails.env).to receive(:test?).and_return(false)
-
- expected_output = ''
- Timecop.freeze do
- expected_output = <<~TOML
- # Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}
- # This is in TOML format suitable for use in Gitaly's config.toml file.
- bin_dir = "tmp/tests/gitaly"
- socket_path = "/path/to/my.socket"
- [gitlab-shell]
- dir = "#{Gitlab.config.gitlab_shell.path}"
- [[storage]]
- name = "default"
- path = "/path/to/default"
- [[storage]]
- name = "nfs_01"
- path = "/path/to/nfs_01"
- TOML
- end
-
- expect { run_rake_task('gitlab:gitaly:storage_config')}
- .to output(expected_output).to_stdout
-
- parsed_output = TomlRB.parse(expected_output)
- config.each do |name, params|
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params.legacy_disk_path })
- end
- end
- end
- end
end
diff --git a/spec/views/shared/notes/_form.html.haml_spec.rb b/spec/views/shared/notes/_form.html.haml_spec.rb
index c57319869f3..0189f926a5f 100644
--- a/spec/views/shared/notes/_form.html.haml_spec.rb
+++ b/spec/views/shared/notes/_form.html.haml_spec.rb
@@ -16,7 +16,7 @@ describe 'shared/notes/_form' do
render
end
- %w[issue merge_request].each do |noteable|
+ %w[issue merge_request commit].each do |noteable|
context "with a note on #{noteable}" do
let(:note) { build(:"note_on_#{noteable}", project: project) }
@@ -25,12 +25,4 @@ describe 'shared/notes/_form' do
end
end
end
-
- context 'with a note on a commit' do
- let(:note) { build(:note_on_commit, project: project) }
-
- it 'says that only markdown is supported, not quick actions' do
- expect(rendered).to have_content('Markdown is supported')
- end
- end
end
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index b698248bc38..ffcf5648075 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -641,10 +641,10 @@ rollout 100%:
function install_dependencies() {
apk add -U openssl curl tar gzip bash ca-certificates git
- wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub
- wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.23-r3/glibc-2.23-r3.apk
- apk add glibc-2.23-r3.apk
- rm glibc-2.23-r3.apk
+ wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
+ wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk
+ apk add glibc-2.28-r0.apk
+ rm glibc-2.28-r0.apk
curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
mv linux-amd64/helm /usr/bin/
@@ -720,10 +720,29 @@ rollout 100%:
if [[ -f Dockerfile ]]; then
echo "Building Dockerfile-based application..."
- docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
+ docker build \
+ --build-arg HTTP_PROXY="$HTTP_PROXY" \
+ --build-arg http_proxy="$http_proxy" \
+ --build-arg HTTPS_PROXY="$HTTPS_PROXY" \
+ --build-arg https_proxy="$https_proxy" \
+ --build-arg FTP_PROXY="$FTP_PROXY" \
+ --build-arg ftp_proxy="$ftp_proxy" \
+ --build-arg NO_PROXY="$NO_PROXY" \
+ --build-arg no_proxy="$no_proxy" \
+ -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
else
echo "Building Heroku-based application using gliderlabs/herokuish docker image..."
- docker run -i -e BUILDPACK_URL --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build
+ docker run -i \
+ -e BUILDPACK_URL \
+ -e HTTP_PROXY \
+ -e http_proxy \
+ -e HTTPS_PROXY \
+ -e https_proxy \
+ -e FTP_PROXY \
+ -e ftp_proxy \
+ -e NO_PROXY \
+ -e no_proxy \
+ --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build
docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
docker rm "$CI_CONTAINER_NAME" >/dev/null
echo ""
diff --git a/vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml b/vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml
index 9f4cc0574d6..983d7b5250e 100644
--- a/vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Pages/Middleman.gitlab-ci.yml
@@ -1,24 +1,25 @@
# Full project: https://gitlab.com/pages/middleman
-image: ruby:2.3
+image: ruby:2.4
+variables:
+ LANG: "C.UTF-8"
cache:
paths:
- vendor
-test:
- script:
+before_script:
- apt-get update -yqqq
- apt-get install -y nodejs
- bundle install --path vendor
+
+test:
+ script:
- bundle exec middleman build
except:
- master
pages:
script:
- - apt-get update -yqqq
- - apt-get install -y nodejs
- - bundle install --path vendor
- bundle exec middleman build
artifacts:
paths: