summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.yml4
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml6
-rw-r--r--.rubocop_todo.yml2
-rw-r--r--CONTRIBUTING.md99
-rw-r--r--Gemfile.rails5.lock4
-rw-r--r--app/assets/javascripts/blob/viewer/index.js2
-rw-r--r--app/assets/javascripts/boards/components/board.js35
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js2
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js2
-rw-r--r--app/assets/javascripts/commits.js2
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js8
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue2
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue2
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue63
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue9
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue26
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue30
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue4
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue12
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue11
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff_worker.js2
-rw-r--r--app/assets/javascripts/ide/lib/editor_options.js1
-rw-r--r--app/assets/javascripts/ide/services/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js62
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js4
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js1
-rw-r--r--app/assets/javascripts/ide/stores/utils.js4
-rw-r--r--app/assets/javascripts/ide/stores/workers/files_decorator_worker.js2
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/locked_warning.vue2
-rw-r--r--app/assets/javascripts/lazy_loader.js4
-rw-r--r--app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js2
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js5
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js2
-rw-r--r--app/assets/javascripts/line_highlighter.js6
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js35
-rw-r--r--app/assets/javascripts/milestone.js4
-rw-r--r--app/assets/javascripts/milestone_select.js5
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue2
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js2
-rw-r--r--app/assets/javascripts/network/branch_graph.js4
-rw-r--r--app/assets/javascripts/notes.js8
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue5
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js4
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js7
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js4
-rw-r--r--app/assets/javascripts/performance_bar/components/detailed_metric.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/action_component.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue1
-rw-r--r--app/assets/javascripts/profile/gl_crop.js2
-rw-r--r--app/assets/javascripts/project_find_file.js4
-rw-r--r--app/assets/javascripts/project_import.js2
-rw-r--r--app/assets/javascripts/projects/project_new.js4
-rw-r--r--app/assets/javascripts/prometheus_metrics/prometheus_metrics.js2
-rw-r--r--app/assets/javascripts/search_autocomplete.js2
-rw-r--r--app/assets/javascripts/settings_panels.js4
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js4
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue2
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js2
-rw-r--r--app/assets/javascripts/single_file_diff.js2
-rw-r--r--app/assets/javascripts/snippet/snippet_embed.js2
-rw-r--r--app/assets/javascripts/users_select.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/expand_button.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue2
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss53
-rw-r--r--app/assets/stylesheets/framework/blocks.scss1
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss1
-rw-r--r--app/assets/stylesheets/framework/flash.scss14
-rw-r--r--app/assets/stylesheets/framework/forms.scss4
-rw-r--r--app/assets/stylesheets/framework/gfm.scss1
-rw-r--r--app/assets/stylesheets/framework/layout.scss4
-rw-r--r--app/assets/stylesheets/framework/modal.scss4
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss11
-rw-r--r--app/assets/stylesheets/pages/commits.scss14
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss3
-rw-r--r--app/assets/stylesheets/pages/issuable.scss2
-rw-r--r--app/assets/stylesheets/pages/labels.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss1
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss3
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss8
-rw-r--r--app/assets/stylesheets/pages/projects.scss36
-rw-r--r--app/assets/stylesheets/pages/repo.scss60
-rw-r--r--app/assets/stylesheets/pages/settings.scss12
-rw-r--r--app/assets/stylesheets/performance_bar.scss8
-rw-r--r--app/controllers/concerns/internal_redirect.rb4
-rw-r--r--app/controllers/concerns/issues_action.rb12
-rw-r--r--app/controllers/concerns/issues_calendar.rb24
-rw-r--r--app/controllers/concerns/uploads_actions.rb13
-rw-r--r--app/controllers/projects/artifacts_controller.rb9
-rw-r--r--app/controllers/projects/blob_controller.rb26
-rw-r--r--app/controllers/projects/branches_controller.rb5
-rw-r--r--app/controllers/projects/issues_controller.rb10
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects/milestones_controller.rb4
-rw-r--r--app/graphql/resolvers/merge_request_resolver.rb11
-rw-r--r--app/graphql/types/project_type.rb7
-rw-r--r--app/graphql/types/query_type.rb7
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb1
-rw-r--r--app/models/concerns/redis_cacheable.rb2
-rw-r--r--app/models/project.rb8
-rw-r--r--app/models/project_services/chat_notification_service.rb1
-rw-r--r--app/models/repository.rb5
-rw-r--r--app/serializers/merge_request_basic_entity.rb4
-rw-r--r--app/services/ci/register_job_service.rb12
-rw-r--r--app/services/projects/autocomplete_service.rb2
-rw-r--r--app/uploaders/favicon_uploader.rb11
-rw-r--r--app/views/admin/appearances/_form.html.haml6
-rw-r--r--app/views/email_rejection_mailer/rejection.html.haml1
-rw-r--r--app/views/email_rejection_mailer/rejection.text.haml1
-rw-r--r--app/views/explore/projects/_filter.html.haml2
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/help/ui.html.haml2
-rw-r--r--app/views/import/gitlab_projects/new.html.haml2
-rw-r--r--app/views/layouts/header/_new_dropdown.haml2
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.text.erb2
-rw-r--r--app/views/projects/_new_project_fields.html.haml2
-rw-r--r--app/views/projects/_project_templates.html.haml24
-rw-r--r--app/views/projects/_stat_anchor_list.html.haml2
-rw-r--r--app/views/projects/blob/_header.html.haml2
-rw-r--r--app/views/projects/buttons/_download.html.haml2
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml2
-rw-r--r--app/views/projects/clusters/_sidebar.html.haml6
-rw-r--r--app/views/projects/clusters/gcp/_form.html.haml23
-rw-r--r--app/views/projects/commits/_commit.html.haml3
-rw-r--r--app/views/projects/deploy_tokens/_index.html.haml9
-rw-r--r--app/views/projects/deploy_tokens/_new_deploy_token.html.haml28
-rw-r--r--app/views/projects/diffs/_collapsed.html.haml2
-rw-r--r--app/views/projects/empty.html.haml5
-rw-r--r--app/views/projects/issues/_new_branch.html.haml4
-rw-r--r--app/views/projects/merge_requests/creations/_new_submit.html.haml2
-rw-r--r--app/views/projects/merge_requests/diffs/_version_controls.html.haml4
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml2
-rw-r--r--app/views/projects/refs/logs_tree.js.haml2
-rw-r--r--app/views/projects/services/_index.html.haml2
-rw-r--r--app/views/projects/wikis/edit.html.haml2
-rw-r--r--app/views/projects/wikis/git_access.html.haml2
-rw-r--r--app/views/projects/wikis/show.html.haml2
-rw-r--r--app/views/shared/_clone_panel.html.haml2
-rw-r--r--app/views/shared/boards/components/sidebar/_labels.html.haml2
-rw-r--r--app/views/shared/builds/_build_output.html.haml2
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml5
-rw-r--r--app/views/shared/notes/_note.html.haml2
-rw-r--r--app/views/shared/notifications/_button.html.haml4
-rw-r--r--app/workers/repository_fork_worker.rb26
-rw-r--r--changelogs/unreleased/45487-slack-tag-push-notifs.yml5
-rw-r--r--changelogs/unreleased/45557-machine-type-help-links.yml6
-rw-r--r--changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml5
-rw-r--r--changelogs/unreleased/46861-issuable-title-with-longer-username.yml5
-rw-r--r--changelogs/unreleased/47646-ui-glitch.yml5
-rw-r--r--changelogs/unreleased/47672-set_inline_content_type_for_ics.yml5
-rw-r--r--changelogs/unreleased/47871-new-mr-tab-active-state.yml5
-rw-r--r--changelogs/unreleased/48050-add-full-commit-sha.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml6
-rw-r--r--changelogs/unreleased/bvl-graphql-nested-merge-request.yml5
-rw-r--r--changelogs/unreleased/ce-5024-filename-search.yml5
-rw-r--r--changelogs/unreleased/dm-blockquote-trailing-whitespace.yml5
-rw-r--r--changelogs/unreleased/dm-branch-api-can-push.yml5
-rw-r--r--changelogs/unreleased/fix-groups-api-ordering.yml4
-rw-r--r--changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml5
-rw-r--r--changelogs/unreleased/ide-commit-actions-update.yml5
-rw-r--r--changelogs/unreleased/more-group-api-sorting-options.yml5
-rw-r--r--changelogs/unreleased/optimise-paused-runners.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-46276.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47836.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47960.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48009.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-48012.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-48104.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-db-check.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-pages-controller.yml5
-rw-r--r--changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml5
-rw-r--r--changelogs/unreleased/remove-link-label-vertical-alignment-property.yml5
-rw-r--r--changelogs/unreleased/remove-small-container-width.yml5
-rw-r--r--changelogs/unreleased/safari-scrollbar-bug.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-locks-check-ce.yml5
-rw-r--r--changelogs/unreleased/text-expander-icon-update.yml5
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/initializers/6_validations.rb1
-rw-r--r--config/initializers/active_record_locking.rb111
-rw-r--r--config/initializers/active_record_migration.rb10
-rw-r--r--config/routes.rb6
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md2
-rw-r--r--doc/api/README.md2
-rw-r--r--doc/api/branches.md11
-rw-r--r--doc/api/graphql/index.md4
-rw-r--r--doc/api/groups.md4
-rw-r--r--doc/api/pages_domains.md8
-rw-r--r--doc/api/search.md9
-rw-r--r--doc/ci/yaml/README.md126
-rw-r--r--doc/development/new_fe_guide/style/prettier.md14
-rw-r--r--doc/integration/google.md7
-rw-r--r--doc/integration/omniauth.md2
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/university/high-availability/aws/README.md4
-rw-r--r--doc/user/permissions.md57
-rw-r--r--doc/user/project/clusters/index.md2
-rw-r--r--doc/user/project/integrations/microsoft_teams.md2
-rw-r--r--doc/user/project/milestones/index.md1
-rw-r--r--doc/user/project/repository/index.md8
-rw-r--r--doc/user/reserved_names.md1
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/groups.rb6
-rw-r--r--lib/api/runner.rb6
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb12
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/gitlab/background_migration/prepare_untracked_uploads.rb3
-rw-r--r--lib/gitlab/checks/commit_check.rb2
-rw-r--r--lib/gitlab/checks/force_push.rb16
-rw-r--r--lib/gitlab/database.rb7
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb1
-rw-r--r--lib/gitlab/favicon.rb2
-rw-r--r--lib/gitlab/file_finder.rb17
-rw-r--r--lib/gitlab/git/lfs_changes.rb60
-rw-r--r--lib/gitlab/git/repository.rb168
-rw-r--r--lib/gitlab/git/rev_list.rb9
-rw-r--r--lib/gitlab/git/version.rb2
-rw-r--r--lib/gitlab/git/wiki.rb170
-rw-r--r--lib/gitlab/health_checks/db_check.rb2
-rw-r--r--lib/gitlab/health_checks/fs_shards_check.rb1
-rw-r--r--lib/gitlab/i18n.rb3
-rw-r--r--lib/gitlab/i18n/metadata_entry.rb11
-rw-r--r--lib/gitlab/i18n/po_linter.rb144
-rw-r--r--lib/gitlab/i18n/translation_entry.rb26
-rw-r--r--lib/gitlab/path_regex.rb1
-rw-r--r--lib/gitlab/profiler.rb13
-rw-r--r--lib/gitlab/search/parsed_query.rb23
-rw-r--r--lib/gitlab/search/query.rb55
-rw-r--r--lib/gitlab/setup_helper.rb1
-rw-r--r--lib/gitlab/shell.rb17
-rw-r--r--lib/peek/rblineprof/custom_controller_helpers.rb2
-rw-r--r--lib/tasks/gettext.rake26
-rw-r--r--lib/tasks/lint.rake1
-rw-r--r--locale/gitlab.pot21
-rw-r--r--package.json4
-rw-r--r--public/favicon.pngbin1611 -> 0 bytes
-rw-r--r--qa/qa/page/main/login.rb65
-rw-r--r--qa/qa/page/menu/main.rb3
-rw-r--r--scripts/frontend/prettier.js53
-rwxr-xr-xscripts/trigger-build181
-rwxr-xr-xscripts/trigger-build-cloud-native61
-rwxr-xr-xscripts/trigger-build-omnibus108
-rw-r--r--spec/controllers/concerns/internal_redirect_spec.rb25
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb19
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb8
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb9
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb4
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb16
-rw-r--r--spec/controllers/sessions_controller_spec.rb10
-rw-r--r--spec/controllers/uploads_controller_spec.rb17
-rw-r--r--spec/features/dashboard/groups_list_spec.rb4
-rw-r--r--spec/features/explore/groups_list_spec.rb4
-rw-r--r--spec/features/ics/dashboard_issues_spec.rb26
-rw-r--r--spec/features/ics/group_issues_spec.rb26
-rw-r--r--spec/features/ics/project_issues_spec.rb26
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb51
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb4
-rw-r--r--spec/features/labels_hierarchy_spec.rb4
-rw-r--r--spec/features/projects/deploy_keys_spec.rb3
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb3
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_basic.json16
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/branch.json3
-rw-r--r--spec/graphql/resolvers/merge_request_resolver_spec.rb23
-rw-r--r--spec/graphql/types/project_type_spec.rb9
-rw-r--r--spec/graphql/types/query_type_spec.rb16
-rw-r--r--spec/javascripts/blob/viewer/index_spec.js4
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js2
-rw-r--r--spec/javascripts/commits_spec.js2
-rw-r--r--spec/javascripts/environments/environments_app_spec.js2
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js2
-rw-r--r--spec/javascripts/gl_field_errors_spec.js4
-rw-r--r--spec/javascripts/helpers/user_mock_data_helper.js2
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_item_spec.js4
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_spec.js3
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js2
-rw-r--r--spec/javascripts/ide/components/repo_commit_section_spec.js20
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js11
-rw-r--r--spec/javascripts/ide/lib/editor_spec.js19
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js4
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js99
-rw-r--r--spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js13
-rw-r--r--spec/javascripts/ide/stores/modules/pipelines/actions_spec.js17
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js56
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js4
-rw-r--r--spec/javascripts/notes/components/noteable_note_spec.js1
-rw-r--r--spec/javascripts/notes_spec.js4
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/profile/account/components/update_username_spec.js3
-rw-r--r--spec/javascripts/settings_panels_spec.js4
-rw-r--r--spec/javascripts/test_bundle.js3
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js5
-rw-r--r--spec/javascripts/vue_shared/components/expand_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_spec.js8
-rw-r--r--spec/lib/banzai/filter/blockquote_fence_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/auth/user_auth_finders_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb2
-rw-r--r--spec/lib/gitlab/checks/force_push_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb9
-rw-r--r--spec/lib/gitlab/favicon_spec.rb2
-rw-r--r--spec/lib/gitlab/file_finder_spec.rb20
-rw-r--r--spec/lib/gitlab/git/committer_with_hooks_spec.rb216
-rw-r--r--spec/lib/gitlab/git/lfs_changes_spec.rb41
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb84
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb10
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb6
-rw-r--r--spec/lib/gitlab/git_access_spec.rb16
-rw-r--r--spec/lib/gitlab/i18n/metadata_entry_spec.rb6
-rw-r--r--spec/lib/gitlab/i18n/po_linter_spec.rb163
-rw-r--r--spec/lib/gitlab/i18n/translation_entry_spec.rb6
-rw-r--r--spec/lib/gitlab/profiler_spec.rb45
-rw-r--r--spec/lib/gitlab/search/query_spec.rb39
-rw-r--r--spec/lib/gitlab/shell_spec.rb34
-rw-r--r--spec/migrations/remove_soft_removed_objects_spec.rb36
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb8
-rw-r--r--spec/models/project_services/jira_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb16
-rw-r--r--spec/requests/api/graphql/merge_request_query_spec.rb49
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb63
-rw-r--r--spec/requests/api/groups_spec.rb49
-rw-r--r--spec/requests/api/runner_spec.rb4
-rw-r--r--spec/requests/api/search_spec.rb24
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/helpers/features/notes_helpers.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb15
-rw-r--r--spec/support/helpers/migrations_helpers.rb4
-rw-r--r--spec/support/matchers/graphql_matchers.rb6
-rw-r--r--spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb81
-rw-r--r--spec/uploaders/favicon_uploader_spec.rb29
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb2
-rw-r--r--spec/workers/repository_fork_worker_spec.rb10
-rw-r--r--yarn.lock18
359 files changed, 2841 insertions, 2055 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index f851e3b67e6..b9c5973d7ac 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -71,7 +71,3 @@ rules:
body: 1
## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
prefer-destructuring: off
- ## no-restricted-globals: https://eslint.org/docs/rules/no-restricted-globals
- no-restricted-globals: off
- ## no-multi-assign: https://eslint.org/docs/rules/no-multi-assign
- no-multi-assign: off
diff --git a/.gitignore b/.gitignore
index 51b77d5ac9e..21dc67384aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,4 @@ eslint-report.html
/.rspec
/plugins/*
/.gitlab_pages_secret
+package-lock.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9a0102c65fd..30c21b452e0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -264,10 +264,10 @@ package-and-qa:
<<: *single-script-job
variables:
<<: *single-script-job-variables
- SCRIPT_NAME: trigger-build-omnibus
+ SCRIPT_NAME: trigger-build
retry: 0
script:
- - ./$SCRIPT_NAME
+ - ./$SCRIPT_NAME omnibus
when: manual
only:
- //@gitlab-org/gitlab-ce
@@ -816,8 +816,6 @@ lint:javascript:report:
before_script: []
script:
- date
- - find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- - date
- yarn run eslint-report || true # ignore exit code
artifacts:
name: eslint-report
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 1fb352306d7..ccf301e6c78 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -487,7 +487,7 @@ Style/EmptyLiteral:
- 'lib/gitlab/fogbugz_import/importer.rb'
- 'lib/gitlab/git/diff_collection.rb'
- 'lib/gitlab/gitaly_client.rb'
- - 'scripts/trigger-build-omnibus'
+ - 'scripts/trigger-build'
- 'spec/features/merge_requests/versions_spec.rb'
- 'spec/helpers/merge_requests_helper_spec.rb'
- 'spec/lib/gitlab/request_context_spec.rb'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fb78973a727..5a66e04247e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,25 +27,26 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
- [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute)
- [Workflow labels](#workflow-labels)
- - [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc)
- - [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc)
- - [Team labels (~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.)](#team-labels-cicd-discussion-quality-platform-etc)
- - [Milestone labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#milestone-labels-deliverable-stretch-next-patch-release)
- - [Priority labels (~P1, ~P2, ~P3 , ~P4)](#bug-priority-labels-p1-p2-p3-p4)
- - [Severity labels (~S1, ~S2, ~S3 , ~S4)](#bug-severity-labels-s1-s2-s3-s4)
- - [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests)
-- [Implement design & UI elements](#implement-design--ui-elements)
+ - [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)
+ - [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)
@@ -145,7 +146,7 @@ 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 (~"feature proposal", ~bug, ~customer, etc.)
+### Type labels
Type labels are very important. They define what kind of issue this is. Every
issue should have one or more.
@@ -161,28 +162,41 @@ already reserved for subject labels).
The descriptions on the [labels page][labels-page] explain what falls under each type label.
-### Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)
+### 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.
-Examples of subject labels are ~wiki, ~"container registry", ~ldap, ~api,
-~issues, ~"merge requests", ~labels, and ~"container registry".
-
Subject labels are always all-lowercase.
-### Team labels (~"CI/CD", ~Discussion, ~Quality, ~Platform, etc.)
+### 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 ~Distribution, ~"CI/CD", ~Discussion, ~Documentation, ~Quality,
-~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products", ~"Configuration", and ~"UX".
+The current team labels are:
+
+- ~Configuration
+- ~"CI/CD"
+- ~Discussion
+- ~Distribution
+- ~Documentation
+- ~Geo
+- ~Gitaly
+- ~Monitoring
+- ~Platform
+- ~Quality
+- ~Release
+- ~"Security Products"
+- ~UX
The descriptions on the [labels page][labels-page] explain what falls under the
responsibility of each team.
@@ -193,7 +207,7 @@ 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.
-### Milestone labels (~Deliverable, ~Stretch, ~"Next Patch Release")
+### Milestone labels
Milestone labels help us clearly communicate expectations of the work for the
release. There are three levels of Milestone labels:
@@ -211,9 +225,9 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable
or ~"Stretch". Any open issue for a previous milestone should be labeled
~"Next Patch Release", or otherwise rescheduled to a different milestone.
-### Bug Priority labels (~P1, ~P2, ~P3, ~P4)
+### Bug Priority labels
-Bug Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
+Bug 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.
@@ -224,7 +238,7 @@ This label documents the planned timeline & urgency which is used to measure aga
| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | |
| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented |
-### Bug Severity labels (~S1, ~S2, ~S3, ~S4)
+### Bug Severity labels
Severity labels help us clearly communicate the impact of a ~bug on users.
@@ -240,11 +254,11 @@ Severity labels help us clearly communicate the impact of a ~bug on users.
| Label | Security Impact | Availability / Performance Impact |
|-------|---------------------------------------------------------------------|--------------------------------------------------------------|
| ~S1 | >50% users impacted (possible company extinction level event) | |
-| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
+| ~S2 | Many users or multiple paid customers impacted (but not apocalyptic)| The issue is (almost) guaranteed to occur in the near future |
| ~S3 | A few users or a single paid customer impacted | The issue is likely to occur in the near future |
| ~S4 | No paid users/customer impacted, or expected impact within 30 days | The issue _may_ occur but it's not likely |
-### Label for community contributors (~"Accepting Merge Requests")
+### 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
@@ -300,20 +314,29 @@ For guidance on UX implementation at GitLab, please refer to our [Design System]
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.
+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 applies the ~"UX ready" label to that issue.
+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.
-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
+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.
-Once the ~"design artifact" issue has been completed, the UXer removes the ~"design artifact" label and applies the ~"UX ready" label. The Product Manager can use the
-existing issue or decide to create a whole new issue for the purpose of development.
+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
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index bff1d0cba70..fd560290d6c 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -299,7 +299,7 @@ GEM
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
multi_json
- gitlab-gollum-lib (4.2.7.2)
+ gitlab-gollum-lib (4.2.7.4)
gemojione (~> 3.2)
github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0)
@@ -307,7 +307,7 @@ GEM
rouge (~> 3.1)
sanitize (~> 2.1)
stringex (~> 2.6)
- gitlab-gollum-rugged_adapter (0.4.4)
+ gitlab-gollum-rugged_adapter (0.4.4.1)
mime-types (>= 1.15)
rugged (~> 0.25)
gitlab-grit (2.8.2)
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index f61c0be9230..5485248cfaf 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -70,7 +70,7 @@ export default class BlobViewer {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
let initialViewerName = initialViewer.getAttribute('data-type');
- if (this.switcher && location.hash.indexOf('#L') === 0) {
+ if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
}
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 86b888c66c8..7920e08e4d8 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,6 +1,5 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
-import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
import AccessorUtilities from '../../lib/utils/accessor';
@@ -57,40 +56,6 @@ gl.issueBoards.Board = Vue.extend({
});
},
deep: true,
- },
- detailIssue: {
- handler () {
- if (!Object.keys(this.detailIssue.issue).length) return;
-
- const issue = this.list.findIssue(this.detailIssue.issue.id);
-
- if (issue) {
- const offsetLeft = this.$el.offsetLeft;
- const boardsList = document.querySelectorAll('.boards-list')[0];
- const left = boardsList.scrollLeft - offsetLeft;
- let right = (offsetLeft + this.$el.offsetWidth);
-
- if (window.innerWidth > 768 && boardsList.classList.contains('is-compact')) {
- // -290 here because width of boardsList is animating so therefore
- // getting the width here is incorrect
- // 290 is the width of the sidebar
- right -= (boardsList.offsetWidth - 290);
- } else {
- right -= boardsList.offsetWidth;
- }
-
- if (right - boardsList.scrollLeft > 0) {
- $(boardsList).animate({
- scrollLeft: right
- }, this.sortableOptions.animation);
- } else if (left > 0) {
- $(boardsList).animate({
- scrollLeft: offsetLeft
- }, this.sortableOptions.animation);
- }
- }
- },
- deep: true
}
},
mounted () {
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index 4482b3b3e70..4dd9aebeed9 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -17,7 +17,7 @@ gl.issueBoards.BoardDelete = Vue.extend({
deleteBoard () {
$(this.$el).tooltip('hide');
- if (confirm('Are you sure you want to delete this list?')) {
+ if (window.confirm('Are you sure you want to delete this list?')) {
this.list.destroy();
}
}
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 7dc83843e9b..ffe86468b12 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -145,6 +145,6 @@ gl.issueBoards.BoardsStore = {
return filteredList[0];
},
updateFiltersUrl () {
- history.pushState(null, null, `?${this.filter.path}`);
+ window.history.pushState(null, null, `?${this.filter.path}`);
}
};
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 7e2a3573f81..9a3ea7a55b6 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -45,7 +45,7 @@ export default class CommitsList {
this.content.fadeTo('fast', 1.0);
// Change url so if user reload a page - search results are saved
- history.replaceState({
+ window.history.replaceState({
page: commitsUrl,
}, document.title, commitsUrl);
})
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index 108082799ef..f77a5730b77 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -366,7 +366,7 @@ export default class CreateMergeRequestDropdown {
removeMessage(target) {
const { input, message } = this.getTargetData(target);
const inputClasses = ['gl-field-error-outline', 'gl-field-success-outline'];
- const messageClasses = ['gl-field-hint', 'gl-field-error-message', 'gl-field-success-message'];
+ const messageClasses = ['text-muted', 'text-danger', 'text-success'];
inputClasses.forEach(cssClass => input.classList.remove(cssClass));
messageClasses.forEach(cssClass => message.classList.remove(cssClass));
@@ -393,7 +393,7 @@ export default class CreateMergeRequestDropdown {
this.removeMessage(target);
input.classList.add('gl-field-success-outline');
- message.classList.add('gl-field-success-message');
+ message.classList.add('text-success');
message.textContent = sprintf(__('%{text} is available'), { text });
message.style.display = 'inline-block';
}
@@ -403,7 +403,7 @@ export default class CreateMergeRequestDropdown {
const text = target === 'branch' ? __('branch name') : __('source');
this.removeMessage(target);
- message.classList.add('gl-field-hint');
+ message.classList.add('text-muted');
message.textContent = sprintf(__('Checking %{text} availability…'), { text });
message.style.display = 'inline-block';
}
@@ -415,7 +415,7 @@ export default class CreateMergeRequestDropdown {
this.removeMessage(target);
input.classList.add('gl-field-error-outline');
- message.classList.add('gl-field-error-message');
+ message.classList.add('text-danger');
message.textContent = text;
message.style.display = 'inline-block';
}
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 2cfa13fdc75..d91e4809126 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -98,7 +98,7 @@ export default {
},
disableKey(deployKey, callback) {
// eslint-disable-next-line no-alert
- if (confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
+ if (window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
this.service
.disableKey(deployKey.id)
.then(this.fetchKeys)
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
index faaaf899a0d..eba58bedd6d 100644
--- a/app/assets/javascripts/environments/components/environment_stop.vue
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -40,7 +40,7 @@
methods: {
onClick() {
// eslint-disable-next-line no-alert
- if (confirm('Are you sure you want to stop this environment?')) {
+ if (window.confirm('Are you sure you want to stop this environment?')) {
this.isLoading = true;
$(this.$el).tooltip('dispose');
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
index 3d59410cbc2..d0fb0e3d99e 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
@@ -34,6 +34,10 @@ export default {
type: String,
required: true,
},
+ actionBtnIcon: {
+ type: String,
+ required: true,
+ },
itemActionComponent: {
type: String,
required: true,
@@ -53,26 +57,21 @@ export default {
required: true,
},
},
- data() {
- return {
- showActionButton: false,
- };
- },
computed: {
titleText() {
return sprintf(__('%{title} changes'), {
title: this.title,
});
},
+ filesLength() {
+ return this.fileList.length;
+ },
},
methods: {
...mapActions(['stageAllChanges', 'unstageAllChanges']),
actionBtnClicked() {
this[this.action]();
},
- setShowActionButton(show) {
- this.showActionButton = show;
- },
},
};
</script>
@@ -83,8 +82,6 @@ export default {
>
<header
class="multi-file-commit-panel-header"
- @mouseenter="setShowActionButton(true)"
- @mouseleave="setShowActionButton(false)"
>
<div
class="multi-file-commit-panel-header-title"
@@ -95,24 +92,40 @@ export default {
:size="18"
/>
{{ titleText }}
- <span
- v-show="!showActionButton"
- class="ide-commit-file-count"
- >
- {{ fileList.length }}
- </span>
- <button
- v-show="showActionButton"
- type="button"
- class="btn btn-blank btn-link ide-staged-action-btn"
- @click="actionBtnClicked"
- >
- {{ actionBtnText }}
- </button>
+ <div class="d-flex ml-auto">
+ <button
+ v-tooltip
+ v-show="filesLength"
+ :class="{
+ 'd-flex': filesLength
+ }"
+ :title="actionBtnText"
+ type="button"
+ class="btn btn-default ide-staged-action-btn p-0 order-1 align-items-center"
+ data-placement="bottom"
+ data-container="body"
+ data-boundary="viewport"
+ @click="actionBtnClicked"
+ >
+ <icon
+ :name="actionBtnIcon"
+ :size="12"
+ class="ml-auto mr-auto"
+ />
+ </button>
+ <span
+ :class="{
+ 'rounded-right': !filesLength
+ }"
+ class="ide-commit-file-count order-0 rounded-left text-center"
+ >
+ {{ filesLength }}
+ </span>
+ </div>
</div>
</header>
<ul
- v-if="fileList.length"
+ v-if="filesLength"
class="multi-file-commit-list list-unstyled append-bottom-0"
>
<li
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
index 2254271c679..d376a004e84 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
@@ -38,14 +38,17 @@ export default {
return this.modifiedFilesLength ? 'multi-file-modified' : '';
},
additionsTooltip() {
- return sprintf(n__('1 %{type} addition', '%d %{type} additions', this.addedFilesLength), {
+ return sprintf(n__('1 %{type} addition', '%{count} %{type} additions', this.addedFilesLength), {
type: this.title.toLowerCase(),
+ count: this.addedFilesLength,
});
},
modifiedTooltip() {
return sprintf(
- n__('1 %{type} modification', '%d %{type} modifications', this.modifiedFilesLength),
- { type: this.title.toLowerCase() },
+ n__('1 %{type} modification', '%{count} %{type} modifications', this.modifiedFilesLength), {
+ type: this.title.toLowerCase(),
+ count: this.modifiedFilesLength,
+ },
);
},
titleTooltip() {
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index 2ecf9af4bf0..5cda7967130 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -1,5 +1,6 @@
<script>
import { mapActions } from 'vuex';
+import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
@@ -11,6 +12,9 @@ export default {
StageButton,
UnstageButton,
},
+ directives: {
+ tooltip,
+ },
props: {
file: {
type: Object,
@@ -50,6 +54,9 @@ export default {
isActive() {
return this.activeFileKey === this.fullKey;
},
+ tooltipTitle() {
+ return this.file.path === this.file.name ? '' : this.file.path;
+ },
},
methods: {
...mapActions([
@@ -81,29 +88,30 @@ export default {
</script>
<template>
- <div
- :class="{
- 'is-active': isActive
- }"
- class="multi-file-commit-list-item"
- >
+ <div class="multi-file-commit-list-item position-relative">
<button
+ v-tooltip
+ :title="tooltipTitle"
+ :class="{
+ 'is-active': isActive
+ }"
type="button"
- class="multi-file-commit-list-path"
+ class="multi-file-commit-list-path w-100 border-0 ml-0 mr-0"
@dblclick="fileAction"
@click="openFileInEditor"
>
- <span class="multi-file-commit-list-file-path">
+ <span class="multi-file-commit-list-file-path d-flex align-items-center">
<icon
:name="iconName"
:size="16"
:css-classes="iconClass"
- />{{ file.path }}
+ />{{ file.name }}
</span>
</button>
<component
:is="actionComponent"
:path="file.path"
+ class="d-flex position-absolute"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
index a786ec80ac2..7014b9f605e 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
@@ -25,15 +25,17 @@ export default {
<template>
<div
v-once
- class="multi-file-discard-btn"
+ class="multi-file-discard-btn dropdown"
>
<button
v-tooltip
:aria-label="__('Stage changes')"
:title="__('Stage changes')"
type="button"
- class="btn btn-blank append-right-5"
+ class="btn btn-blank append-right-5 d-flex align-items-center"
data-container="body"
+ data-boundary="viewport"
+ data-placement="bottom"
@click.stop="stageChange(path)"
>
<icon
@@ -43,17 +45,31 @@ export default {
</button>
<button
v-tooltip
- :aria-label="__('Discard changes')"
- :title="__('Discard changes')"
+ :title="__('More actions')"
type="button"
- class="btn btn-blank"
+ class="btn btn-blank d-flex align-items-center"
data-container="body"
- @click.stop="discardFileChanges(path)"
+ data-boundary="viewport"
+ data-placement="bottom"
+ data-toggle="dropdown"
+ data-display="static"
>
<icon
:size="12"
- name="remove"
+ name="more"
/>
</button>
+ <div class="dropdown-menu dropdown-menu-right">
+ <ul>
+ <li>
+ <button
+ type="button"
+ @click.stop="discardFileChanges(path)"
+ >
+ {{ __('Discard changes') }}
+ </button>
+ </li>
+ </ul>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
index 34b366f63ac..9cec73ec00e 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
@@ -32,8 +32,10 @@ export default {
:aria-label="__('Unstage changes')"
:title="__('Unstage changes')"
type="button"
- class="btn btn-blank"
+ class="btn btn-blank d-flex align-items-center"
data-container="body"
+ data-boundary="viewport"
+ data-placement="bottom"
@click="unstageChange(path)"
>
<icon
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 01df0019fd4..c2c678ff0be 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -93,23 +93,25 @@ export default {
:title="__('Unstaged')"
:key-prefix="$options.stageKeys.unstaged"
:file-list="changedFiles"
- :action-btn-text="__('Stage all')"
+ :action-btn-text="__('Stage all changes')"
:active-file-key="activeFileKey"
- class="is-first"
- icon-name="unstaged"
action="stageAllChanges"
+ action-btn-icon="mobile-issue-close"
item-action-component="stage-button"
+ class="is-first"
+ icon-name="unstaged"
/>
<commit-files-list
:title="__('Staged')"
:key-prefix="$options.stageKeys.staged"
:file-list="stagedFiles"
- :action-btn-text="__('Unstage all')"
+ :action-btn-text="__('Unstage all changes')"
:staged-list="true"
:active-file-key="activeFileKey"
- icon-name="staged"
action="unstageAllChanges"
+ action-btn-icon="history"
item-action-component="unstage-button"
+ icon-name="staged"
/>
</template>
<empty-state
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 24b6a4fdea1..08ee12fd98f 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -20,7 +20,13 @@ export default {
},
},
computed: {
- ...mapState(['rightPanelCollapsed', 'viewer', 'panelResizing', 'currentActivityView']),
+ ...mapState([
+ 'rightPanelCollapsed',
+ 'viewer',
+ 'panelResizing',
+ 'currentActivityView',
+ 'rightPane',
+ ]),
...mapGetters([
'currentMergeRequest',
'getStagedFile',
@@ -88,6 +94,9 @@ export default {
this.editor.updateDimensions();
}
},
+ rightPane() {
+ this.editor.updateDimensions();
+ },
},
beforeDestroy() {
this.editor.dispose();
diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js
index e74c4046330..f09930e8158 100644
--- a/app/assets/javascripts/ide/lib/diff/diff_worker.js
+++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js
@@ -1,8 +1,10 @@
import { computeDiff } from './diff';
+// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', (e) => {
const data = e.data;
+ // eslint-disable-next-line no-restricted-globals
self.postMessage({
path: data.path,
changes: computeDiff(data.originalContent, data.newContent),
diff --git a/app/assets/javascripts/ide/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js
index 9f895d49f2e..e35595ab1fd 100644
--- a/app/assets/javascripts/ide/lib/editor_options.js
+++ b/app/assets/javascripts/ide/lib/editor_options.js
@@ -12,5 +12,6 @@ export const defaultEditorOptions = {
export default [
{
readOnly: model => !!model.file.file_lock,
+ quickSuggestions: model => !(model.language === 'markdown'),
},
];
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index e8b51f2b516..da9de25302a 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -9,7 +9,7 @@ export default {
return Vue.http.get(endpoint, { params: { format: 'json' } });
},
getFileData(endpoint) {
- return Vue.http.get(endpoint, { params: { format: 'json' } });
+ return Vue.http.get(endpoint, { params: { format: 'json', viewer: 'none' } });
},
getRawFileData(file) {
if (file.tempFile) {
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 0a0db4033c8..7219abc4185 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -49,31 +49,6 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => {
commit(rootTypes.SET_LAST_COMMIT_MSG, commitMsg, { root: true });
};
-export const checkCommitStatus = ({ rootState }) =>
- service
- .getBranchData(rootState.currentProjectId, rootState.currentBranchId)
- .then(({ data }) => {
- const { id } = data.commit;
- const selectedBranch =
- rootState.projects[rootState.currentProjectId].branches[rootState.currentBranchId];
-
- if (selectedBranch.workingReference !== id) {
- return true;
- }
-
- return false;
- })
- .catch(() =>
- flash(
- __('Error checking branch data. Please try again.'),
- 'alert',
- document,
- null,
- false,
- true,
- ),
- );
-
export const updateFilesAfterCommit = ({ commit, dispatch, rootState }, { data }) => {
const selectedProject = rootState.projects[rootState.currentProjectId];
const lastCommit = {
@@ -128,24 +103,17 @@ export const updateFilesAfterCommit = ({ commit, dispatch, rootState }, { data }
export const commitChanges = ({ commit, state, getters, dispatch, rootState, rootGetters }) => {
const newBranch = state.commitAction !== consts.COMMIT_TO_CURRENT_BRANCH;
- const payload = createCommitPayload(getters.branchName, newBranch, state, rootState);
- const getCommitStatus = newBranch ? Promise.resolve(false) : dispatch('checkCommitStatus');
+ const payload = createCommitPayload({
+ branch: getters.branchName,
+ newBranch,
+ state,
+ rootState,
+ });
commit(types.UPDATE_LOADING, true);
- return getCommitStatus
- .then(
- branchChanged =>
- new Promise(resolve => {
- if (branchChanged) {
- // show the modal with a Bootstrap call
- $('#ide-create-branch-modal').modal('show');
- } else {
- resolve();
- }
- }),
- )
- .then(() => service.commit(rootState.currentProjectId, payload))
+ return service
+ .commit(rootState.currentProjectId, payload)
.then(({ data }) => {
commit(types.UPDATE_LOADING, false);
@@ -220,12 +188,16 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
);
})
.catch(err => {
- let errMsg = __('Error committing changes. Please try again.');
- if (err.response.data && err.response.data.message) {
- errMsg += ` (${stripHtml(err.response.data.message)})`;
+ if (err.response.status === 400) {
+ $('#ide-create-branch-modal').modal('show');
+ } else {
+ let errMsg = __('Error committing changes. Please try again.');
+ if (err.response.data && err.response.data.message) {
+ errMsg += ` (${stripHtml(err.response.data.message)})`;
+ }
+ flash(errMsg, 'alert', document, null, false, true);
+ window.dispatchEvent(new Event('resize'));
}
- flash(errMsg, 'alert', document, null, false, true);
- window.dispatchEvent(new Event('resize'));
commit(types.UPDATE_LOADING, false);
});
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index 4e1df80b3a2..551dd322c9b 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -31,15 +31,16 @@ export const openMergeRequest = ({ commit, dispatch }, { projectPath, id }) => {
commit(rootTypes.CLEAR_PROJECTS, null, { root: true });
commit(rootTypes.SET_CURRENT_MERGE_REQUEST, `${id}`, { root: true });
commit(rootTypes.RESET_OPEN_FILES, null, { root: true });
- dispatch('pipelines/resetLatestPipeline', null, { root: true });
dispatch('setCurrentBranchId', '', { root: true });
dispatch('pipelines/stopPipelinePolling', null, { root: true })
.then(() => {
+ dispatch('pipelines/resetLatestPipeline', null, { root: true });
dispatch('pipelines/clearEtagPoll', null, { root: true });
})
.catch(e => {
throw e;
});
+ dispatch('setRightPane', null, { root: true });
router.push(`/project/${projectPath}/merge_requests/${id}`);
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 6718f7eae4e..fe1dc9ac8f8 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -106,7 +106,9 @@ export const fetchJobTrace = ({ dispatch, state }) => {
.catch(() => dispatch('receiveJobTraceError'));
};
-export const resetLatestPipeline = ({ commit }) =>
+export const resetLatestPipeline = ({ commit }) => {
commit(types.RECEIVE_LASTEST_PIPELINE_SUCCESS, null);
+ commit(types.SET_DETAIL_JOB, null);
+};
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 5826f6cb828..46547820425 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -47,6 +47,7 @@ export default {
baseRaw: null,
html: data.html,
size: data.size,
+ lastCommitSha: data.last_commit_sha,
});
},
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index a04a33cd12d..10368a4d97c 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -17,6 +17,7 @@ export const dataStructure = () => ({
changed: false,
staged: false,
lastCommitPath: '',
+ lastCommitSha: '',
lastCommit: {
id: '',
url: '',
@@ -104,7 +105,7 @@ export const setPageTitle = title => {
document.title = title;
};
-export const createCommitPayload = (branch, newBranch, state, rootState) => ({
+export const createCommitPayload = ({ branch, newBranch, state, rootState }) => ({
branch,
commit_message: state.commitMessage,
actions: rootState.stagedFiles.map(f => ({
@@ -112,6 +113,7 @@ export const createCommitPayload = (branch, newBranch, state, rootState) => ({
file_path: f.path,
content: f.content,
encoding: f.base64 ? 'base64' : 'text',
+ last_commit_id: newBranch ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
});
diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
index 0a1c253c637..fa35c215880 100644
--- a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
+++ b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
@@ -1,6 +1,7 @@
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import { decorateData, sortTree } from '../utils';
+// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', e => {
const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data;
@@ -89,6 +90,7 @@ self.addEventListener('message', e => {
return acc;
}, {});
+ // eslint-disable-next-line no-restricted-globals
self.postMessage({
entries,
treeList: sortTree(treeList),
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index e87a8ed7fea..b6364318537 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -226,7 +226,7 @@
.then(res => res.data)
.then(data => this.checkForSpam(data))
.then((data) => {
- if (location.pathname !== data.web_url) {
+ if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index 8cb0ab22bfb..597c6d69a81 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -38,7 +38,7 @@
},
deleteIssuable() {
// eslint-disable-next-line no-alert
- if (confirm('Issue will be removed! Are you sure?')) {
+ if (window.confirm('Issue will be removed! Are you sure?')) {
this.deleteLoading = true;
eventHub.$emit('delete.issuable');
diff --git a/app/assets/javascripts/issue_show/components/locked_warning.vue b/app/assets/javascripts/issue_show/components/locked_warning.vue
index 1c2789f154a..ad0d40faf32 100644
--- a/app/assets/javascripts/issue_show/components/locked_warning.vue
+++ b/app/assets/javascripts/issue_show/components/locked_warning.vue
@@ -2,7 +2,7 @@
export default {
computed: {
currentPath() {
- return location.pathname;
+ return window.location.pathname;
},
},
};
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index dbbf1637a47..9482d131344 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -44,8 +44,8 @@ export default class LazyLoader {
requestAnimationFrame(() => this.checkElementsInView());
}
checkElementsInView() {
- const scrollTop = pageYOffset;
- const visHeight = scrollTop + innerHeight + SCROLL_THRESHOLD;
+ const scrollTop = window.pageYOffset;
+ const visHeight = scrollTop + window.innerHeight + SCROLL_THRESHOLD;
// Loading Images which are in the current viewport or close to them
this.lazyImages = this.lazyImages.filter((selectedImage) => {
diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
index 3873f4528ce..c28ed04f94f 100644
--- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
+++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
@@ -93,7 +93,7 @@ export default class LinkedTabs {
const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
- history.replaceState({
+ window.history.replaceState({
url: newState,
}, document.title, newState);
return newState;
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index d55d0585031..d0b0e5e1ba1 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -197,7 +197,10 @@ export const insertText = (target, text) => {
// eslint-disable-next-line no-param-reassign
target.value = newText;
// eslint-disable-next-line no-param-reassign
- target.selectionStart = target.selectionEnd = selectionStart + insertedText.length;
+ target.selectionStart = selectionStart + insertedText.length;
+
+ // eslint-disable-next-line no-param-reassign
+ target.selectionEnd = selectionStart + insertedText.length;
// Trigger autosave
target.dispatchEvent(new Event('input'));
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index a02c79b787e..f086d962221 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -12,7 +12,7 @@ export function formatRelevantDigits(number) {
let digitsLeft = '';
let relevantDigits = 0;
let formattedNumber = '';
- if (!isNaN(Number(number))) {
+ if (!Number.isNaN(Number(number))) {
digitsLeft = number.toString().split('.')[0];
switch (digitsLeft.length) {
case 1:
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index f2323f57455..303c5d8a894 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -35,7 +35,7 @@ const LineHighlighter = function(options = {}) {
options.highlightLineClass = options.highlightLineClass || 'hll';
options.fileHolderSelector = options.fileHolderSelector || '.file-holder';
options.scrollFileHolder = options.scrollFileHolder || false;
- options.hash = options.hash || location.hash;
+ options.hash = options.hash || window.location.hash;
this.options = options;
this._hash = options.hash;
@@ -145,6 +145,8 @@ LineHighlighter.prototype.highlightRange = function(range) {
var i, lineNumber, ref, ref1, results;
if (range[1]) {
results = [];
+
+ // eslint-disable-next-line no-multi-assign
for (lineNumber = i = ref = range[0], ref1 = range[1]; ref <= ref1 ? i <= ref1 : i >= ref1; lineNumber = ref <= ref1 ? (i += 1) : (i -= 1)) {
results.push(this.highlightLine(lineNumber));
}
@@ -170,7 +172,7 @@ LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
//
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
- return history.pushState({
+ return window.history.pushState({
url: value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index 326d4523cce..491858c3602 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -1,13 +1,9 @@
-/* eslint-disable new-cap, comma-dangle, no-new */
-
import $ from 'jquery';
import Vue from 'vue';
-import Flash from '../flash';
+import createFlash from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
import MergeConflictsService from './merge_conflict_service';
-import './mixins/line_conflict_utils';
-import './mixins/line_conflict_actions';
import './components/diff_file_editor';
import './components/inline_conflict_lines';
import './components/parallel_conflict_lines';
@@ -19,7 +15,7 @@ export default function initMergeConflicts() {
const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore;
const mergeConflictsService = new MergeConflictsService({
conflictsPath: conflictsEl.dataset.conflictsPath,
- resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath
+ resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath,
});
initIssuableSidebar();
@@ -29,17 +25,26 @@ export default function initMergeConflicts() {
components: {
'diff-file-editor': gl.mergeConflicts.diffFileEditor,
'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines,
- 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines
+ 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines,
},
data: mergeConflictsStore.state,
computed: {
- conflictsCountText() { return mergeConflictsStore.getConflictsCountText(); },
- readyToCommit() { return mergeConflictsStore.isReadyToCommit(); },
- commitButtonText() { return mergeConflictsStore.getCommitButtonText(); },
- showDiffViewTypeSwitcher() { return mergeConflictsStore.fileTextTypePresent(); }
+ conflictsCountText() {
+ return mergeConflictsStore.getConflictsCountText();
+ },
+ readyToCommit() {
+ return mergeConflictsStore.isReadyToCommit();
+ },
+ commitButtonText() {
+ return mergeConflictsStore.getCommitButtonText();
+ },
+ showDiffViewTypeSwitcher() {
+ return mergeConflictsStore.fileTextTypePresent();
+ },
},
created() {
- mergeConflictsService.fetchConflictsData()
+ mergeConflictsService
+ .fetchConflictsData()
.then(({ data }) => {
if (data.type === 'error') {
mergeConflictsStore.setFailedRequest(data.message);
@@ -87,9 +92,9 @@ export default function initMergeConflicts() {
})
.catch(() => {
mergeConflictsStore.setSubmitState(false);
- new Flash('Failed to save merge conflicts resolutions. Please try again!');
+ createFlash('Failed to save merge conflicts resolutions. Please try again!');
});
- }
- }
+ },
+ },
});
}
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 325fa570f37..6da04020881 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -18,13 +18,13 @@ export default class Milestone {
return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
const $target = $(e.target);
- location.hash = $target.attr('href');
+ window.location.hash = $target.attr('href');
this.loadTab($target);
});
}
// eslint-disable-next-line class-methods-use-this
loadInitialTab() {
- const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`);
+ const $target = $(`.js-milestone-tabs a[href="${window.location.hash}"]`);
if ($target.length) {
$target.tab('show');
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index d269c45203a..334279137d8 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -16,10 +16,10 @@ export default class MilestoneSelect {
typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject;
}
- this.init(els, options);
+ MilestoneSelect.init(els, options);
}
- init(els, options) {
+ static init(els, options) {
let $els = $(els);
if (!els) {
@@ -224,7 +224,6 @@ export default class MilestoneSelect {
$selectBox.hide();
$value.css('display', '');
if (data.milestone != null) {
- data.milestone.full_path = this.currentProject.full_path;
data.milestone.remaining = timeFor(data.milestone.due_date);
data.milestone.name = data.milestone.title;
$value.html(milestoneLinkTemplate(data.milestone));
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 21934021852..e1c8b6a6d4a 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -139,7 +139,7 @@ export default {
this.updateAspectRatio = true;
},
toggleAspectRatio() {
- this.updatedAspectRatios = this.updatedAspectRatios += 1;
+ this.updatedAspectRatios += 1;
if (this.store.getMetricsCount() === this.updatedAspectRatios) {
this.updateAspectRatio = !this.updateAspectRatio;
this.updatedAspectRatios = 0;
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index 20400154100..e5680a0499f 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -154,7 +154,7 @@ export default {
point.x = e.clientX;
point.y = e.clientY;
point = point.matrixTransform(this.$refs.graphData.getScreenCTM().inverse());
- point.x = point.x += 7;
+ point.x += 7;
const firstTimeSeries = this.timeSeries[0];
const timeValueOverlay = firstTimeSeries.timeSeriesScaleX.invert(point.x);
const overlayIndex = bisectDate(firstTimeSeries.values, timeValueOverlay, 1);
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 282c5c24384..92fe98508ad 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -97,7 +97,7 @@ export default {
? this.deploymentFlagData.seriesIndex
: indexFromCoordinates;
const value = series.values[index] && series.values[index].value;
- if (isNaN(value)) {
+ if (Number.isNaN(value)) {
return '-';
}
return `${formatRelevantDigits(value)}${this.unitOfDisplay}`;
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index 4d3f1f1a7cc..ed3a27dd68b 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -73,7 +73,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
timeSeriesScaleX.ticks(d3.timeMinute, 60);
timeSeriesScaleY.domain(yDom);
- const defined = d => !isNaN(d.value) && d.value != null;
+ const defined = d => !Number.isNaN(d.value) && d.value != null;
const lineFunction = d3
.line()
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index bd007c707f2..e4096ddb00d 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -112,6 +112,8 @@ export default (function() {
fill: "#444"
});
ref = this.days;
+
+ // eslint-disable-next-line no-multi-assign
for (mm = j = 0, len = ref.length; j < len; mm = (j += 1)) {
day = ref[mm];
if (cuday !== day[0] || cumonth !== day[1]) {
@@ -285,6 +287,8 @@ export default (function() {
r = this.r;
ref = commit.parents;
results = [];
+
+ // eslint-disable-next-line no-multi-assign
for (i = j = 0, len = ref.length; j < len; i = (j += 1)) {
parent = ref[i];
parentCommit = this.preparedCommits[parent[0]];
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index b2c1a26bbae..27c5dedcf0b 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -315,7 +315,7 @@ export default class Notes {
if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
if (
- !confirm('Are you sure you want to cancel creating this comment?')
+ !window.confirm('Are you sure you want to cancel creating this comment?')
) {
return;
}
@@ -329,7 +329,7 @@ export default class Notes {
newText = $textarea.val();
if (originalText !== newText) {
if (
- !confirm('Are you sure you want to cancel editing this comment?')
+ !window.confirm('Are you sure you want to cancel editing this comment?')
) {
return;
}
@@ -1675,7 +1675,7 @@ export default class Notes {
<div class="note-header">
<div class="note-header-info">
<a href="/${_.escape(currentUsername)}">
- <span class="d-none d-sm-block">${_.escape(
+ <span class="d-none d-sm-inline-block">${_.escape(
currentUsername,
)}</span>
<span class="note-headline-light">${_.escape(
@@ -1694,7 +1694,7 @@ export default class Notes {
</li>`,
);
- $tempNote.find('.d-none.d-sm-block').text(_.escape(currentUserFullname));
+ $tempNote.find('.d-none.d-sm-inline-block').text(_.escape(currentUserFullname));
$tempNote
.find('.note-headline-light')
.text(`@${_.escape(currentUsername)}`);
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index f9f5041a9f9..3f865431155 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -152,7 +152,7 @@ export default {
const msg = 'Are you sure you want to cancel creating this comment?';
// eslint-disable-next-line no-alert
- if (!confirm(msg)) {
+ if (!window.confirm(msg)) {
return;
}
}
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index ec3ee407f0a..9225a6b1a7c 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -77,7 +77,7 @@ export default {
},
deleteHandler() {
// eslint-disable-next-line no-alert
- if (confirm('Are you sure you want to delete this comment?')) {
+ if (window.confirm('Are you sure you want to delete this comment?')) {
this.isDeleting = true;
this.deleteNote(this.note)
@@ -129,7 +129,7 @@ export default {
formCancelHandler(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
// eslint-disable-next-line no-alert
- if (!confirm('Are you sure you want to cancel editing this comment?'))
+ if (!window.confirm('Are you sure you want to cancel editing this comment?'))
return;
}
this.$refs.noteBody.resetAutoSave();
@@ -170,7 +170,6 @@ export default {
:author="author"
:created-at="note.created_at"
:note-id="note.id"
- action-text="commented"
/>
<note-actions
:author-id="author.id"
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
index 653e2502d01..37336a8cb69 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
@@ -36,7 +36,9 @@ export default (function() {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$(".contributors-list").append(author_header);
- _this.authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates);
+
+ author_graph = new ContributorsAuthorGraph(d.dates);
+ _this.authors[d.author_name] = author_graph;
return author_graph.draw();
};
})(this));
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
index 77135ad1f0e..165446a4db6 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
@@ -111,10 +111,15 @@ export default {
parse_log_entry: function(log_entry, field, date_range) {
var parsed_entry;
parsed_entry = {};
+
parsed_entry.author_name = log_entry.author_name;
parsed_entry.author_email = log_entry.author_email;
parsed_entry.dates = {};
- parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0;
+
+ parsed_entry.commits = 0;
+ parsed_entry.additions = 0;
+ parsed_entry.deletions = 0;
+
_.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) {
return function(value, key) {
if (_this.in_range(value.date, date_range)) {
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index 9404b06615e..a2ca03536f2 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -180,14 +180,14 @@ export default class UserTabs {
}
toggleLoading(status) {
- return this.$parentEl.find('.loading-status .loading').toggleClass('hidden', !status);
+ return this.$parentEl.find('.loading-status .loading').toggleClass('hide', !status);
}
setCurrentAction(source) {
let newState = source;
newState = newState.replace(/\/+$/, '');
newState += this.windowLocation.search + this.windowLocation.hash;
- history.replaceState(
+ window.history.replaceState(
{
url: newState,
},
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index d765ff3b41c..dc7d6d29b8f 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -56,7 +56,7 @@ export default {
<gl-modal
:id="`modal-peek-${metric}-details`"
:header-title-text="header"
- modal-size="lg"
+ modal-size="xl"
class="performance-bar-modal"
>
<table
@@ -71,7 +71,7 @@ export default {
<td
v-for="key in keys"
:key="key"
- class="break-word all-words"
+ class="break-word"
>
{{ item[key] }}
</td>
diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue
index db0505a55fe..1f152ed438d 100644
--- a/app/assets/javascripts/pipelines/components/graph/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue
@@ -91,6 +91,7 @@ export default {
class="js-ci-action btn btn-blank
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
data-container="body"
+ data-boundary="viewport"
@click="onClickAction"
>
<icon :name="actionIcon"/>
diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
index 04120d45834..e047d10ac93 100644
--- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
@@ -87,6 +87,7 @@ export default {
data-toggle="dropdown"
data-container="body"
data-boundary="viewport"
+ data-display="static"
class="dropdown-menu-toggle build-content"
>
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 4dd2fb9fbed..b9231c002fd 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -165,6 +165,7 @@ export default {
class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
data-placement="top"
data-toggle="dropdown"
+ data-display="static"
type="button"
aria-haspopup="true"
aria-expanded="false"
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index 8f93156cdd1..ba120c4bbdf 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -139,6 +139,8 @@ import _ from 'underscore';
var array, binary, i, k, len, v;
binary = atob(dataURL.split(',')[1]);
array = [];
+
+ // eslint-disable-next-line no-multi-assign
for (k = i = 0, len = binary.length; i < len; k = (i += 1)) {
v = binary[k];
array.push(binary.charCodeAt(k));
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 4c4acd487f8..17497283695 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -91,6 +91,8 @@ export default class ProjectFindFile {
var blobItemUrl, filePath, html, i, j, len, matches, results;
this.element.find(".tree-table > tbody").empty();
results = [];
+
+ // eslint-disable-next-line no-multi-assign
for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) {
filePath = filePaths[i];
if (i === 20) {
@@ -150,7 +152,7 @@ export default class ProjectFindFile {
}
goToTree() {
- return location.href = this.options.treeUrl;
+ return window.location.href = this.options.treeUrl;
}
goToBlob() {
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index d2d26d6f67e..5a0d2b642eb 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -2,7 +2,7 @@ import { visitUrl } from './lib/utils/url_utility';
export default function projectImport() {
setTimeout(() => {
- visitUrl(location.href);
+ visitUrl(window.location.href);
}, 5000);
}
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 888b1d6ce33..002edb4663c 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -90,7 +90,7 @@ const bindEvents = () => {
function chooseTemplate() {
$('.template-option').hide();
$projectFieldsForm.addClass('selected');
- $selectedIcon.removeClass('active');
+ $selectedIcon.removeClass('d-block');
const value = $(this).val();
const templates = {
rails: {
@@ -109,7 +109,7 @@ const bindEvents = () => {
const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text);
- $(selectedTemplate.icon).addClass('active');
+ $(selectedTemplate.icon).addClass('d-block');
$templateProjectNameInput.focus();
}
diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
index 1a75fdd75db..078ccbbbac2 100644
--- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
+++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
@@ -107,7 +107,7 @@ export default class PrometheusMetrics {
if (data && data.success) {
stop(data);
} else {
- this.backOffRequestCounter = this.backOffRequestCounter += 1;
+ this.backOffRequestCounter += 1;
if (this.backOffRequestCounter < 3) {
next();
} else {
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 2da022fde63..ef3c71eeafe 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -438,7 +438,7 @@ export default class SearchAutocomplete {
}
onClick(item, $el, e) {
- if (location.pathname.indexOf(item.url) !== -1) {
+ if (window.location.pathname.indexOf(item.url) !== -1) {
if (!e.metaKey) e.preventDefault();
if (!this.badgePresent) {
if (item.category === 'Projects') {
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index eecde4550f9..37b4a2a4c63 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -42,8 +42,8 @@ export default function initSettingsPanels() {
}
});
- if (location.hash) {
- const $target = $(location.hash);
+ if (window.location.hash) {
+ const $target = $(window.location.hash);
if ($target.length && $target.hasClass('settings')) {
expandSection($target);
}
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 1e246a56b85..8658081c6c2 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -13,8 +13,8 @@ export default class ShortcutsFindFile extends ShortcutsNavigation {
element === this.projectFindFile.inputElement[0] &&
(combo === 'up' || combo === 'down' || combo === 'esc' || combo === 'enter')
) {
- // when press up/down key in textbox, cusor prevent to move to home/end
- event.preventDefault();
+ // when press up/down key in textbox, cursor prevent to move to home/end
+ e.preventDefault();
return false;
}
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 1b866cca4b1..2b8d6207dea 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -54,7 +54,7 @@ export default {
updateConfidentialAttribute(confidential) {
this.service
.update('issue', { confidential })
- .then(() => location.reload())
+ .then(() => window.location.reload())
.catch(() => {
Flash(
__(
diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
index 10bedf8af1f..8bbc59f623a 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -76,7 +76,7 @@ export default {
.update(this.issuableType, {
discussion_locked: locked,
})
- .then(() => location.reload())
+ .then(() => window.location.reload())
.catch(() =>
Flash(
this.__(
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index d86557e870a..d9ca5e46770 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -80,7 +80,7 @@ export default class SidebarMediator {
return this.service.moveIssue(this.store.moveToProjectId)
.then(response => response.json())
.then((data) => {
- if (location.pathname !== data.web_url) {
+ if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
});
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 1afff0dba38..ae27c676fa0 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -11,7 +11,7 @@ import syntaxHighlight from './syntax_highlight';
const WRAPPER = '<div class="diff-content"></div>';
const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
const ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
-const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
+const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link">Click to expand it.</button></div>';
export default class SingleFileDiff {
constructor(file) {
diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js
index 81ec483f2d9..873a506a92f 100644
--- a/app/assets/javascripts/snippet/snippet_embed.js
+++ b/app/assets/javascripts/snippet/snippet_embed.js
@@ -1,5 +1,5 @@
export default () => {
- const { protocol, host, pathname } = location;
+ const { protocol, host, pathname } = window.location;
const shareBtn = document.querySelector('.js-share-btn');
const embedBtn = document.querySelector('.js-embed-btn');
const snippetUrlArea = document.querySelector('.js-snippet-url-area');
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index cd954f75613..349614460e1 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -259,6 +259,7 @@ function UsersSelect(currentUser, els, options = {}) {
showDivider = 0;
if (firstUser) {
// Move current user to the front of the list
+ // eslint-disable-next-line no-multi-assign
for (index = j = 0, len = users.length; j < len; index = (j += 1)) {
obj = users[index];
if (obj.username === firstUser) {
@@ -561,6 +562,8 @@ function UsersSelect(currentUser, els, options = {}) {
if (firstUser) {
// Move current user to the front of the list
ref = data.results;
+
+ // eslint-disable-next-line no-multi-assign
for (index = j = 0, len = ref.length; j < len; index = (j += 1)) {
obj = ref[index];
if (obj.username === firstUser) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
index f012f9c6772..5e76f6b1cac 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue
@@ -105,7 +105,7 @@ export default {
MRWidgetService.fetchMetrics(this.metricsUrl)
.then((res) => {
if (res.status === statusCodes.NO_CONTENT) {
- this.backOffRequestCounter = this.backOffRequestCounter += 1;
+ this.backOffRequestCounter += 1;
/* eslint-disable no-unused-expressions */
this.backOffRequestCounter < 3 ? next() : stop(res);
} else {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index 985f44dee97..1a444c04a1d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -173,7 +173,7 @@
</a>
<clipboard-button
:title="__('Copy commit SHA to clipboard')"
- :text="mr.shortMergeCommitSha"
+ :text="mr.mergeCommitSha"
css-class="btn-default btn-transparent btn-clipboard js-mr-merged-copy-sha"
/>
</p>
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 134aaacf9d2..c881cd496d1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -26,6 +26,7 @@ export default class MergeRequestStore {
this.mergeStatus = data.merge_status;
this.commitMessage = data.merge_commit_message;
this.shortMergeCommitSha = data.short_merge_commit_sha;
+ this.mergeCommitSha = data.merge_commit_sha;
this.commitMessageWithDescription = data.merge_commit_message_with_description;
this.commitsCount = data.commits_count;
this.divergedCommitsCount = data.diverged_commits_count;
diff --git a/app/assets/javascripts/vue_shared/components/expand_button.vue b/app/assets/javascripts/vue_shared/components/expand_button.vue
index 0fdea651130..e6e92594b65 100644
--- a/app/assets/javascripts/vue_shared/components/expand_button.vue
+++ b/app/assets/javascripts/vue_shared/components/expand_button.vue
@@ -1,5 +1,7 @@
<script>
import { __ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+
/**
* Port of detail_behavior expand button.
*
@@ -12,6 +14,9 @@ import { __ } from '~/locale';
*/
export default {
name: 'ExpandButton',
+ components: {
+ Icon,
+ },
data() {
return {
isCollapsed: true,
@@ -22,6 +27,9 @@ export default {
return __('Click to expand text');
},
},
+ destroyed() {
+ this.isCollapsed = true;
+ },
methods: {
onClick() {
this.isCollapsed = !this.isCollapsed;
@@ -37,7 +45,10 @@ export default {
type="button"
class="text-expander btn-blank"
@click="onClick">
- ...
+ <icon
+ :size="12"
+ name="ellipsis_h"
+ />
</button>
<span v-if="!isCollapsed">
<slot name="expanded"></slot>
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index c03a342e777..b298b989203 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -1,6 +1,6 @@
<script>
const buttonVariants = ['danger', 'primary', 'success', 'warning'];
-const sizeVariants = ['sm', 'md', 'lg'];
+const sizeVariants = ['sm', 'md', 'lg', 'xl'];
export default {
name: 'GlModal',
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index eccba61a8c0..38115f268bb 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -54,7 +54,7 @@
<div class="note-header">
<div class="note-header-info">
<a :href="getUserData.path">
- <span class="d-none d-sm-block">{{ getUserData.name }}</span>
+ <span class="d-none d-sm-inline-block">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{ getUserData.username }}</span>
</a>
</div>
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 810ed5bb0a6..e6303ad4642 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -89,6 +89,11 @@ a {
color: $gl-link-color;
}
+a:not(.btn):focus,
+a:not(.btn):active {
+ text-decoration: underline;
+}
+
hr {
overflow: hidden;
}
@@ -128,11 +133,6 @@ table {
border-spacing: 0;
}
-.tooltip {
- // Fix bootstrap4 bug whereby tooltips flicker when they are hovered over their borders
- pointer-events: none;
-}
-
.popover {
font-size: 14px;
}
@@ -178,7 +178,10 @@ table {
display: none;
}
-.badge {
+// Add to .label so that old system notes that are saved to the db
+// will still receive the correct styling
+.badge,
+.label {
padding: 4px 5px;
font-size: 12px;
font-style: normal;
@@ -213,6 +216,15 @@ table {
&:not(:last-of-type) {
border-bottom: 1px solid $well-inner-border;
}
+
+ p,
+ ol,
+ ul,
+ .form-group {
+ &:last-of-type {
+ margin-bottom: 0;
+ }
+ }
}
.badge.badge-gray {
@@ -264,15 +276,36 @@ pre code {
white-space: pre-wrap;
}
+.alert,
+.flash-notice {
+ border-radius: 0;
+}
+
+.alert-success {
+ background-color: $green-500;
+ border-color: $green-500;
+}
+
+.alert-info {
+ background-color: $blue-500;
+ border-color: $blue-500;
+}
+
+.alert-warning {
+ background-color: $orange-500;
+ border-color: $orange-500;
+}
+
.alert-danger {
background-color: $red-500;
border-color: $red-500;
}
+.alert-success,
+.alert-info,
.alert-warning,
.alert-danger,
.flash-notice {
- border-radius: 0;
color: $white-light;
h4,
@@ -293,3 +326,9 @@ input[type=color].form-control {
color: $gl-text-color-secondary;
}
}
+
+.project-templates-buttons {
+ .btn {
+ vertical-align: unset;
+ }
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index c5be27f2d29..0de05548c68 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -16,6 +16,7 @@
.click-to-expand {
cursor: pointer;
+ vertical-align: initial;
}
}
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index e5197e27b82..326499125fc 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -440,10 +440,6 @@ img.emoji {
.break-word {
word-wrap: break-word;
-
- &.all-words {
- word-break: break-word;
- }
}
/** COMMON CLASSES **/
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 9cbaaa5dc8d..cccd1a6d942 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -193,6 +193,7 @@
&:focus {
background: $link-active-background;
color: $gl-text-color;
+ text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 52c3f18a682..a6e324036ae 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -10,6 +10,20 @@
@extend .alert;
background-color: $blue-500;
margin: 0;
+
+ &.flash-notice-persistent {
+ background-color: $blue-100;
+ color: $gl-text-color;
+
+ a {
+ color: $gl-link-color;
+
+ &:hover {
+ color: $gl-link-hover-color;
+ text-decoration: none;
+ }
+ }
+ }
}
.flash-warning {
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 03520f42997..2b2e6d69e33 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -201,6 +201,10 @@ label {
}
.gl-show-field-errors {
+ .form-control {
+ height: 34px;
+ }
+
.gl-field-success-outline {
border: 1px solid $green-600;
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index e378e84ca1b..1cf12b1a015 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -19,6 +19,7 @@
.gfm-color_chip {
display: inline-block;
+ line-height: 1;
margin: 0 0 2px 4px;
vertical-align: middle;
border-radius: 3px;
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 55c0bc76f23..52b5f059f20 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -54,10 +54,6 @@ body {
&.limit-container-width {
max-width: $limited-layout-width;
}
-
- &.limit-container-width-sm {
- max-width: $limited-layout-width-sm;
- }
}
.alert-wrapper {
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index a7896cc3fc3..ffb40166c15 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -1,3 +1,7 @@
+.modal-xl {
+ max-width: 98%;
+}
+
.modal-header {
background-color: $modal-body-bg;
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 2d9e9e6a67d..9dbb04e5443 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -347,7 +347,7 @@
.empty-state .project-item-select-holder.btn-group {
float: none;
- display: inline-block;
+ justify-content: center;
.btn {
// overrides styles applied to plain `.empty-state .btn`
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 0826bfd0035..f30f296d41f 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -244,10 +244,11 @@ $tooltip-font-size: 12px;
/*
* Padding
*/
-$gl-padding-24: 24px;
-$gl-padding: 16px;
-$gl-padding-8: 8px;
$gl-padding-4: 4px;
+$gl-padding-8: 8px;
+$gl-padding: 16px;
+$gl-padding-24: 24px;
+$gl-padding-32: 32px;
$gl-col-padding: 15px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
@@ -265,7 +266,6 @@ $header-height: 40px;
$ide-statusbar-height: 25px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
-$limited-layout-width-sm: 790px;
$container-text-max-width: 540px;
$gl-avatar-size: 40px;
$error-exclamation-point: $red-500;
@@ -832,3 +832,6 @@ $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;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index a4ca82de90e..49226ae8eac 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -135,10 +135,10 @@
}
.text-expander {
- display: inline-block;
+ display: inline-flex;
background: $white-light;
color: $gl-text-color-secondary;
- padding: 0 4px;
+ padding: 1px $gl-padding-4;
cursor: pointer;
border: 1px solid $border-gray-dark;
border-radius: $border-radius-default;
@@ -180,6 +180,11 @@
.commit-content {
padding-right: 10px;
white-space: normal;
+
+ .commit-title {
+ display: flex;
+ align-items: center;
+ }
}
.commit-actions {
@@ -193,6 +198,10 @@
display: inline-flex;
}
+ .ci-status-icon svg {
+ vertical-align: text-bottom;
+ }
+
> .ci-status-link,
> .btn,
> .commit-sha-group {
@@ -249,7 +258,6 @@
.generic_commit_status {
a,
button {
- color: $gl-text-color;
vertical-align: baseline;
}
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 7b36bcb3c7d..2e007c52592 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -23,7 +23,8 @@
position: relative;
line-height: 35px;
display: flex;
- flex-grow: 1;
+ flex: 1 1;
+ min-width: 0;
@include media-breakpoint-up(sm) {
padding-left: 0;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index b42c232fd91..f9fd9f1ab8b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -698,6 +698,8 @@
font-size: 14px;
line-height: 24px;
align-self: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.js-issuable-selector-wrap {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 6882b4adb15..79cac7f4ff0 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -220,7 +220,7 @@
.label-link {
display: inline-flex;
- vertical-align: top;
+ vertical-align: text-bottom;
&:hover .color-label {
text-decoration: underline;
@@ -280,7 +280,7 @@
width: 150px;
flex-shrink: 0;
- .label {
+ .badge {
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 99fe4a578be..596d3aa171c 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -46,6 +46,7 @@
.btn {
font-size: $gl-font-size;
+ max-height: 26px;
&[disabled] {
opacity: 0.3;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 30428fd198d..52332ac97dd 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -36,6 +36,7 @@
}
.table-holder {
+ overflow: unset;
width: 100%;
}
@@ -1000,7 +1001,7 @@ button.mini-pipeline-graph-dropdown-toggle {
/**
* Center dropdown menu in mini graph
*/
- &.dropdown-menu {
+ .dropdown &.dropdown-menu {
transform: translate(-80%, 0);
@media (min-width: map-get($grid-breakpoints, md)) {
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index babe81cb0f7..a353f301d07 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -16,7 +16,7 @@
.application-theme {
label {
- margin: 0 $gl-padding $gl-padding 0;
+ margin: 0 $gl-padding-32 $gl-padding 0;
text-align: center;
}
@@ -24,7 +24,7 @@
font-size: 0;
height: 48px;
border-radius: 4px;
- min-width: 135px;
+ min-width: 112px;
margin-bottom: $gl-padding-8;
&.ui-indigo {
@@ -75,7 +75,8 @@
.syntax-theme {
label {
- margin-right: 20px;
+ margin-right: $gl-padding-32;
+ margin-bottom: $gl-padding;
text-align: center;
.preview {
@@ -84,7 +85,6 @@
img {
border-radius: 4px;
-
max-width: 100%;
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index caafda5fb05..aa83e5bdebc 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -497,6 +497,12 @@
&:not(:first-child) {
border-top: 1px solid $border-color;
}
+
+ .btn-template-icon {
+ position: absolute;
+ left: $gl-padding;
+ top: $gl-padding;
+ }
}
.template-title {
@@ -514,12 +520,6 @@
}
}
- svg {
- position: absolute;
- left: $gl-padding;
- top: $gl-padding;
- }
-
.project-fields-form {
display: none;
@@ -530,34 +530,23 @@
}
.template-input-group {
- position: relative;
-
- @include media-breakpoint-up(sm) {
- display: flex;
- }
-
- .input-group-prepend,
- .input-group-append {
+ .input-group-prepend {
flex: 1;
- text-align: left;
- padding-left: ($gl-padding * 3);
- background-color: $white-light;
}
- .selected-template {
- line-height: 20px;
+ .input-group-text {
+ width: 100%;
+ background-color: $white-light;
}
.selected-icon {
+ padding-right: $gl-padding;
+
svg {
display: none;
top: 7px;
height: 20px;
width: 20px;
-
- &.active {
- display: block;
- }
}
}
}
@@ -869,7 +858,6 @@ pre.light-well {
.git-clone-holder {
width: 380px;
- height: 28px;
.btn-clipboard {
border: 1px solid $border-color;
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 4b8a3db1d06..0a56153203c 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -540,36 +540,12 @@
margin-right: -$grid-size;
min-height: 60px;
- .multi-file-commit-list-item {
- margin-left: 0;
- margin-right: 0;
- }
-
&.form-text.text-muted {
margin-left: 0;
right: 0;
}
}
-.multi-file-commit-list-item {
- &.is-active {
- background-color: $white-normal;
- }
-
- .multi-file-discard-btn {
- display: none;
- margin-top: -2px;
- margin-left: auto;
- color: $gl-link-color;
- }
-
- &:hover {
- .multi-file-discard-btn {
- display: flex;
- }
- }
-}
-
.multi-file-addition,
.multi-file-addition-solid {
color: $green-500;
@@ -599,7 +575,7 @@
}
}
-.multi-file-commit-list-item,
+.multi-file-commit-list-path,
.ide-file-list .file {
display: flex;
align-items: center;
@@ -616,11 +592,9 @@
}
.multi-file-commit-list-path {
- padding: 0;
- background: none;
- border: 0;
- text-align: left;
- width: 100%;
+ &.is-active {
+ background-color: $white-normal;
+ }
&:hover,
&:focus {
@@ -635,7 +609,7 @@
}
.multi-file-commit-list-file-path {
- @include str-truncated(100%);
+ @include str-truncated(calc(100% - 30px));
&:hover {
text-decoration: underline;
@@ -646,6 +620,16 @@
}
}
+.multi-file-discard-btn {
+ top: 4px;
+ right: 8px;
+ bottom: 4px;
+
+ svg {
+ top: 0;
+ }
+}
+
.multi-file-commit-form {
position: relative;
background-color: $white-light;
@@ -840,18 +824,20 @@
}
.ide-staged-action-btn {
- margin-left: auto;
- line-height: 22px;
+ width: 22px;
+ margin-left: -1px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+
+ > svg {
+ top: 0;
+ }
}
.ide-commit-file-count {
min-width: 22px;
- margin-left: auto;
background-color: $gray-light;
- border-radius: $border-radius-default;
border: 1px solid $white-dark;
- line-height: 20px;
- text-align: center;
}
.ide-commit-radios {
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 1f8e61257a9..2f28031b9c8 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -52,7 +52,7 @@
.settings-content {
max-height: 1px;
- overflow-y: scroll;
+ overflow-y: hidden;
padding-right: 110px;
animation: collapseMaxHeight 300ms ease-out;
// Keep the section from expanding when we scroll over it
@@ -127,13 +127,6 @@
color: $gl-danger;
}
-.service-settings {
- input[type="radio"],
- input[type="checkbox"] {
- margin-top: 10px;
- }
-}
-
.integration-settings-form {
.card.card-body,
.info-well {
@@ -296,7 +289,8 @@
}
.btn-clipboard {
- margin-left: 5px;
+ background-color: $white-light;
+ border: 1px solid $theme-gray-200;
}
.deploy-token-help-block {
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 8cdf2275551..5127ddfde6e 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -107,12 +107,12 @@
}
.performance-bar-modal {
- .modal-footer {
- display: none;
+ .modal-body {
+ padding: 0;
}
- .modal-dialog {
- width: 860px;
+ .modal-footer {
+ display: none;
}
}
}
diff --git a/app/controllers/concerns/internal_redirect.rb b/app/controllers/concerns/internal_redirect.rb
index 7409b2e89a5..10b9852e329 100644
--- a/app/controllers/concerns/internal_redirect.rb
+++ b/app/controllers/concerns/internal_redirect.rb
@@ -23,6 +23,10 @@ module InternalRedirect
nil
end
+ def sanitize_redirect(url_or_path)
+ safe_redirect_path(url_or_path) || safe_redirect_path_for_url(url_or_path)
+ end
+
def host_allowed?(uri)
uri.host == request.host &&
uri.port == request.port
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index b6eb7d292fc..9d58656773d 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -1,6 +1,7 @@
module IssuesAction
extend ActiveSupport::Concern
include IssuableCollections
+ include IssuesCalendar
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues
@@ -17,18 +18,9 @@ module IssuesAction
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
- # rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues_calendar
- @issues = issuables_collection
- .non_archived
- .with_due_date
- .limit(100)
-
- respond_to do |format|
- format.ics { response.headers['Content-Disposition'] = 'inline' }
- end
+ render_issues_calendar(issuables_collection)
end
- # rubocop:enable Gitlab/ModuleWithInstanceVariables
private
diff --git a/app/controllers/concerns/issues_calendar.rb b/app/controllers/concerns/issues_calendar.rb
new file mode 100644
index 00000000000..671a204621d
--- /dev/null
+++ b/app/controllers/concerns/issues_calendar.rb
@@ -0,0 +1,24 @@
+module IssuesCalendar
+ extend ActiveSupport::Concern
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def render_issues_calendar(issuables)
+ @issues = issuables
+ .non_archived
+ .with_due_date
+ .limit(100)
+
+ respond_to do |format|
+ format.ics do
+ # NOTE: with text/calendar as Content-Type, the browser always downloads
+ # the content as a file (even ignoring the Content-Disposition
+ # header). We want to display the content inline when accessed
+ # from GitLab, similarly to the RSS feed.
+ if request.referer&.start_with?(::Settings.gitlab.base_url)
+ response.headers['Content-Type'] = 'text/plain'
+ end
+ end
+ end
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+end
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 170bca8b56f..16374146ae4 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -1,9 +1,15 @@
module UploadsActions
+ extend ActiveSupport::Concern
+
include Gitlab::Utils::StrongMemoize
include SendFileUpload
UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze
+ included do
+ prepend_before_action :set_html_format, only: :show
+ end
+
def create
link_to_file = UploadService.new(model, params[:file], uploader_class).execute
@@ -41,6 +47,13 @@ module UploadsActions
private
+ # Explicitly set the format.
+ # Otherwise rails 5 will set it from a file extension.
+ # See https://github.com/rails/rails/commit/84e8accd6fb83031e4c27e44925d7596655285f7#diff-2b8f2fbb113b55ca8e16001c393da8f1
+ def set_html_format
+ request.format = :html
+ end
+
def uploader_class
raise NotImplementedError
end
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index abc283d7aa9..6484a713f8e 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -7,6 +7,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
before_action :authorize_read_build!
before_action :authorize_update_build!, only: [:keep]
before_action :extract_ref_name_and_path
+ before_action :set_request_format, only: [:file]
before_action :validate_artifacts!
before_action :entry, only: [:file]
@@ -101,4 +102,12 @@ class Projects::ArtifactsController < Projects::ApplicationController
render_404 unless @entry.exists?
end
+
+ def set_request_format
+ request.format = :html if set_request_format?
+ end
+
+ def set_request_format?
+ request.format != :json
+ end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 0c1c286a0a4..a8c0a68fc17 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -7,6 +7,7 @@ class Projects::BlobController < Projects::ApplicationController
prepend_before_action :authenticate_user!, only: [:edit]
+ before_action :set_request_format, only: [:edit, :show, :update]
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
@@ -188,6 +189,18 @@ class Projects::BlobController < Projects::ApplicationController
.last_for_path(@repository, @ref, @path).sha
end
+ # In Rails 4.2 if params[:format] is empty, Rails set it to :html
+ # But since Rails 5.0 the framework now looks for an extension.
+ # E.g. for `blob/master/CHANGELOG.md` in Rails 4 the format would be `:html`, but in Rails 5 on it'd be `:md`
+ # This before_action explicitly sets the `:html` format for all requests unless `:format` is set by a client e.g. by JS for XHR requests.
+ def set_request_format
+ request.format = :html if set_request_format?
+ end
+
+ def set_request_format?
+ params[:id].present? && params[:format].blank? && request.format != "json"
+ end
+
def show_html
environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
@@ -197,15 +210,14 @@ class Projects::BlobController < Projects::ApplicationController
end
def show_json
- json = blob_json(@blob)
- return render_404 unless json
-
+ set_last_commit_sha
path_segments = @path.split('/')
path_segments.pop
tree_path = path_segments.join('/')
- render json: json.merge(
+ json = {
id: @blob.id,
+ last_commit_sha: @last_commit_sha,
path: blob.path,
name: blob.name,
extension: blob.extension,
@@ -221,6 +233,10 @@ class Projects::BlobController < Projects::ApplicationController
commits_path: project_commits_path(project, @id),
tree_path: project_tree_path(project, File.join(@ref, tree_path)),
permalink: project_blob_path(project, File.join(@commit.id, @path))
- )
+ }
+
+ json.merge!(blob_json(@blob) || {}) unless params[:viewer] == 'none'
+
+ render json: json
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index b7b36f770f5..cd7250b10fc 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -31,7 +31,10 @@ class Projects::BranchesController < Projects::ApplicationController
end
end
- render
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/48097
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ render
+ end
end
format.json do
branches = BranchesFinder.new(@repository, params).execute
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 35c36c725e2..7c897b2d86c 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -4,6 +4,7 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableActions
include ToggleAwardEmoji
include IssuableCollections
+ include IssuesCalendar
include SpammableActions
prepend_before_action :authenticate_user!, only: [:new]
@@ -40,14 +41,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def calendar
- @issues = @issuables
- .non_archived
- .with_due_date
- .limit(100)
-
- respond_to do |format|
- format.ics { response.headers['Content-Disposition'] = 'inline' }
- end
+ render_issues_calendar(@issuables)
end
def new
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index b452bfd7e6f..38918b3cd52 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -115,7 +115,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
format.json do
- render json: @merge_request.to_json(include: { milestone: {}, assignee: { only: [:name, :username], methods: [:avatar_url] }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
+ render json: serializer.represent(@merge_request, serializer: 'basic')
end
end
rescue ActiveRecord::StaleObjectError
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 2494b56981d..f85dcfe6bfc 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -123,9 +123,9 @@ class Projects::MilestonesController < Projects::ApplicationController
def search_params
if request.format.json? && @project.group && can?(current_user, :read_group, @project.group)
- groups = @project.group.self_and_ancestors
+ groups = @project.group.self_and_ancestors_ids
end
- params.permit(:state).merge(project_ids: @project.id, group_ids: groups&.select(:id))
+ params.permit(:state).merge(project_ids: @project.id, group_ids: groups)
end
end
diff --git a/app/graphql/resolvers/merge_request_resolver.rb b/app/graphql/resolvers/merge_request_resolver.rb
index b1857ab09f7..9f2d348e95f 100644
--- a/app/graphql/resolvers/merge_request_resolver.rb
+++ b/app/graphql/resolvers/merge_request_resolver.rb
@@ -1,15 +1,14 @@
module Resolvers
class MergeRequestResolver < BaseResolver
- prepend FullPathResolver
-
- type Types::ProjectType, null: true
-
argument :iid, GraphQL::ID_TYPE,
required: true,
description: 'The IID of the merge request, e.g., "1"'
- def resolve(full_path:, iid:)
- project = model_by_full_path(Project, full_path)
+ type Types::MergeRequestType, null: true
+
+ alias_method :project, :object
+
+ def resolve(iid:)
return unless project.present?
BatchLoader.for(iid.to_s).batch(key: project.id) do |iids, loader|
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 9e885d5845a..d9058ae7431 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -61,5 +61,12 @@ module Types
field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true
field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::BOOLEAN_TYPE, null: true
field :printing_merge_request_link_enabled, GraphQL::BOOLEAN_TYPE, null: true
+
+ field :merge_request,
+ Types::MergeRequestType,
+ null: true,
+ resolver: Resolvers::MergeRequestResolver do
+ authorize :read_merge_request
+ end
end
end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index be79c78bf67..010ec2d7942 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -9,13 +9,6 @@ module Types
authorize :read_project
end
- field :merge_request, Types::MergeRequestType,
- null: true,
- resolver: Resolvers::MergeRequestResolver,
- description: "Find a merge request" do
- authorize :read_merge_request
- end
-
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index d42284868c7..9f501ea55fb 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -157,7 +157,7 @@ module IssuablesHelper
output = ""
output << "Opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
output << content_tag(:strong) do
- author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline-block", tooltip: true)
+ author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-block d-sm-none")
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index daad829faa2..c405e6d117f 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -407,6 +407,7 @@ module ProjectsHelper
@ref || @repository.try(:root_ref)
end
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1235
def sanitize_repo_path(project, message)
return '' unless message.present?
diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb
index b5425295130..3bdc1330d23 100644
--- a/app/models/concerns/redis_cacheable.rb
+++ b/app/models/concerns/redis_cacheable.rb
@@ -48,7 +48,7 @@ module RedisCacheable
def cast_value_from_cache(attribute, value)
if Gitlab.rails5?
- self.class.type_for_attribute(attribute).cast(value)
+ self.class.type_for_attribute(attribute.to_s).cast(value)
else
self.class.column_for_attribute(attribute).type_cast_from_database(value)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index f0d8c40bfea..0d777515536 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -25,6 +25,7 @@ class Project < ActiveRecord::Base
include FastDestroyAll::Helpers
include WithUploads
include BatchDestroyDependentAssociations
+ extend Gitlab::Cache::RequestCache
extend Gitlab::ConfigHelper
@@ -68,7 +69,7 @@ class Project < ActiveRecord::Base
add_authentication_token_field :runners_token
- before_validation :mark_remote_mirrors_for_removal, if: -> { ActiveRecord::Base.connection.table_exists?(:remote_mirrors) }
+ before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? }
before_save :ensure_runners_token
@@ -2013,6 +2014,11 @@ class Project < ActiveRecord::Base
@gitlab_deploy_token ||= deploy_tokens.gitlab_deploy_token
end
+ def any_lfs_file_locks?
+ lfs_file_locks.any?
+ end
+ request_cache(:any_lfs_file_locks?) { self.id }
+
private
def storage
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index ae0debbd3ac..a60b4c7fd0d 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -155,6 +155,7 @@ class ChatNotificationService < Service
end
def notify_for_ref?(data)
+ return true if data[:object_kind] == 'tag_push'
return true if data.dig(:object_attributes, :tag)
return true unless notify_only_default_branch?
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e4202505634..c2f62badbcb 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -154,7 +154,10 @@ class Repository
# Returns a list of commits that are not present in any reference
def new_commits(newrev)
- refs = ::Gitlab::Git::RevList.new(raw, newrev: newrev).new_refs
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
+ refs = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ ::Gitlab::Git::RevList.new(raw, newrev: newrev).new_refs
+ end
refs.map { |sha| commit(sha.strip) }
end
diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb
index e4aec977f01..1c06691026d 100644
--- a/app/serializers/merge_request_basic_entity.rb
+++ b/app/serializers/merge_request_basic_entity.rb
@@ -5,4 +5,8 @@ class MergeRequestBasicEntity < IssuableSidebarEntity
expose :state
expose :source_branch_exists?, as: :source_branch_exists
expose :rebase_in_progress?, as: :rebase_in_progress
+ expose :milestone, using: API::Entities::Milestone
+ expose :labels, using: LabelEntity
+ expose :assignee, using: API::Entities::UserBasic
+ expose :task_status, :task_status_short
end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 925775aea0b..9bdbb2c0d99 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -25,14 +25,12 @@ module Ci
valid = true
- if Feature.enabled?('ci_job_request_with_tags_matcher')
- # pick builds that does not have other tags than runner's one
- builds = builds.matches_tag_ids(runner.tags.ids)
+ # pick builds that does not have other tags than runner's one
+ builds = builds.matches_tag_ids(runner.tags.ids)
- # pick builds that have at least one tag
- unless runner.run_untagged?
- builds = builds.with_any_tags
- end
+ # pick builds that have at least one tag
+ unless runner.run_untagged?
+ builds = builds.with_any_tags
end
builds.find do |build|
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 3e38a8a12d4..aa60661f7f2 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -11,7 +11,7 @@ module Projects
order: { due_date: :asc, title: :asc }
}
- finder_params[:group_ids] = @project.group.self_and_ancestors.select(:id) if @project.group
+ finder_params[:group_ids] = @project.group.self_and_ancestors_ids if @project.group
MilestonesFinder.new(finder_params).execute.select([:iid, :title])
end
diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb
index 09afc63a5aa..3639375d474 100644
--- a/app/uploaders/favicon_uploader.rb
+++ b/app/uploaders/favicon_uploader.rb
@@ -1,17 +1,6 @@
class FaviconUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[png ico].freeze
- include CarrierWave::MiniMagick
-
- version :favicon_main do
- process resize_to_fill: [32, 32]
- process convert: 'png'
-
- def full_filename(filename)
- filename_for_different_format(super(filename), 'png')
- end
- end
-
def extension_whitelist
EXTENSION_WHITELIST
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 94db374040c..a0861870ba4 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -25,7 +25,7 @@
= f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label'
.col-sm-10
- if @appearance.favicon?
- = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview'
+ = image_tag @appearance.favicon_url, class: 'appearance-light-logo-preview'
- if @appearance.persisted?
%br
= link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo"
@@ -33,9 +33,9 @@
= f.hidden_field :favicon_cache
= f.file_field :favicon, class: ''
.hint
- Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}.
+ Maximum file size is 1MB. Image size must be 32x32px. Allowed image formats are #{favicon_extension_whitelist}.
%br
- The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px.
+ Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.
%fieldset.sign-in
%legend
diff --git a/app/views/email_rejection_mailer/rejection.html.haml b/app/views/email_rejection_mailer/rejection.html.haml
index 7f7d841fe21..c4ae7befe4e 100644
--- a/app/views/email_rejection_mailer/rejection.html.haml
+++ b/app/views/email_rejection_mailer/rejection.html.haml
@@ -2,3 +2,4 @@
Unfortunately, your email message to GitLab could not be processed.
= markdown @reason
+= render_if_exists 'shared/additional_email_text'
diff --git a/app/views/email_rejection_mailer/rejection.text.haml b/app/views/email_rejection_mailer/rejection.text.haml
index af518b5b583..0e13b2a6473 100644
--- a/app/views/email_rejection_mailer/rejection.text.haml
+++ b/app/views/email_rejection_mailer/rejection.text.haml
@@ -1,3 +1,4 @@
Unfortunately, your email message to GitLab could not be processed.
\
= @reason
+= render_if_exists 'shared/additional_email_text'
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 845f4046d0d..6abb56ba6d2 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,6 +1,6 @@
- if current_user
.dropdown
- %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
+ %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe')
%span.light Visibility:
- if params[:visibility_level].present?
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 8b0ef3cd87a..5a88619f769 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -18,7 +18,7 @@
- if can_create_subgroups
.btn-group.new-project-subgroup.droplab-dropdown.js-new-project-subgroup{ data: { project_path: new_project_path(namespace_id: @group.id), subgroup_path: new_group_path(parent_id: @group.id) } }
%input.btn.btn-success.dropdown-primary.js-new-group-child{ type: "button", value: new_project_label, data: { action: "new-project" } }
- %button.btn.btn-success.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown" } }
+ %button.btn.btn-success.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown", 'display' => 'static' } }
= icon("caret-down", class: "dropdown-btn-icon")
%ul#new-project-or-subgroup-dropdown.dropdown-menu.dropdown-menu-right{ data: { dropdown: true } }
%li.droplab-item-selected{ role: "button", data: { value: "new-project", text: new_project_label } }
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index de8369ed7b9..b32b602ceb3 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -443,8 +443,6 @@
.col-md-6
.alert.alert-success
= lorem
- .alert.alert-primary
- = lorem
.alert.alert-info
= lorem
.col-md-6
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index f311ac98ac6..cc672a5ea7c 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -20,7 +20,7 @@
- else
.input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
- .input-group-text
+ .input-group-text.border-0
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml
index db8137cc248..d35df706036 100644
--- a/app/views/layouts/header/_new_dropdown.haml
+++ b/app/views/layouts/header/_new_dropdown.haml
@@ -1,5 +1,5 @@
%li.header-new.dropdown
- = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body' } do
+ = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
= sprite_icon('plus-square', size: 16)
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 0a9adc6f243..dd6a84e503d 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -9,6 +9,8 @@
%p
Assignee: #{@merge_request.assignee_name}
+= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request
+
- if @merge_request.description
%div
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb
index 7d98400e6fe..d5b8f8d764f 100644
--- a/app/views/notify/new_merge_request_email.text.erb
+++ b/app/views/notify/new_merge_request_email.text.erb
@@ -5,6 +5,6 @@ New Merge Request <%= @merge_request.to_reference %>
<%= merge_path_description(@merge_request, 'to') %>
Author: <%= @merge_request.author_name %>
Assignee: <%= @merge_request.assignee_name %>
+<%= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request %>
<%= @merge_request.description %>
-
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index cfbd0459e3e..6f957533287 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -16,7 +16,7 @@
- else
.input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
- .input-group-text
+ .input-group-text.border-0
#{user_url(current_user.username)}/
= f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index 9d27f51926e..d08807b5135 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -10,16 +10,18 @@
%a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank' } Preview
.project-fields-form
- .form-group
- %label.label-light
- Template
- .input-group.template-input-group
- .input-group-prepend
- .input-group-text
- .selected-icon
- - Gitlab::ProjectTemplate.all.each do |template|
- = custom_icon(template.logo)
- .selected-template
- %button.btn.btn-default.change-template{ type: "button" } Change template
+ .row
+ .form-group.col-sm-12
+ %label.label-light
+ Template
+ .input-group.template-input-group
+ .input-group-prepend
+ .input-group-text
+ .selected-icon
+ - Gitlab::ProjectTemplate.all.each do |template|
+ = custom_icon(template.logo)
+ .selected-template
+ .input-group-append
+ %button.btn.btn-default.change-template{ type: "button" } Change template
= render 'new_project_fields', f: f, project_name_id: "template-project-name"
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
index 8bffd1396ae..15ec58289e3 100644
--- a/app/views/projects/_stat_anchor_list.html.haml
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -5,4 +5,4 @@
- anchors.each do |anchor|
%li.nav-item
= link_to_if anchor.link, anchor.label, anchor.link, class: anchor.enabled ? 'nav-link stat-link' : "nav-link btn btn-#{anchor.class_modifier || 'missing'}" do
- %span.stat-text= anchor.label
+ .stat-text= anchor.label
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 1b150ec3e5c..0a0b3ce1d6f 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -11,6 +11,7 @@
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
.btn-group{ role: "group" }<
+ = render_if_exists 'projects/blob/header_file_locks_link'
= edit_blob_button
= ide_edit_button
- if current_user
@@ -18,3 +19,4 @@
= delete_blob_link
= render 'projects/fork_suggestion'
+= render_if_exists 'projects/blob/header_file_locks', project: @project, path: @path
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index c75093c4c24..f7551434d47 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -3,7 +3,7 @@
- if !project.empty_repo? && can?(current_user, :download_code, project)
- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}"
.project-action-button.dropdown.inline>
- %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
+ %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static' }
= sprite_icon('download')
= icon("caret-down")
%span.sr-only= _('Select Archive Format')
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 84245d72f4a..8b9c52f0802 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -8,7 +8,7 @@
- if show_menu
.project-action-button.dropdown.inline
- %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...') }
+ %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
= icon('plus')
= icon("caret-down")
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
diff --git a/app/views/projects/clusters/_sidebar.html.haml b/app/views/projects/clusters/_sidebar.html.haml
index 73cd7c50922..3d10348212f 100644
--- a/app/views/projects/clusters/_sidebar.html.haml
+++ b/app/views/projects/clusters/_sidebar.html.haml
@@ -1,7 +1,9 @@
+- clusters_help_url = help_page_path('user/project/clusters/index.md')
+- help_link_start = "<a href=\"%{url}\" target=\"_blank\" rel=\"noopener noreferrer\">".html_safe
+- help_link_end = '</a>'.html_safe
%h4.prepend-top-0
= s_('ClusterIntegration|Kubernetes cluster integration')
%p
= s_('ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way.')
%p
- - link = link_to(_('Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
- = s_('ClusterIntegration|Learn more about %{link_to_documentation}').html_safe % { link_to_documentation: link }
+ = s_('ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: clusters_help_url }, help_link_end: help_link_end }
diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml
index 59c4eeec17a..b8e40b0a38b 100644
--- a/app/views/projects/clusters/gcp/_form.html.haml
+++ b/app/views/projects/clusters/gcp/_form.html.haml
@@ -1,4 +1,10 @@
= javascript_include_tag 'https://apis.google.com/js/api.js'
+- external_link_icon = icon('external-link')
+- zones_link_url = 'https://cloud.google.com/compute/docs/regions-zones/regions-zones'
+- machine_type_link_url = 'https://cloud.google.com/compute/docs/machine-types'
+- pricing_link_url = 'https://cloud.google.com/compute/pricing#machinetype'
+- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
+- help_link_end = ' %{external_link_icon}</a>'.html_safe % { external_link_icon: external_link_icon }
%p
- link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
@@ -7,15 +13,15 @@
= form_for @cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
= form_errors(@cluster)
.form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name')
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
.form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope')
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light'
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
= field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
.form-group
- = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project')
+ = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-light'
.js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
= provider_gcp_field.hidden_field :gcp_project_id
.dropdown
@@ -26,8 +32,7 @@
%span.form-text.text-muted &nbsp;
.form-group
- = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone')
- = link_to(s_('ClusterIntegration|See zones'), 'https://cloud.google.com/compute/docs/regions-zones/regions-zones', target: '_blank', rel: 'noopener noreferrer')
+ = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-light'
.js-gcp-zone-dropdown-entry-point
= provider_gcp_field.hidden_field :zone
.dropdown
@@ -35,13 +40,15 @@
%span.dropdown-toggle-text
= _('Select project to choose zone')
= icon('chevron-down')
+ %p.form-text.text-muted
+ = s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end }
.form-group
- = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes')
+ = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-light'
= provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
.form-group
- = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type')
+ = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-light'
.js-gcp-machine-type-dropdown-entry-point
= provider_gcp_field.hidden_field :machine_type
.dropdown
@@ -49,6 +56,8 @@
%span.dropdown-toggle-text
= _('Select project and zone to choose machine type')
= icon('chevron-down')
+ %p.form-text.text-muted
+ = s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
.form-group
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 12b27eb9b66..90e55fd0fb0 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -34,7 +34,8 @@
.d-block.d-sm-none
= render_commit_status(commit, ref: ref)
- if commit.description?
- %button.text-expander.d-none.d-sm-inline-block.js-toggle-button{ type: "button" } ...
+ %button.text-expander.js-toggle-button
+ = sprite_icon('ellipsis_h', size: 12)
.commiter
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
diff --git a/app/views/projects/deploy_tokens/_index.html.haml b/app/views/projects/deploy_tokens/_index.html.haml
index 50e5950ced4..725720d2222 100644
--- a/app/views/projects/deploy_tokens/_index.html.haml
+++ b/app/views/projects/deploy_tokens/_index.html.haml
@@ -10,9 +10,8 @@
.settings-content
- if @new_deploy_token.persisted?
= render 'projects/deploy_tokens/new_deploy_token', deploy_token: @new_deploy_token
- - else
- %h5.prepend-top-0
- = s_('DeployTokens|Add a deploy token')
- = render 'projects/deploy_tokens/form', project: @project, token: @new_deploy_token, presenter: @deploy_tokens
- %hr
+ %h5.prepend-top-0
+ = s_('DeployTokens|Add a deploy token')
+ = render 'projects/deploy_tokens/form', project: @project, token: @new_deploy_token, presenter: @deploy_tokens
+ %hr
= render 'projects/deploy_tokens/table', project: @project, active_tokens: @deploy_tokens
diff --git a/app/views/projects/deploy_tokens/_new_deploy_token.html.haml b/app/views/projects/deploy_tokens/_new_deploy_token.html.haml
index 1e715681e59..5dd9ffba074 100644
--- a/app/views/projects/deploy_tokens/_new_deploy_token.html.haml
+++ b/app/views/projects/deploy_tokens/_new_deploy_token.html.haml
@@ -1,14 +1,18 @@
-.created-deploy-token-container
- %h5.prepend-top-0
- = s_('DeployTokens|Your New Deploy Token')
+.created-deploy-token-container.info-well
+ .well-segment
+ %h5.prepend-top-0
+ = s_('DeployTokens|Your New Deploy Token')
- .form-group
- = text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
- = clipboard_button(text: deploy_token.username, title: s_('DeployTokens|Copy username to clipboard'), placement: 'left')
- %span.deploy-token-help-block.prepend-top-5.text-success= s_("DeployTokens|Use this username as a login.")
+ .form-group
+ .input-group
+ = text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
+ .input-group-append
+ = clipboard_button(text: deploy_token.username, title: s_('DeployTokens|Copy username to clipboard'), placement: 'left')
+ %span.deploy-token-help-block.prepend-top-5.text-success= s_("DeployTokens|Use this username as a login.")
- .form-group
- = text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
- = clipboard_button(text: deploy_token.token, title: s_('DeployTokens|Copy deploy token to clipboard'), placement: 'left')
- %span.deploy-token-help-block.prepend-top-5.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
-%hr
+ .form-group
+ .input-group
+ = text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
+ .input-group-append
+ = clipboard_button(text: deploy_token.token, title: s_('DeployTokens|Copy deploy token to clipboard'), placement: 'left')
+ %span.deploy-token-help-block.prepend-top-5.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
diff --git a/app/views/projects/diffs/_collapsed.html.haml b/app/views/projects/diffs/_collapsed.html.haml
index 5762f4d86d7..9bd1255fe00 100644
--- a/app/views/projects/diffs/_collapsed.html.haml
+++ b/app/views/projects/diffs/_collapsed.html.haml
@@ -2,4 +2,4 @@
- url = url_for(safe_params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
.nothing-here-block.diff-collapsed{ data: { diff_for_path: url } }
This diff is collapsed.
- %a.click-to-expand Click to expand it.
+ %button.click-to-expand.btn.btn-link Click to expand it.
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 69753427d17..d47dc3d8143 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -1,3 +1,4 @@
+- @content_class = "limit-container-width" unless fluid_layout
- @no_container = true
- breadcrumb_title _("Details")
@@ -6,7 +7,7 @@
= render "home_panel"
.project-empty-note-panel
- %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
+ %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.prepend-top-20
%h4
= _('The repository for this project is empty')
@@ -36,7 +37,7 @@
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project)
- %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
+ %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.prepend-top-20
.empty_wrapper
%h3#repo-command-line-instructions.page-title-empty
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index 76438fae663..a678cb6f058 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -18,7 +18,7 @@
%button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
= value
- %button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' } } }
+ %button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
= icon('caret-down')
.droplab-dropdown
@@ -40,7 +40,7 @@
%label{ for: 'new-branch-name' }
= _('Branch name')
%input#new-branch-name.js-branch-name.form-control{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" }
- %span.js-branch-message.form-text.text-muted
+ %span.js-branch-message.form-text
.form-group
%label{ for: 'source-name' }
diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml
index ebcd99f2a9b..b2eacabc21a 100644
--- a/app/views/projects/merge_requests/creations/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml
@@ -25,7 +25,7 @@
= custom_icon ('illustration_no_commits')
- else
%ul.merge-request-tabs.nav.nav-tabs.nav-links.no-top.no-bottom
- %li.commits-tab.active
+ %li.commits-tab
= link_to url_for(safe_params), data: {target: 'div#commits', action: 'new', toggle: 'tab'} do
Commits
%span.badge.badge-pill= @commits.size
diff --git a/app/views/projects/merge_requests/diffs/_version_controls.html.haml b/app/views/projects/merge_requests/diffs/_version_controls.html.haml
index 1c26f0405d2..52bf584d550 100644
--- a/app/views/projects/merge_requests/diffs/_version_controls.html.haml
+++ b/app/views/projects/merge_requests/diffs/_version_controls.html.haml
@@ -3,7 +3,7 @@
.mr-version-menus-container.content-block
Changes between
%span.dropdown.inline.mr-version-dropdown
- %a.dropdown-toggle.btn.btn-default{ data: {toggle: :dropdown} }
+ %a.dropdown-toggle.btn.btn-default{ data: { toggle: :dropdown, display: 'static' } }
%span
- if @merge_request_diff.latest?
latest version
@@ -36,7 +36,7 @@
- if @merge_request_diff.base_commit_sha
and
%span.dropdown.inline.mr-version-compare-dropdown
- %a.btn.btn-default.dropdown-toggle{ data: {toggle: :dropdown} }
+ %a.btn.btn-default.dropdown-toggle{ data: { toggle: :dropdown, display: 'static' } }
- if @start_version
version #{version_index(@start_version)}
- else
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index b478fbbb15e..f7b04c436a6 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -69,6 +69,8 @@
.wiki
= markdown_field(@milestone, :description)
+ = render_if_exists 'shared/milestones/burndown', milestone: @milestone, project: @project
+
- if can?(current_user, :read_issue, @project) && @milestone.total_items_count(current_user).zero?
.alert.alert-success.prepend-top-default
%span Assign some issues to this milestone.
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index aa53fc3ea28..04131a90a57 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -29,7 +29,7 @@
= link_to @commit.short_id, project_commit_path(@project, @pipeline.sha), class: "commit-sha js-details-short"
= link_to("#", class: "js-details-expand d-none d-sm-none d-md-inline") do
%span.text-expander
- \...
+ = sprite_icon('ellipsis_h', size: 12)
%span.js-details-content.hide
= link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
= clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
diff --git a/app/views/projects/refs/logs_tree.js.haml b/app/views/projects/refs/logs_tree.js.haml
index d07bb661615..506bf54b3f8 100644
--- a/app/views/projects/refs/logs_tree.js.haml
+++ b/app/views/projects/refs/logs_tree.js.haml
@@ -8,6 +8,8 @@
row.find("td.tree-time-ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
row.find("td.tree-commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
+ = render_if_exists 'projects/refs/logs_tree_lock_label', lock_label: content_data[:lock_label]
+
- if @more_log_url
:plain
if($('#tree-slider').length) {
diff --git a/app/views/projects/services/_index.html.haml b/app/views/projects/services/_index.html.haml
index acbab8b85c9..16e48814578 100644
--- a/app/views/projects/services/_index.html.haml
+++ b/app/views/projects/services/_index.html.haml
@@ -8,7 +8,7 @@
%colgroup
%col
%col
- %col.d-none.d-sm-block
+ %col
%col{ width: "120" }
%thead
%tr
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 35c7dc2984a..d80d2957466 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,4 +1,4 @@
-- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
+- @content_class = "limit-container-width" unless fluid_layout
- page_title _("Edit"), @page.title.capitalize, _("Wiki")
= wiki_page_errors(@error)
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 909987d8090..8c2cbd495a0 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,4 +1,4 @@
-- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
+- @content_class = "limit-container-width" unless fluid_layout
- page_title s_("WikiClone|Git Access"), _("Wiki")
.wiki-page-header.has-sidebar-toggle
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index ff72c8bb75d..a08973c7f32 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,4 +1,4 @@
-- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
+- @content_class = "limit-container-width" unless fluid_layout
- breadcrumb_title @page.title.capitalize
- wiki_breadcrumb_dropdown_links(@page.slug)
- page_title @page.title.capitalize, _("Wiki")
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 5fc02ba3160..3655c2a1d42 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -11,7 +11,7 @@
%span
= default_clone_protocol.upcase
= icon('caret-down')
- %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown
+ %ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
%li
= ssh_clone_button(project)
%li
diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml
index a112a9f1f7e..daee691e358 100644
--- a/app/views/shared/boards/components/sidebar/_labels.html.haml
+++ b/app/views/shared/boards/components/sidebar/_labels.html.haml
@@ -9,7 +9,7 @@
None
%a{ href: "#",
"v-for" => "label in issue.labels" }
- %span.badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
+ .badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
{{ label.title }}
- if can_admin_issue?
.selectbox
diff --git a/app/views/shared/builds/_build_output.html.haml b/app/views/shared/builds/_build_output.html.haml
index 07f1501fadd..0e18128a8f1 100644
--- a/app/views/shared/builds/_build_output.html.haml
+++ b/app/views/shared/builds/_build_output.html.haml
@@ -1,3 +1,3 @@
%pre.build-trace#build-trace
%code.bash.js-build-output
- .build-loader-animation.js-build-refresh
+ .build-loader-animation.js-build-refresh
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 955b8866c2c..37625a4a163 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -1,6 +1,8 @@
- project = @target_project || @project
- extra_class = extra_class || ''
- show_menu_above = show_menu_above || false
+- selected = local_assigns.fetch(:selected, nil)
+
- selected_text = selected.try(:title) || params[:milestone_title]
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
- if selected.present? || params[:milestone_title].present?
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index ed3ef6155db..8a13c7a3b83 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -50,7 +50,7 @@
- data[:multi_select] = true
- data['dropdown-title'] = title
- data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
- - data['max-select'] = dropdown_options[:data][:'max-select']
+ - data['max-select'] = dropdown_options[:data][:'max-select'] if dropdown_options[:data][:'max-select']
- options[:data].merge!(data)
= dropdown_tag(title, options: options)
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index 01fbc163a14..bd87bb38e77 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -25,7 +25,10 @@
= form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-md-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
+
+ = render_if_exists "shared/issuable/form/weight", issuable: issuable, form: form
+
- if has_due_date
.col-lg-6
.form-group.row
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index ca6e3602f05..526330f4e50 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -36,8 +36,6 @@
= note.author.to_reference
%span.note-headline-light
%span.note-headline-meta
- - unless note.system
- commented
- if note.system
%span.system-note-message
= markdown_field(note, :note)
diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml
index e99d8d0973f..09ddf732ada 100644
--- a/app/views/shared/notifications/_button.html.haml
+++ b/app/views/shared/notifications/_button.html.haml
@@ -6,14 +6,14 @@
.js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom?
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting) } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting) } }
= icon('caret-down')
.sr-only Toggle dropdown
- else
- %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting) } }
+ %button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
= icon("caret-down")
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index db48bb7e8b8..dbb215f1964 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -8,28 +8,12 @@ class RepositoryForkWorker
target_project_id = args.shift
target_project = Project.find(target_project_id)
- # By v10.8, we should've drained the queue of all jobs using the old arguments.
- # We can remove the else clause if we're no longer logging the message in that clause.
- # See https://gitlab.com/gitlab-org/gitaly/issues/1110
- if args.empty?
- source_project = target_project.forked_from_project
- unless source_project
- return target_project.mark_import_as_failed('Source project cannot be found.')
- end
-
- fork_repository(target_project, source_project.repository_storage, source_project.disk_path)
- else
- Rails.logger.info("Project #{target_project.id} is being forked using old-style arguments.")
-
- source_repository_storage_path, source_disk_path = *args
-
- source_repository_storage_name = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages.find do |_, info|
- info.legacy_disk_path == source_repository_storage_path
- end&.first || raise("no shard found for path '#{source_repository_storage_path}'")
- end
- fork_repository(target_project, source_repository_storage_name, source_disk_path)
+ source_project = target_project.forked_from_project
+ unless source_project
+ return target_project.mark_import_as_failed('Source project cannot be found.')
end
+
+ fork_repository(target_project, source_project.repository_storage, source_project.disk_path)
end
private
diff --git a/changelogs/unreleased/45487-slack-tag-push-notifs.yml b/changelogs/unreleased/45487-slack-tag-push-notifs.yml
new file mode 100644
index 00000000000..647000bd97c
--- /dev/null
+++ b/changelogs/unreleased/45487-slack-tag-push-notifs.yml
@@ -0,0 +1,5 @@
+---
+title: Fix chat service tag notifications not sending when only default branch enabled
+merge_request: 19864
+author:
+type: fixed
diff --git a/changelogs/unreleased/45557-machine-type-help-links.yml b/changelogs/unreleased/45557-machine-type-help-links.yml
new file mode 100644
index 00000000000..870a650e10b
--- /dev/null
+++ b/changelogs/unreleased/45557-machine-type-help-links.yml
@@ -0,0 +1,6 @@
+---
+title: Add machine type and pricing documentation links, add class to labels to make
+ bold
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml b/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml
new file mode 100644
index 00000000000..b564fb0174f
--- /dev/null
+++ b/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml
@@ -0,0 +1,5 @@
+---
+title: Allows you to create another deploy token dimmediately after creating one
+merge_request: 19639
+author:
+type: changed
diff --git a/changelogs/unreleased/46861-issuable-title-with-longer-username.yml b/changelogs/unreleased/46861-issuable-title-with-longer-username.yml
new file mode 100644
index 00000000000..9df6879deb6
--- /dev/null
+++ b/changelogs/unreleased/46861-issuable-title-with-longer-username.yml
@@ -0,0 +1,5 @@
+---
+title: Fix CSS for buttons not to be hidden on issues/MR title
+merge_request: 19176
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/47646-ui-glitch.yml b/changelogs/unreleased/47646-ui-glitch.yml
new file mode 100644
index 00000000000..384df4e2cc9
--- /dev/null
+++ b/changelogs/unreleased/47646-ui-glitch.yml
@@ -0,0 +1,5 @@
+---
+title: Line height fixed
+merge_request:
+author: Murat Dogan
+type: fixed
diff --git a/changelogs/unreleased/47672-set_inline_content_type_for_ics.yml b/changelogs/unreleased/47672-set_inline_content_type_for_ics.yml
new file mode 100644
index 00000000000..4bc883d41bd
--- /dev/null
+++ b/changelogs/unreleased/47672-set_inline_content_type_for_ics.yml
@@ -0,0 +1,5 @@
+---
+title: Render calendar feed inline when accessed from GitLab
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/47871-new-mr-tab-active-state.yml b/changelogs/unreleased/47871-new-mr-tab-active-state.yml
new file mode 100644
index 00000000000..c3fc8672d82
--- /dev/null
+++ b/changelogs/unreleased/47871-new-mr-tab-active-state.yml
@@ -0,0 +1,5 @@
+---
+title: Fix active tab highlight when creating new merge request
+merge_request: 19781
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/48050-add-full-commit-sha.yml b/changelogs/unreleased/48050-add-full-commit-sha.yml
new file mode 100644
index 00000000000..30376fe35e0
--- /dev/null
+++ b/changelogs/unreleased/48050-add-full-commit-sha.yml
@@ -0,0 +1,5 @@
+---
+title: Uses long sha version of the merged commit in MR widget copy to clipboard button
+merge_request: 19955
+author:
+type: other
diff --git a/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml b/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml
new file mode 100644
index 00000000000..e7bb2703b03
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix sessions_controller_spec"
+merge_request: 19936
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml b/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml
new file mode 100644
index 00000000000..fad15de2dd5
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Set request.format for artifacts_controller"
+merge_request: 19937
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml b/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml
new file mode 100644
index 00000000000..a83aa03606a
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Explicitly set request.format for blob_controller"
+merge_request: 19876
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml
new file mode 100644
index 00000000000..1915dff73ab
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix optimistic lock value"
+merge_request: 19878
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml b/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml
new file mode 100644
index 00000000000..7a2b19ad681
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix pipeline_schedules_controller_spec"
+merge_request: 19919
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml b/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml
new file mode 100644
index 00000000000..92e6ce35941
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml
@@ -0,0 +1,6 @@
+---
+title: "[Rails5] Invalid single-table inheritance type: Group is not a subclass of
+ Namespace"
+merge_request: 19918
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/bvl-graphql-nested-merge-request.yml b/changelogs/unreleased/bvl-graphql-nested-merge-request.yml
new file mode 100644
index 00000000000..f0f0488d31a
--- /dev/null
+++ b/changelogs/unreleased/bvl-graphql-nested-merge-request.yml
@@ -0,0 +1,5 @@
+---
+title: Allow querying a single merge request within a project
+merge_request: 19853
+author:
+type: changed
diff --git a/changelogs/unreleased/ce-5024-filename-search.yml b/changelogs/unreleased/ce-5024-filename-search.yml
new file mode 100644
index 00000000000..a8bf9b1f802
--- /dev/null
+++ b/changelogs/unreleased/ce-5024-filename-search.yml
@@ -0,0 +1,5 @@
+---
+title: Add filename filtering to code search
+merge_request: 19509
+author:
+type: added
diff --git a/changelogs/unreleased/dm-blockquote-trailing-whitespace.yml b/changelogs/unreleased/dm-blockquote-trailing-whitespace.yml
new file mode 100644
index 00000000000..98ecdde4f4c
--- /dev/null
+++ b/changelogs/unreleased/dm-blockquote-trailing-whitespace.yml
@@ -0,0 +1,5 @@
+---
+title: Allow trailing whitespace on blockquote fence lines
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/dm-branch-api-can-push.yml b/changelogs/unreleased/dm-branch-api-can-push.yml
new file mode 100644
index 00000000000..3be8962089b
--- /dev/null
+++ b/changelogs/unreleased/dm-branch-api-can-push.yml
@@ -0,0 +1,5 @@
+---
+title: Expose whether current user can push into a branch on branches API
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/fix-groups-api-ordering.yml b/changelogs/unreleased/fix-groups-api-ordering.yml
new file mode 100644
index 00000000000..3a6a7f84356
--- /dev/null
+++ b/changelogs/unreleased/fix-groups-api-ordering.yml
@@ -0,0 +1,4 @@
+title: Fixed pagination of groups API
+merge_request: 19665
+author: Marko, Peter
+type: added
diff --git a/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml b/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml
new file mode 100644
index 00000000000..6a4d9b6c8c4
--- /dev/null
+++ b/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml
@@ -0,0 +1,5 @@
+---
+title: Disabled Web IDE autocomplete suggestions for Markdown files.
+merge_request:
+author: Isaac Smith
+type: fixed
diff --git a/changelogs/unreleased/ide-commit-actions-update.yml b/changelogs/unreleased/ide-commit-actions-update.yml
new file mode 100644
index 00000000000..35bee94e156
--- /dev/null
+++ b/changelogs/unreleased/ide-commit-actions-update.yml
@@ -0,0 +1,5 @@
+---
+title: Improve Web IDE commit flow
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/more-group-api-sorting-options.yml b/changelogs/unreleased/more-group-api-sorting-options.yml
new file mode 100644
index 00000000000..b29f76a65a9
--- /dev/null
+++ b/changelogs/unreleased/more-group-api-sorting-options.yml
@@ -0,0 +1,5 @@
+---
+title: Added id sorting option to GET groups and subgroups API
+merge_request: 19665
+author: Marko, Peter
+type: added
diff --git a/changelogs/unreleased/optimise-paused-runners.yml b/changelogs/unreleased/optimise-paused-runners.yml
new file mode 100644
index 00000000000..13097e507d3
--- /dev/null
+++ b/changelogs/unreleased/optimise-paused-runners.yml
@@ -0,0 +1,5 @@
+---
+title: Optimise paused runners to reduce amount of used requests
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/rails5-fix-46276.yml b/changelogs/unreleased/rails5-fix-46276.yml
new file mode 100644
index 00000000000..cdca91a755d
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-46276.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix format in uploads actions
+merge_request: 19907
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47836.yml b/changelogs/unreleased/rails5-fix-47836.yml
new file mode 100644
index 00000000000..2aef2db607a
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47836.yml
@@ -0,0 +1,6 @@
+---
+title: Rails5 fix passing Group objects array into for_projects_and_groups milestone
+ scope
+merge_request: 19863
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47960.yml b/changelogs/unreleased/rails5-fix-47960.yml
new file mode 100644
index 00000000000..2229511ccd6
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47960.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix update_attribute usage not causing a save
+merge_request: 19881
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48009.yml b/changelogs/unreleased/rails5-fix-48009.yml
new file mode 100644
index 00000000000..7ade9ef2b7d
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-48009.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 update Gemfile.rails5.lock
+merge_request: 19921
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48012.yml b/changelogs/unreleased/rails5-fix-48012.yml
new file mode 100644
index 00000000000..953ccbd8af7
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-48012.yml
@@ -0,0 +1,6 @@
+---
+title: Rails5 fix passing Group objects array into for_projects_and_groups milestone
+ scope
+merge_request: 19920
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-48104.yml b/changelogs/unreleased/rails5-fix-48104.yml
new file mode 100644
index 00000000000..6cf519ad791
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-48104.yml
@@ -0,0 +1,6 @@
+---
+title: 'Rails5 fix expected: 1 time with arguments: (97, anything, {"squash"=>false})
+ received: 0 times'
+merge_request: 20004
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-db-check.yml b/changelogs/unreleased/rails5-fix-db-check.yml
new file mode 100644
index 00000000000..ccac4619ea7
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-db-check.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix connection execute return integer instead of string
+merge_request: 19901
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-pages-controller.yml b/changelogs/unreleased/rails5-fix-pages-controller.yml
new file mode 100644
index 00000000000..eeb3747c4eb
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-pages-controller.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix Projects::PagesController spec
+merge_request: 20007
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml b/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml
new file mode 100644
index 00000000000..b86512445d5
--- /dev/null
+++ b/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml
@@ -0,0 +1,5 @@
+---
+title: Remove the ci_job_request_with_tags_matcher
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml b/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml
new file mode 100644
index 00000000000..40ec3998b05
--- /dev/null
+++ b/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml
@@ -0,0 +1,5 @@
+---
+title: Change label link vertical alignment property
+merge_request: 18777
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/remove-small-container-width.yml b/changelogs/unreleased/remove-small-container-width.yml
new file mode 100644
index 00000000000..1af8aafa87e
--- /dev/null
+++ b/changelogs/unreleased/remove-small-container-width.yml
@@ -0,0 +1,5 @@
+---
+title: Remove small container width
+merge_request: 19893
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/safari-scrollbar-bug.yml b/changelogs/unreleased/safari-scrollbar-bug.yml
new file mode 100644
index 00000000000..792a66d1ada
--- /dev/null
+++ b/changelogs/unreleased/safari-scrollbar-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Remove scrollbar in Safari in repo settings page
+merge_request: 19809
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/sh-optimize-locks-check-ce.yml b/changelogs/unreleased/sh-optimize-locks-check-ce.yml
new file mode 100644
index 00000000000..933ec9b79bf
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-locks-check-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Eliminate N+1 queries in LFS file locks checks during a push
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/text-expander-icon-update.yml b/changelogs/unreleased/text-expander-icon-update.yml
new file mode 100644
index 00000000000..be9dc98728f
--- /dev/null
+++ b/changelogs/unreleased/text-expander-icon-update.yml
@@ -0,0 +1,5 @@
+---
+title: Updated the icon for expand buttons to ellipsis
+merge_request: 18793
+author: Constance Okoghenun
+type: changed \ No newline at end of file
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 12d09150127..3d3448cb4d6 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -394,6 +394,7 @@ repositories_storages = Settings.repositories.storages.values
repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(%r{/$}, '')
repository_downloads_full_path = File.expand_path(repository_downloads_path, Settings.gitlab['user_home'])
+# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1237
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs.legacy_disk_path.gsub(%r{/$}, '')) }
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive')
diff --git a/config/initializers/6_validations.rb b/config/initializers/6_validations.rb
index 362a23164ab..ff6865608f0 100644
--- a/config/initializers/6_validations.rb
+++ b/config/initializers/6_validations.rb
@@ -37,6 +37,7 @@ def validate_storages_config
end
end
+# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1237
def validate_storages_paths
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages.each do |name, repository_storage|
diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb
index 3e7111fd063..0861544c5a4 100644
--- a/config/initializers/active_record_locking.rb
+++ b/config/initializers/active_record_locking.rb
@@ -1,73 +1,80 @@
# rubocop:disable Lint/RescueException
-# Remove this entire initializer when we are at rails 5.0.
-# This file fixes the bug (see below) which has been fixed in the upstream.
-unless Gitlab.rails5?
- # This patch fixes https://github.com/rails/rails/issues/26024
- # TODO: Remove it when it's no longer necessary
-
- module ActiveRecord
- module Locking
- module Optimistic
- # We overwrite this method because we don't want to have default value
- # for newly created records
- def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
- super
- end
+# Remove this monkey-patch when all lock_version values are converted from NULLs to zeros.
+# See https://gitlab.com/gitlab-org/gitlab-ce/issues/25228
+module ActiveRecord
+ module Locking
+ module Optimistic
+ # We overwrite this method because we don't want to have default value
+ # for newly created records
+ def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
+ super
+ end
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
- return super unless locking_enabled?
- return 0 if attribute_names.empty?
+ def _update_record(attribute_names = self.attribute_names) #:nodoc:
+ return super unless locking_enabled?
+ return 0 if attribute_names.empty?
- lock_col = self.class.locking_column
+ lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
+ previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
- # This line is added as a patch
- previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
+ # This line is added as a patch
+ previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
- increment_lock
+ increment_lock
- attribute_names += [lock_col]
- attribute_names.uniq!
+ attribute_names += [lock_col]
+ attribute_names.uniq!
- begin
- relation = self.class.unscoped
+ begin
+ relation = self.class.unscoped
- affected_rows = relation.where(
- self.class.primary_key => id,
- lock_col => previous_lock_value
- ).update_all(
- attributes_for_update(attribute_names).map do |name|
- [name, _read_attribute(name)]
- end.to_h
- )
+ affected_rows = relation.where(
+ self.class.primary_key => id,
+ lock_col => previous_lock_value
+ ).update_all(
+ attributes_for_update(attribute_names).map do |name|
+ [name, _read_attribute(name)]
+ end.to_h
+ )
- unless affected_rows == 1
- raise ActiveRecord::StaleObjectError.new(self, "update")
- end
+ unless affected_rows == 1
+ raise ActiveRecord::StaleObjectError.new(self, "update")
+ end
- affected_rows
+ affected_rows
- # If something went wrong, revert the version.
- rescue Exception
- send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
- raise
- end
+ # If something went wrong, revert the version.
+ rescue Exception
+ send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
+ raise
end
+ end
- # This is patched because we need it to query `lock_version IS NULL`
- # rather than `lock_version = 0` whenever lock_version is NULL.
- def relation_for_destroy
- return super unless locking_enabled?
+ # This is patched because we need it to query `lock_version IS NULL`
+ # rather than `lock_version = 0` whenever lock_version is NULL.
+ def relation_for_destroy
+ return super unless locking_enabled?
- column_name = self.class.locking_column
- super.where(self.class.arel_table[column_name].eq(self[column_name]))
- end
+ column_name = self.class.locking_column
+ super.where(self.class.arel_table[column_name].eq(self[column_name]))
end
+ end
+
+ # This is patched because we want `lock_version` default to `NULL`
+ # rather than `0`
+ if Gitlab.rails5?
+ class LockingType
+ def deserialize(value)
+ super
+ end
- # This is patched because we want `lock_version` default to `NULL`
- # rather than `0`
+ def serialize(value)
+ super
+ end
+ end
+ else
class LockingType < SimpleDelegator
def type_cast_from_database(value)
super
diff --git a/config/initializers/active_record_migration.rb b/config/initializers/active_record_migration.rb
new file mode 100644
index 00000000000..04c06be7834
--- /dev/null
+++ b/config/initializers/active_record_migration.rb
@@ -0,0 +1,10 @@
+require 'active_record/migration'
+
+module ActiveRecord
+ class Migration
+ # data_source_exists? is not available in 4.2.10, table_exists deprecated in 5.0
+ def table_exists?(table_name)
+ ActiveRecord::Base.connection.data_source_exists?(table_name)
+ end
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 52726f94753..e0a9139b1b4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -11,6 +11,12 @@ Rails.application.routes.draw do
post :toggle_award_emoji, on: :member
end
+ favicon_redirect = redirect do |_params, _request|
+ ActionController::Base.helpers.asset_url(Gitlab::Favicon.main)
+ end
+ get 'favicon.png', to: favicon_redirect
+ get 'favicon.ico', to: favicon_redirect
+
draw :sherlock
draw :development
draw :ci
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
index aa5e9513290..621d4f77d5e 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
@@ -107,7 +107,7 @@ Global Admins GitLab.org/GitLab INT/Global Groups/Global Admins
## GitLab LDAP configuration
-The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb` configuration file. Below is an example of a complete configuration using an Active Directory.
+The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb` configuration file (`/etc/gitlab/gitlab.rb`). Below is an example of a complete configuration using an Active Directory.
The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix)
diff --git a/doc/api/README.md b/doc/api/README.md
index 1c756dc855f..6267618d3bc 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -104,7 +104,7 @@ with a major point release of GitLab itself. All deprecations and changes
between two versions should be listed in the documentation. For the changes
between v3 and v4; please read the [v3 to v4 documentation](v3_to_v4.md)
-#### Current status
+### Current status
Currently only API version v4 is available. Version v3 was removed in
[GitLab 11.0](https://gitlab.com/gitlab-org/gitlab-ce/issues/36819).
diff --git a/doc/api/branches.md b/doc/api/branches.md
index 01bb30c3859..bfb21608d28 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -29,6 +29,7 @@ Example response:
"protected": true,
"developers_can_push": false,
"developers_can_merge": false,
+ "can_push": true,
"commit": {
"author_email": "john@example.com",
"author_name": "John Smith",
@@ -76,6 +77,7 @@ Example response:
"protected": true,
"developers_can_push": false,
"developers_can_merge": false,
+ "can_push": true,
"commit": {
"author_email": "john@example.com",
"author_name": "John Smith",
@@ -140,7 +142,8 @@ Example response:
"merged": false,
"protected": true,
"developers_can_push": true,
- "developers_can_merge": true
+ "developers_can_merge": true,
+ "can_push": true
}
```
@@ -188,7 +191,8 @@ Example response:
"merged": false,
"protected": false,
"developers_can_push": false,
- "developers_can_merge": false
+ "developers_can_merge": false,
+ "can_push": true
}
```
@@ -231,7 +235,8 @@ Example response:
"merged": false,
"protected": false,
"developers_can_push": false,
- "developers_can_merge": false
+ "developers_can_merge": false,
+ "can_push": true
}
```
diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md
index dcd5377284c..59e27922f64 100644
--- a/doc/api/graphql/index.md
+++ b/doc/api/graphql/index.md
@@ -29,9 +29,7 @@ curl --data "value=100" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://g
## Available queries
-A first iteration of a GraphQL API includes only 2 queries: `project` and
-`merge_request` and only returns scalar fields, or fields of the type `Project`
-or `MergeRequest`.
+A first iteration of a GraphQL API includes a query for: `project`. Within a project it is also possible to fetch a `mergeRequest` by IID.
## GraphiQL
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 96842ef330f..a48905f2f15 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -12,7 +12,7 @@ Parameters:
| `skip_groups` | array of integers | no | Skip the group IDs passed |
| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
-| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
+| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
@@ -96,7 +96,7 @@ Parameters:
| `skip_groups` | array of integers | no | Skip the group IDs passed |
| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
-| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
+| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md
index 20275b902c6..da2ffcfe40a 100644
--- a/doc/api/pages_domains.md
+++ b/doc/api/pages_domains.md
@@ -122,11 +122,11 @@ POST /projects/:id/pages/domains
| `key` | file/string | no | The certificate key in PEM format. |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="domain=ssl.domain.example" --form="certificate=@/path/to/cert.pem" --form="key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "domain=ssl.domain.example" --form "certificate=@/path/to/cert.pem" --form "key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains
```
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="domain=ssl.domain.example" --form="certificate=$CERT_PEM" --form="key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "domain=ssl.domain.example" --form "certificate=$CERT_PEM" --form "key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains
```
```json
@@ -158,11 +158,11 @@ PUT /projects/:id/pages/domains/:domain
| `key` | file/string | no | The certificate key in PEM format. |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="certificate=@/path/to/cert.pem" --form="key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "certificate=@/path/to/cert.pem" --form "key=@/path/to/key.pem" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
```
```bash
-curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form="certificate=$CERT_PEM" --form="key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "certificate=$CERT_PEM" --form "key=$KEY_PEM" https://gitlab.example.com/api/v4/projects/5/pages/domains/ssl.domain.example
```
```json
diff --git a/doc/api/search.md b/doc/api/search.md
index 107ddaffa6a..9716f682ace 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -776,6 +776,15 @@ Example response:
### Scope: blobs
+Filters are available for this scope:
+- filename
+- path
+- extension
+
+to use a filter simply include it in your query like so: `a query filename:some_name*`.
+
+You may use wildcards (`*`) to use glob matching.
+
```bash
curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/6/search?scope=blobs&search=installation
```
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 8e13ceb9257..26dcf67fe23 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -88,18 +88,18 @@ The example below simply moves all files from the root of the project to the
`public/` directory. The `.public` workaround is so `cp` doesn't also copy
`public/` to itself in an infinite loop:
-```
+```yaml
pages:
stage: deploy
script:
- - mkdir .public
- - cp -r * .public
- - mv .public public
+ - mkdir .public
+ - cp -r * .public
+ - mv .public public
artifacts:
paths:
- - public
+ - public
only:
- - master
+ - master
```
Read more on [GitLab Pages user documentation](../../user/project/pages/index.md).
@@ -131,15 +131,15 @@ if you set it per-job:
```yaml
before_script:
-- global before script
+ - global before script
job:
before_script:
- - execute this instead of global before script
+ - execute this instead of global before script
script:
- - my command
+ - my command
after_script:
- - execute this after my script
+ - execute this after my script
```
## `stages`
@@ -409,18 +409,18 @@ fails, it will not stop the next stage from running, since it's marked with
job1:
stage: test
script:
- - execute_script_that_will_fail
+ - execute_script_that_will_fail
allow_failure: true
job2:
stage: test
script:
- - execute_script_that_will_succeed
+ - execute_script_that_will_succeed
job3:
stage: deploy
script:
- - deploy_to_staging
+ - deploy_to_staging
```
## `when`
@@ -442,38 +442,38 @@ For example:
```yaml
stages:
-- build
-- cleanup_build
-- test
-- deploy
-- cleanup
+ - build
+ - cleanup_build
+ - test
+ - deploy
+ - cleanup
build_job:
stage: build
script:
- - make build
+ - make build
cleanup_build_job:
stage: cleanup_build
script:
- - cleanup build when failed
+ - cleanup build when failed
when: on_failure
test_job:
stage: test
script:
- - make test
+ - make test
deploy_job:
stage: deploy
script:
- - make deploy
+ - make deploy
when: manual
cleanup_job:
stage: cleanup
script:
- - cleanup after jobs
+ - cleanup after jobs
when: always
```
@@ -734,8 +734,8 @@ rspec:
script: test
cache:
paths:
- - binaries/*.apk
- - .config
+ - binaries/*.apk
+ - .config
```
Locally defined cache overrides globally defined options. The following `rspec`
@@ -744,14 +744,14 @@ job will cache only `binaries/`:
```yaml
cache:
paths:
- - my/files
+ - my/files
rspec:
script: test
cache:
key: rspec
paths:
- - binaries/
+ - binaries/
```
Note that since cache is shared between jobs, if you're using different
@@ -786,7 +786,7 @@ For example, to enable per-branch caching:
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- - binaries/
+ - binaries/
```
If you use **Windows Batch** to run your shell scripts you need to replace
@@ -796,7 +796,7 @@ If you use **Windows Batch** to run your shell scripts you need to replace
cache:
key: "%CI_COMMIT_REF_SLUG%"
paths:
- - binaries/
+ - binaries/
```
### `cache:untracked`
@@ -819,7 +819,7 @@ rspec:
cache:
untracked: true
paths:
- - binaries/
+ - binaries/
```
### `cache:policy`
@@ -897,8 +897,8 @@ Send all files in `binaries` and `.config`:
```yaml
artifacts:
paths:
- - binaries/
- - .config
+ - binaries/
+ - .config
```
To disable artifact passing, define the job with empty [dependencies](#dependencies):
@@ -927,7 +927,7 @@ release-job:
- mvn package -U
artifacts:
paths:
- - target/*.war
+ - target/*.war
only:
- tags
```
@@ -949,7 +949,7 @@ job:
artifacts:
name: "$CI_JOB_NAME"
paths:
- - binaries/
+ - binaries/
```
To create an archive with a name of the current branch or tag including only
@@ -960,7 +960,7 @@ job:
artifacts:
name: "$CI_COMMIT_REF_NAME"
paths:
- - binaries/
+ - binaries/
```
To create an archive with a name of the current job and the current branch or
@@ -971,7 +971,7 @@ job:
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
paths:
- - binaries/
+ - binaries/
```
To create an archive with a name of the current [stage](#stages) and branch name:
@@ -981,7 +981,7 @@ job:
artifacts:
name: "$CI_JOB_STAGE-$CI_COMMIT_REF_NAME"
paths:
- - binaries/
+ - binaries/
```
---
@@ -994,7 +994,7 @@ job:
artifacts:
name: "%CI_JOB_STAGE%-%CI_COMMIT_REF_NAME%"
paths:
- - binaries/
+ - binaries/
```
If you use **Windows PowerShell** to run your shell scripts you need to replace
@@ -1005,7 +1005,7 @@ job:
artifacts:
name: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_NAME"
paths:
- - binaries/
+ - binaries/
```
### `artifacts:untracked`
@@ -1030,7 +1030,7 @@ Send all Git untracked files and files in `binaries`:
artifacts:
untracked: true
paths:
- - binaries/
+ - binaries/
```
### `artifacts:when`
@@ -1120,26 +1120,26 @@ build:osx:
script: make build:osx
artifacts:
paths:
- - binaries/
+ - binaries/
build:linux:
stage: build
script: make build:linux
artifacts:
paths:
- - binaries/
+ - binaries/
test:osx:
stage: test
script: make test:osx
dependencies:
- - build:osx
+ - build:osx
test:linux:
stage: test
script: make test:linux
dependencies:
- - build:linux
+ - build:linux
deploy:
stage: deploy
@@ -1406,6 +1406,43 @@ variables:
You can set it globally or per-job in the [`variables`](#variables) section.
+### Custom build directories
+
+> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1
+
+NOTE: **Note:**
+This can only be used when `custom_build_dir` is set to true in the [Runner's
+configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
+
+By default, GitLab Runner clones the repository in the `/builds` directory,
+but sometimes your project might require to have the code in a specific
+directory, like the GO projects for example. In that case, you can specify
+the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone
+the repository:
+
+```yml
+image: golang:1.10-alpine3.7
+
+variables:
+ CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name
+
+stages:
+ - test
+
+dir:
+ stage: test
+ script:
+ - pwd # /go/src/gitlab.com/namespace/project-name
+```
+
+The following executors may use this feature only when
+[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
+is set to `1`:
+
+- `shell`
+- `ssh`
+- `docker`, `docker+machine` when the job's working directory is mounted as a host volume.
+
## Special YAML features
It's possible to use special YAML features like anchors (`&`), aliases (`*`)
@@ -1599,5 +1636,6 @@ CI with various languages.
[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
[ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447
[ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909
+[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876
[schedules]: ../../user/project/pipelines/schedules.md
[variables-expressions]: ../variables/README.md#variables-expressions
diff --git a/doc/development/new_fe_guide/style/prettier.md b/doc/development/new_fe_guide/style/prettier.md
index eb18189282b..6395af6f815 100644
--- a/doc/development/new_fe_guide/style/prettier.md
+++ b/doc/development/new_fe_guide/style/prettier.md
@@ -43,3 +43,17 @@ yarn prettier-all-save
Formats all files in the repository with Prettier. (This should only be used to test global rule updates otherwise you would end up with huge MR's).
The source of these Yarn scripts can be found in `/scripts/frontend/prettier.js`.
+
+### Scripts during Conversion period
+
+```
+node ./scripts/frontend/prettier.js check ./vendor/
+```
+
+This will go over all files in a specific folder check it.
+
+```
+node ./scripts/frontend/prettier.js save ./vendor/
+```
+
+This will go over all files in a specific folder and save it.
diff --git a/doc/integration/google.md b/doc/integration/google.md
index ae1d848f439..8906f91b6b4 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -35,7 +35,12 @@ In Google's side:
1. You should now be able to see a Client ID and Client secret. Note them down
or keep this page open as you will need them later.
-1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google Kubernetes Engine API > Enable**
+1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google+ API > Enable**
+1. To enable projects to access [Google Kubernetes Engine](../user/project/clusters/index.md), you must also
+ enable these APIs:
+ - Google Kubernetes Engine API
+ - Cloud Resource Manager API
+ - Cloud Billing API
On your GitLab server:
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index 3edde3de83d..82e8fbdb93e 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -168,7 +168,7 @@ want their accounts to be upgraded to full internal accounts.
>**Note:**
The following information only applies for installations from source.
-GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships
+GitLab uses [Omniauth](https://github.com/omniauth/omniauth) for authentication and already ships
with a few providers pre-installed (e.g. LDAP, GitHub, Twitter). But sometimes that
is not enough and you need to integrate with other authentication solutions. For
these cases you can use the Omniauth provider.
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index 2ca2bf743fb..7707d56764e 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -17,7 +17,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [How to install Git](how_to_install_git/index.md)
- [Start using Git on the command line](../../gitlab-basics/start-using-git.md)
- [Command Line basic commands](../../gitlab-basics/command-line-commands.md)
-- [GitLab Git Cheat Sheet (download)](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf)
+- [GitLab Git Cheat Sheet (download)](https://about.gitlab.com/images/press/git-cheat-sheet.pdf)
- Commits
- [Revert a commit](../../user/project/merge_requests/revert_changes.md#reverting-a-commit)
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index f340164b882..dc045961ed7 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -2,6 +2,10 @@
comments: false
---
+DANGER: This guide exists for reference of how an AWS deployment could work.
+We are currently seeing very slow EFS access performance which causes GitLab to
+be 5-10x slower than using NFS or Local disk. We _do not_ recommend follow this
+guide at this time.
# High Availability on AWS
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 16c19855136..b36b0b4f757 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -51,6 +51,9 @@ The following table depicts the various user permission levels in a project.
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| See environments | | ✓ | ✓ | ✓ | ✓ |
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
+| Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ |
+| Lock issue discussions | | ✓ | ✓ | ✓ | ✓ |
+| Lock merge request discussions | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Stop environments | | | ✓ | ✓ | ✓ |
| Manage/Accept merge requests | | | ✓ | ✓ | ✓ |
@@ -76,11 +79,12 @@ The following table depicts the various user permission levels in a project.
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ |
-| Manage runners | | | | ✓ | ✓ |
+| Manage Runners | | | | ✓ | ✓ |
| Manage job triggers | | | | ✓ | ✓ |
| Manage variables | | | | ✓ | ✓ |
-| Manage pages | | | | ✓ | ✓ |
-| Manage pages domains and certificates | | | | ✓ | ✓ |
+| Manage GitLab Pages | | | | ✓ | ✓ |
+| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
+| Remove GitLab Pages | | | | | ✓ |
| Manage clusters | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
@@ -90,6 +94,7 @@ The following table depicts the various user permission levels in a project.
| Remove pages | | | | | ✓ |
| Force push to protected branches [^4] | | | | | |
| Remove protected branches [^4] | | | | | |
+| View project Audit Events | | | | ✓ | ✓ |
## Project features permissions
@@ -127,17 +132,12 @@ and drag issues around. Read though the
[documentation on Issue Boards permissions](project/issue_board.md#permissions)
to learn more.
-### File Locking permissions
-
-> Available in [GitLab Premium](https://about.gitlab.com/products/).
+### File Locking permissions **[PREMIUM]**
The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located.
Read through the documentation on [permissions for File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html#permissions-on-file-locking) to learn more.
-File Locking is available in
-[GitLab Premium](https://about.gitlab.com/products/) only.
-
### Confidential Issues permissions
Confidential issues can be accessed by reporters and higher permission levels,
@@ -160,6 +160,12 @@ group.
| Remove group | | | | | ✓ |
| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
+| View private group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| View internal group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View public group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create/edit group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| Delete group epic **[ULTIMATE]** | | | | | ✓ |
+| View group Audit Events | | | | | ✓ |
### Subgroup permissions
@@ -194,6 +200,27 @@ will find the option to flag the user as external.
By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**.
+## Auditor users **[PREMIUM ONLY]**
+
+>[Introduced][ee-998] in [GitLab Premium][eep] 8.17.
+
+Auditor users are given read-only access to all projects, groups, and other
+resources on the GitLab instance.
+
+An Auditor user should be able to access all projects and groups of a GitLab instance
+with the permissions described on the documentation on [auditor users permissions](https://docs.gitlab.com/ee/administration/auditor_users.html#permissions-and-restrictions-of-an-auditor-user).
+
+[Read more about Auditor users.](https://docs.gitlab.com/ee/administration/auditor_users.html)
+
+## Project features
+
+Project features like wiki and issues can be hidden from users depending on
+which visibility level you select on project settings.
+
+- Disabled: disabled for everyone
+- Only team members: only team members will see even if your project is public or internal
+- Everyone with access: everyone can see depending on your project visibility level
+
## GitLab CI/CD permissions
GitLab CI/CD permissions rely on the role the user has in GitLab. There are four
@@ -263,16 +290,6 @@ for details about the pipelines security model.
Since GitLab 8.15, LDAP user permissions can now be manually overridden by an admin user.
Read through the documentation on [LDAP users permissions](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/index.html#updating-user-permissions-new-feature) to learn more.
-## Auditor users permissions
-
-> Available in [GitLab Premium](https://about.gitlab.com/products/).
-
-An Auditor user should be able to access all projects and groups of a GitLab instance
-with the permissions described on the documentation on [auditor users permissions](https://docs.gitlab.com/ee/administration/auditor_users.html#permissions-and-restrictions-of-an-auditor-user).
-
-Auditor users are available in [GitLab Premium](https://about.gitlab.com/products/)
-only.
-
[^1]: On public and internal projects, all users are able to perform this action
[^2]: Guest users can only view the confidential issues they created themselves
[^3]: If **Public pipelines** is enabled in **Project Settings > CI/CD**
@@ -283,3 +300,5 @@ only.
[ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994
[new-mod]: project/new_ci_build_permissions_model.md
+[ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998
+[eep]: https://about.gitlab.com/products/ \ No newline at end of file
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 48bb2e543c1..58a483bb3b2 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -156,7 +156,7 @@ added directly to your configured cluster. Those applications are needed for
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. |
-| [JupyterHub](http://jupyter.org/) | 11.0+ | The Jupyter Notebook is an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and narrative text. |
+| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. |
## Getting the external IP address
diff --git a/doc/user/project/integrations/microsoft_teams.md b/doc/user/project/integrations/microsoft_teams.md
index eaad2d5138a..5cf80a298ad 100644
--- a/doc/user/project/integrations/microsoft_teams.md
+++ b/doc/user/project/integrations/microsoft_teams.md
@@ -2,7 +2,7 @@
## On Microsoft Teams
-To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://msdn.microsoft.com/en-us/microsoft-teams/connectors).
+To enable Microsoft Teams integration you must create an incoming webhook integration on Microsoft Teams by following the steps described in this [document](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors#setting-up-a-custom-incoming-webhook).
## On GitLab
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 64bb33be547..632253db94c 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -10,7 +10,6 @@ Milestones allow you to organize issues and merge requests into a cohesive group
- **Project milestones** can be assigned to issues or merge requests in that project only.
- **Group milestones** can be assigned to any issue or merge request of any project in that group.
-- In the [future](https://gitlab.com/gitlab-org/gitlab-ce/issues/36862), you will be able to assign group milestones to issues and merge requests of projects in [subgroups](../../group/subgroups/index.md).
## Creating milestones
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 376f4e3cbe4..bda293bd00e 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -176,4 +176,12 @@ Lock your files to prevent any conflicting changes.
You can access your repos via [repository API](../../../api/repositories.md).
+## Clone in Apple Xcode
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45820) in GitLab 11.0
+
+Projects that contain a `.xcodeproj` or `.xcworkspace` directory can now be cloned
+in Xcode using the new **Open in Xcode** button, located next to the Git URL
+used for cloning your project. The button is only shown on macOS.
+
[jupyter]: https://jupyter.org
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index 6c1378560ef..918daee5d9f 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -58,6 +58,7 @@ Currently the following names are reserved as top level groups:
- dashboard
- deploy.html
- explore
+- favicon.ico
- favicon.png
- groups
- header_logo_dark.png
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 1cc8fcb8408..bb48a86fe9e 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -349,6 +349,10 @@ module API
expose :developers_can_merge do |repo_branch, options|
options[:project].protected_branches.developers_can?(:merge, repo_branch.name)
end
+
+ expose :can_push do |repo_branch, options|
+ Gitlab::UserAccess.new(options[:current_user], project: options[:project]).can_push_to_branch?(repo_branch.name)
+ end
end
class TreeObject < Grape::Entity
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 03b6b30a0d8..c7f41aba854 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -32,7 +32,7 @@ module API
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
- optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path'
+ optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
@@ -46,7 +46,9 @@ module API
groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
- groups = groups.reorder(params[:order_by] => params[:sort])
+ order_options = { params[:order_by] => params[:sort] }
+ order_options["id"] ||= "asc"
+ groups = groups.reorder(order_options)
groups
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index dc102259ca8..96a02914faa 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -84,7 +84,11 @@ module API
end
post '/request' do
authenticate_runner!
- no_content! unless current_runner.active?
+
+ unless current_runner.active?
+ header 'X-GitLab-Last-Update', current_runner.ensure_runner_queue_value
+ break no_content!
+ end
if current_runner.runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index d2c4b1e4d76..fbfcd72c916 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -10,7 +10,7 @@ module Banzai
^```
.+?
- \n```$
+ \n```\ *$
)
|
(?<html>
@@ -19,9 +19,9 @@ module Banzai
# Anything, including `>>>` blocks which are ignored by this filter
# </tag>
- ^<[^>]+?>\n
+ ^<[^>]+?>\ *\n
.+?
- \n<\/[^>]+?>$
+ \n<\/[^>]+?>\ *$
)
|
(?:
@@ -30,14 +30,14 @@ module Banzai
# Anything, including code and HTML blocks
# >>>
- ^>>>\n
+ ^>>>\ *\n
(?<quote>
(?:
# Any character that doesn't introduce a code or HTML block
(?!
^```
|
- ^<[^>]+?>\n
+ ^<[^>]+?>\ *\n
)
.
|
@@ -48,7 +48,7 @@ module Banzai
\g<html>
)+?
)
- \n>>>$
+ \n>>>\ *$
)
}mx.freeze
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index 858e790005c..af8448937b3 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -65,7 +65,7 @@ module Banzai
# We don't support IID lookups for group milestones, because IIDs can
# clash between group and project milestones.
if project.group && !params[:iid]
- finder_params[:group_ids] = project.group.self_and_ancestors.select(:id)
+ finder_params[:group_ids] = project.group.self_and_ancestors_ids
end
MilestonesFinder.new(finder_params).find_by(params)
diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
index 914a9e48a2f..522c69a0bb1 100644
--- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
@@ -54,7 +54,8 @@ module Gitlab
def ensure_temporary_tracking_table_exists
table_name = :untracked_files_for_uploads
- unless UntrackedFile.connection.table_exists?(table_name)
+
+ unless ActiveRecord::Base.connection.data_source_exists?(table_name)
UntrackedFile.connection.create_table table_name do |t|
t.string :path, limit: 600, null: false
t.index :path, unique: true
diff --git a/lib/gitlab/checks/commit_check.rb b/lib/gitlab/checks/commit_check.rb
index 43a52b493bb..22310e313ac 100644
--- a/lib/gitlab/checks/commit_check.rb
+++ b/lib/gitlab/checks/commit_check.rb
@@ -37,7 +37,7 @@ module Gitlab
def validate_lfs_file_locks?
strong_memoize(:validate_lfs_file_locks) do
- project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev
+ project.lfs_enabled? && newrev && oldrev && project.any_lfs_file_locks?
end
end
diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb
index c9c3050cfc2..87af4a90572 100644
--- a/lib/gitlab/checks/force_push.rb
+++ b/lib/gitlab/checks/force_push.rb
@@ -7,18 +7,10 @@ module Gitlab
# Created or deleted branch
return false if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
- GitalyClient.migrate(:force_push) do |is_enabled|
- if is_enabled
- !project
- .repository
- .gitaly_commit_client
- .ancestor?(oldrev, newrev)
- else
- Gitlab::Git::RevList.new(
- project.repository.raw, oldrev: oldrev, newrev: newrev
- ).missed_ref.present?
- end
- end
+ !project
+ .repository
+ .gitaly_commit_client
+ .ancestor?(oldrev, newrev)
end
end
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index d49d055c3f2..4ad106e7b0a 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -188,8 +188,11 @@ module Gitlab
end
def self.cached_table_exists?(table_name)
- # Rails 5 uses data_source_exists? instead of table_exists?
- connection.schema_cache.table_exists?(table_name)
+ if Gitlab.rails5?
+ connection.schema_cache.data_source_exists?(table_name)
+ else
+ connection.schema_cache.table_exists?(table_name)
+ end
end
private_class_method :connection
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb
index 62d4d0a92a6..26ae6966746 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb
@@ -37,6 +37,7 @@ module Gitlab
class Namespace < ActiveRecord::Base
include MigrationClasses::Routable
self.table_name = 'namespaces'
+ self.inheritance_column = :_type_disabled
belongs_to :parent,
class_name: "#{MigrationClasses.name}::Namespace"
has_one :route, as: :source
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 451c9daf761..faf7016d73a 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -2,7 +2,7 @@ module Gitlab
class Favicon
class << self
def main
- return appearance_favicon.favicon_main.url if appearance_favicon.exists?
+ return appearance_favicon.url if appearance_favicon.exists?
image_name =
if Gitlab::Utils.to_boolean(ENV['CANARY'])
diff --git a/lib/gitlab/file_finder.rb b/lib/gitlab/file_finder.rb
index f42088f980e..af8270c8db8 100644
--- a/lib/gitlab/file_finder.rb
+++ b/lib/gitlab/file_finder.rb
@@ -14,14 +14,21 @@ module Gitlab
end
def find(query)
- by_content = find_by_content(query)
+ query = Gitlab::Search::Query.new(query) do
+ filter :filename, matcher: ->(filter, blob) { blob.filename =~ /#{filter[:regex_value]}$/i }
+ filter :path, matcher: ->(filter, blob) { blob.filename =~ /#{filter[:regex_value]}/i }
+ filter :extension, matcher: ->(filter, blob) { blob.filename =~ /\.#{filter[:regex_value]}$/i }
+ end
+
+ by_content = find_by_content(query.term)
already_found = Set.new(by_content.map(&:filename))
- by_filename = find_by_filename(query, except: already_found)
+ by_filename = find_by_filename(query.term, except: already_found)
+
+ files = (by_content + by_filename)
+ .sort_by(&:filename)
- (by_content + by_filename)
- .sort_by(&:filename)
- .map { |blob| [blob.filename, blob] }
+ query.filter_results(files).map { |blob| [blob.filename, blob] }
end
private
diff --git a/lib/gitlab/git/lfs_changes.rb b/lib/gitlab/git/lfs_changes.rb
index f3cc388ea41..f0fab1e76a3 100644
--- a/lib/gitlab/git/lfs_changes.rb
+++ b/lib/gitlab/git/lfs_changes.rb
@@ -7,67 +7,11 @@ module Gitlab
end
def new_pointers(object_limit: nil, not_in: nil)
- @repository.gitaly_migrate(:blob_get_new_lfs_pointers) do |is_enabled|
- if is_enabled
- @repository.gitaly_blob_client.get_new_lfs_pointers(@newrev, object_limit, not_in)
- else
- git_new_pointers(object_limit, not_in)
- end
- end
+ @repository.gitaly_blob_client.get_new_lfs_pointers(@newrev, object_limit, not_in)
end
def all_pointers
- @repository.gitaly_migrate(:blob_get_all_lfs_pointers) do |is_enabled|
- if is_enabled
- @repository.gitaly_blob_client.get_all_lfs_pointers(@newrev)
- else
- git_all_pointers
- end
- end
- end
-
- private
-
- def git_new_pointers(object_limit, not_in)
- @new_pointers ||= begin
- rev_list.new_objects(rev_list_params(not_in: not_in)) do |object_ids|
- object_ids = object_ids.take(object_limit) if object_limit
-
- Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
- end
- end
- end
-
- def git_all_pointers
- params = {}
- if rev_list_supports_new_options?
- params[:options] = ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"]
- end
-
- rev_list.all_objects(rev_list_params(params)) do |object_ids|
- Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
- end
- end
-
- def rev_list
- Gitlab::Git::RevList.new(@repository, newrev: @newrev)
- end
-
- # We're passing the `--in-commit-order` arg to ensure we don't wait
- # for git to traverse all commits before returning pointers.
- # This is required in order to improve the performance of LFS integrity check
- def rev_list_params(params = {})
- params[:options] ||= []
- params[:options] << "--in-commit-order" if rev_list_supports_new_options?
- params[:require_path] = true
-
- params
- end
-
- def rev_list_supports_new_options?
- return @option_supported if defined?(@option_supported)
-
- @option_supported = Gitlab::Git.version >= Gitlab::VersionInfo.parse('2.16.0')
+ @repository.gitaly_blob_client.get_all_lfs_pointers(@newrev)
end
end
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index eb5d6318dcb..7ce90ce170f 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -472,13 +472,21 @@ module Gitlab
end
def count_commits(options)
- count_commits_options = process_count_commits_options(options)
+ options = process_count_commits_options(options.dup)
- gitaly_migrate(:count_commits, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- count_commits_by_gitaly(count_commits_options)
+ wrapped_gitaly_errors do
+ if options[:left_right]
+ from = options[:from]
+ to = options[:to]
+
+ right_count = gitaly_commit_client
+ .commit_count("#{from}..#{to}", options)
+ left_count = gitaly_commit_client
+ .commit_count("#{to}..#{from}", options)
+
+ [left_count, right_count]
else
- count_commits_by_shelling_out(count_commits_options)
+ gitaly_commit_client.commit_count(options[:ref], options)
end
end
end
@@ -676,15 +684,9 @@ module Gitlab
end
# Return total commits count accessible from passed ref
- #
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/330
def commit_count(ref)
- gitaly_migrate(:commit_count, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_commit_client.commit_count(ref)
- else
- rugged_commit_count(ref)
- end
+ wrapped_gitaly_errors do
+ gitaly_commit_client.commit_count(ref)
end
end
@@ -984,21 +986,7 @@ module Gitlab
def info_attributes
return @info_attributes if @info_attributes
- content =
- gitaly_migrate(:get_info_attributes, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_repository_client.info_attributes
- else
- attributes_path = File.join(File.expand_path(path), 'info', 'attributes')
-
- if File.exist?(attributes_path)
- File.read(attributes_path)
- else
- ""
- end
- end
- end
-
+ content = gitaly_repository_client.info_attributes
@info_attributes = AttributesParser.new(content)
end
@@ -1054,18 +1042,8 @@ module Gitlab
end
def license_short_name
- gitaly_migrate(:license_short_name,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_repository_client.license_short_name
- else
- begin
- # The licensee gem creates a Rugged object from the path:
- # https://github.com/benbalter/licensee/blob/v8.7.0/lib/licensee/projects/git_project.rb
- Licensee.license(path).try(:key)
- rescue Rugged::Error
- end
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.license_short_name
end
end
@@ -1252,12 +1230,8 @@ module Gitlab
end
def rebase_in_progress?(rebase_id)
- gitaly_migrate(:rebase_in_progress) do |is_enabled|
- if is_enabled
- gitaly_repository_client.rebase_in_progress?(rebase_id)
- else
- fresh_worktree?(worktree_path(REBASE_WORKTREE_PREFIX, rebase_id))
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.rebase_in_progress?(rebase_id)
end
end
@@ -1273,12 +1247,8 @@ module Gitlab
end
def squash_in_progress?(squash_id)
- gitaly_migrate(:squash_in_progress, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_repository_client.squash_in_progress?(squash_id)
- else
- fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id))
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.squash_in_progress?(squash_id)
end
end
@@ -1514,10 +1484,6 @@ module Gitlab
run_git!(args, lazy_block: block)
end
- def missed_ref(oldrev, newrev)
- run_git!(['rev-list', '--max-count=1', oldrev, "^#{newrev}"])
- end
-
def with_worktree(worktree_path, branch, sparse_checkout_files: nil, env:)
base_args = %w(worktree add --detach)
@@ -1621,21 +1587,6 @@ module Gitlab
end
end
- # This function is duplicated in Gitaly-Go, don't change it!
- # https://gitlab.com/gitlab-org/gitaly/merge_requests/698
- def fresh_worktree?(path)
- File.exist?(path) && !clean_stuck_worktree(path)
- end
-
- # This function is duplicated in Gitaly-Go, don't change it!
- # https://gitlab.com/gitlab-org/gitaly/merge_requests/698
- def clean_stuck_worktree(path)
- return false unless File.mtime(path) < 15.minutes.ago
-
- FileUtils.rm_rf(path)
- true
- end
-
# Adding a worktree means checking out the repository. For large repos,
# this can be very expensive, so set up sparse checkout for the worktree
# to only check out the files we're interested in.
@@ -1902,71 +1853,6 @@ module Gitlab
gitaly_repository_client.repository_size
end
- def count_commits_by_gitaly(options)
- if options[:left_right]
- from = options[:from]
- to = options[:to]
-
- right_count = gitaly_commit_client
- .commit_count("#{from}..#{to}", options)
- left_count = gitaly_commit_client
- .commit_count("#{to}..#{from}", options)
-
- [left_count, right_count]
- else
- gitaly_commit_client.commit_count(options[:ref], options)
- end
- end
-
- def count_commits_by_shelling_out(options)
- cmd = count_commits_shelling_command(options)
-
- raw_output, _status = run_git(cmd)
-
- process_count_commits_raw_output(raw_output, options)
- end
-
- def count_commits_shelling_command(options)
- cmd = %w[rev-list]
- cmd << "--after=#{options[:after].iso8601}" if options[:after]
- cmd << "--before=#{options[:before].iso8601}" if options[:before]
- cmd << "--max-count=#{options[:max_count]}" if options[:max_count]
- cmd << "--left-right" if options[:left_right]
- cmd << '--count'
-
- cmd << if options[:all]
- '--all'
- elsif options[:ref]
- options[:ref]
- else
- raise ArgumentError, "Please specify a valid ref or set the 'all' attribute to true"
- end
-
- cmd += %W[-- #{options[:path]}] if options[:path].present?
- cmd
- end
-
- def process_count_commits_raw_output(raw_output, options)
- if options[:left_right]
- result = raw_output.scan(/\d+/).map(&:to_i)
-
- if result.sum != options[:max_count]
- result
- else # Reaching max count, right is not accurate
- right_option =
- process_count_commits_options(options
- .except(:left_right, :from, :to)
- .merge(ref: options[:to]))
-
- right = count_commits_by_shelling_out(right_option)
-
- [result.first, right] # left should be accurate in the first call
- end
- else
- raw_output.to_i
- end
- end
-
def gitaly_ls_files(ref)
gitaly_commit_client.ls_files(ref)
end
@@ -2393,16 +2279,6 @@ module Gitlab
nil
end
- def rugged_commit_count(ref)
- walker = Rugged::Walker.new(rugged)
- walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
- oid = rugged.rev_parse_oid(ref)
- walker.push(oid)
- walker.count
- rescue Rugged::ReferenceError
- 0
- end
-
def rev_list_param(spec)
spec == :all ? ['--all'] : spec
end
diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb
index 4e661eceffb..5fdad077eea 100644
--- a/lib/gitlab/git/rev_list.rb
+++ b/lib/gitlab/git/rev_list.rb
@@ -1,5 +1,3 @@
-# Gitaly note: JV: will probably be migrated indirectly by migrating the call sites.
-
module Gitlab
module Git
class RevList
@@ -45,13 +43,6 @@ module Gitlab
&lazy_block)
end
- # This methods returns an array of missed references
- #
- # Should become obsolete after https://gitlab.com/gitlab-org/gitaly/issues/348.
- def missed_ref
- repository.missed_ref(oldrev, newrev).split("\n")
- end
-
private
def execute(args)
diff --git a/lib/gitlab/git/version.rb b/lib/gitlab/git/version.rb
index 11184ca3457..1e14e8b652a 100644
--- a/lib/gitlab/git/version.rb
+++ b/lib/gitlab/git/version.rb
@@ -4,7 +4,7 @@ module Gitlab
extend Gitlab::Git::Popen
def self.git_version
- Gitlab::VersionInfo.parse(popen(%W(#{Gitlab.config.git.bin_path} --version), nil).first)
+ Gitlab::VersionInfo.parse(Gitaly::Server.all.first.git_binary_version)
end
end
end
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 1ab8c4e0229..8ee46b59830 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -27,63 +27,38 @@ module Gitlab
end
def write_page(name, format, content, commit_details)
- @repository.gitaly_migrate(:wiki_write_page) do |is_enabled|
- if is_enabled
- gitaly_write_page(name, format, content, commit_details)
- else
- gollum_write_page(name, format, content, commit_details)
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_write_page(name, format, content, commit_details)
end
end
def delete_page(page_path, commit_details)
- @repository.gitaly_migrate(:wiki_delete_page) do |is_enabled|
- if is_enabled
- gitaly_delete_page(page_path, commit_details)
- else
- gollum_delete_page(page_path, commit_details)
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_delete_page(page_path, commit_details)
end
end
def update_page(page_path, title, format, content, commit_details)
- @repository.gitaly_migrate(:wiki_update_page) do |is_enabled|
- if is_enabled
- gitaly_update_page(page_path, title, format, content, commit_details)
- else
- gollum_update_page(page_path, title, format, content, commit_details)
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_update_page(page_path, title, format, content, commit_details)
end
end
def pages(limit: nil)
- @repository.gitaly_migrate(:wiki_get_all_pages) do |is_enabled|
- if is_enabled
- gitaly_get_all_pages
- else
- gollum_get_all_pages(limit: limit)
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_get_all_pages
end
end
def page(title:, version: nil, dir: nil)
- @repository.gitaly_migrate(:wiki_find_page,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_find_page(title: title, version: version, dir: dir)
- else
- gollum_find_page(title: title, version: version, dir: dir)
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_find_page(title: title, version: version, dir: dir)
end
end
def file(name, version)
- @repository.gitaly_migrate(:wiki_find_file) do |is_enabled|
- if is_enabled
- gitaly_find_file(name, version)
- else
- gollum_find_file(name, version)
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_find_file(name, version)
end
end
@@ -92,24 +67,15 @@ module Gitlab
# :per_page - The number of items per page.
# :limit - Total number of items to return.
def page_versions(page_path, options = {})
- @repository.gitaly_migrate(:wiki_page_versions) do |is_enabled|
- if is_enabled
- versions = gitaly_wiki_client.page_versions(page_path, options)
-
- # Gitaly uses gollum-lib to get the versions. Gollum defaults to 20
- # per page, but also fetches 20 if `limit` or `per_page` < 20.
- # Slicing returns an array with the expected number of items.
- slice_bound = options[:limit] || options[:per_page] || Gollum::Page.per_page
- versions[0..slice_bound]
- else
- current_page = gollum_page_by_path(page_path)
-
- commits_from_page(current_page, options).map do |gitlab_git_commit|
- gollum_page = gollum_wiki.page(current_page.title, gitlab_git_commit.id)
- Gitlab::Git::WikiPageVersion.new(gitlab_git_commit, gollum_page&.format)
- end
- end
+ versions = @repository.wrapped_gitaly_errors do
+ gitaly_wiki_client.page_versions(page_path, options)
end
+
+ # Gitaly uses gollum-lib to get the versions. Gollum defaults to 20
+ # per page, but also fetches 20 if `limit` or `per_page` < 20.
+ # Slicing returns an array with the expected number of items.
+ slice_bound = options[:limit] || options[:per_page] || Gollum::Page.per_page
+ versions[0..slice_bound]
end
def count_page_versions(page_path)
@@ -131,46 +97,13 @@ module Gitlab
def page_formatted_data(title:, dir: nil, version: nil)
version = version&.id
- @repository.gitaly_migrate(:wiki_page_formatted_data, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_wiki_client.get_formatted_data(title: title, dir: dir, version: version)
- else
- # We don't use #page because if wiki_find_page feature is enabled, we would
- # get a page without formatted_data.
- gollum_find_page(title: title, dir: dir, version: version)&.formatted_data
- end
+ @repository.wrapped_gitaly_errors do
+ gitaly_wiki_client.get_formatted_data(title: title, dir: dir, version: version)
end
end
- def gollum_wiki
- @gollum_wiki ||= Gollum::Wiki.new(@repository.path)
- end
-
private
- # options:
- # :page - The Integer page number.
- # :per_page - The number of items per page.
- # :limit - Total number of items to return.
- def commits_from_page(gollum_page, options = {})
- unless options[:limit]
- options[:offset] = ([1, options.delete(:page).to_i].max - 1) * Gollum::Page.per_page
- options[:limit] = (options.delete(:per_page) || Gollum::Page.per_page).to_i
- end
-
- @repository.log(ref: gollum_page.last_version.id,
- path: gollum_page.path,
- limit: options[:limit],
- offset: options[:offset])
- end
-
- def gollum_page_by_path(page_path)
- page_name = Gollum::Page.canonicalize_filename(page_path)
- page_dir = File.split(page_path).first
-
- gollum_wiki.paged(page_name, page_dir)
- end
-
def new_page(gollum_page)
Gitlab::Git::WikiPage.new(gollum_page, new_version(gollum_page, gollum_page.version.id))
end
@@ -199,65 +132,6 @@ module Gitlab
@gitaly_wiki_client ||= Gitlab::GitalyClient::WikiService.new(@repository)
end
- def gollum_write_page(name, format, content, commit_details)
- assert_type!(format, Symbol)
- assert_type!(commit_details, CommitDetails)
-
- with_committer_with_hooks(commit_details) do |committer|
- filename = File.basename(name)
- dir = (tmp_dir = File.dirname(name)) == '.' ? '' : tmp_dir
-
- gollum_wiki.write_page(filename, format, content, { committer: committer }, dir)
- end
- rescue Gollum::DuplicatePageError => e
- raise Gitlab::Git::Wiki::DuplicatePageError, e.message
- end
-
- def gollum_delete_page(page_path, commit_details)
- assert_type!(commit_details, CommitDetails)
-
- with_committer_with_hooks(commit_details) do |committer|
- gollum_wiki.delete_page(gollum_page_by_path(page_path), committer: committer)
- end
- end
-
- def gollum_update_page(page_path, title, format, content, commit_details)
- assert_type!(format, Symbol)
- assert_type!(commit_details, CommitDetails)
-
- with_committer_with_hooks(commit_details) do |committer|
- page = gollum_page_by_path(page_path)
- # Instead of performing two renames if the title has changed,
- # the update_page will only update the format and content and
- # the rename_page will do anything related to moving/renaming
- gollum_wiki.update_page(page, page.name, format, content, committer: committer)
- gollum_wiki.rename_page(page, title, committer: committer)
- end
- end
-
- def gollum_find_page(title:, version: nil, dir: nil)
- if version
- version = Gitlab::Git::Commit.find(@repository, version).id
- end
-
- gollum_page = gollum_wiki.page(title, version, dir)
- return unless gollum_page
-
- new_page(gollum_page)
- end
-
- def gollum_find_file(name, version)
- version ||= self.class.default_ref
- gollum_file = gollum_wiki.file(name, version)
- return unless gollum_file
-
- Gitlab::Git::WikiFile.new(gollum_file)
- end
-
- def gollum_get_all_pages(limit: nil)
- gollum_wiki.pages(limit: limit).map { |gollum_page| new_page(gollum_page) }
- end
-
def gitaly_write_page(name, format, content, commit_details)
gitaly_wiki_client.write_page(name, format, content, commit_details)
end
diff --git a/lib/gitlab/health_checks/db_check.rb b/lib/gitlab/health_checks/db_check.rb
index e27e16ddaf6..08495c0a59e 100644
--- a/lib/gitlab/health_checks/db_check.rb
+++ b/lib/gitlab/health_checks/db_check.rb
@@ -17,7 +17,7 @@ module Gitlab
def check
catch_timeout 10.seconds do
if Gitlab::Database.postgresql?
- ActiveRecord::Base.connection.execute('SELECT 1 as ping')&.first&.[]('ping')
+ ActiveRecord::Base.connection.execute('SELECT 1 as ping')&.first&.[]('ping')&.to_s
else
ActiveRecord::Base.connection.execute('SELECT 1 as ping')&.first&.first&.to_s
end
diff --git a/lib/gitlab/health_checks/fs_shards_check.rb b/lib/gitlab/health_checks/fs_shards_check.rb
index fcbf266b80b..050fe7a5173 100644
--- a/lib/gitlab/health_checks/fs_shards_check.rb
+++ b/lib/gitlab/health_checks/fs_shards_check.rb
@@ -1,5 +1,6 @@
module Gitlab
module HealthChecks
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1218
class FsShardsCheck
extend BaseAbstractCheck
RANDOM_STRING = SecureRandom.hex(1000).freeze
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 3772ef11c7f..343487bc361 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -21,7 +21,8 @@ module Gitlab
'nl_NL' => 'Nederlands',
'tr_TR' => 'Türkçe',
'id_ID' => 'Bahasa Indonesia',
- 'fil_PH' => 'Filipino'
+ 'fil_PH' => 'Filipino',
+ 'pl_PL' => 'Polski'
}.freeze
def available_locales
diff --git a/lib/gitlab/i18n/metadata_entry.rb b/lib/gitlab/i18n/metadata_entry.rb
index 35d57459a3d..36fc1bcdcb7 100644
--- a/lib/gitlab/i18n/metadata_entry.rb
+++ b/lib/gitlab/i18n/metadata_entry.rb
@@ -3,16 +3,25 @@ module Gitlab
class MetadataEntry
attr_reader :entry_data
+ # Avoid testing too many plurals if `nplurals` was incorrectly set.
+ # Based on info on https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
+ # which mentions special cases for numbers ending in 2 digits
+ MAX_FORMS_TO_TEST = 101
+
def initialize(entry_data)
@entry_data = entry_data
end
- def expected_plurals
+ def expected_forms
return nil unless plural_information
plural_information['nplurals'].to_i
end
+ def forms_to_test
+ @forms_to_test ||= [expected_forms, MAX_FORMS_TO_TEST].compact.min
+ end
+
private
def plural_information
diff --git a/lib/gitlab/i18n/po_linter.rb b/lib/gitlab/i18n/po_linter.rb
index 7d3ff8c7f58..d8e7269a2c2 100644
--- a/lib/gitlab/i18n/po_linter.rb
+++ b/lib/gitlab/i18n/po_linter.rb
@@ -1,6 +1,8 @@
module Gitlab
module I18n
class PoLinter
+ include Gitlab::Utils::StrongMemoize
+
attr_reader :po_path, :translation_entries, :metadata_entry, :locale
VARIABLE_REGEX = /%{\w*}|%[a-z]/.freeze
@@ -34,7 +36,7 @@ module Gitlab
end
@translation_entries = entries.map do |entry_data|
- Gitlab::I18n::TranslationEntry.new(entry_data, metadata_entry.expected_plurals)
+ Gitlab::I18n::TranslationEntry.new(entry_data, metadata_entry.expected_forms)
end
nil
@@ -48,7 +50,7 @@ module Gitlab
translation_entries.each do |entry|
errors_for_entry = validate_entry(entry)
- errors[join_message(entry.msgid)] = errors_for_entry if errors_for_entry.any?
+ errors[entry.msgid] = errors_for_entry if errors_for_entry.any?
end
errors
@@ -62,6 +64,7 @@ module Gitlab
validate_newlines(errors, entry)
validate_number_of_plurals(errors, entry)
validate_unescaped_chars(errors, entry)
+ validate_translation(errors, entry)
errors
end
@@ -81,35 +84,39 @@ module Gitlab
end
def validate_number_of_plurals(errors, entry)
- return unless metadata_entry&.expected_plurals
+ return unless metadata_entry&.expected_forms
return unless entry.translated?
- if entry.has_plural? && entry.all_translations.size != metadata_entry.expected_plurals
- errors << "should have #{metadata_entry.expected_plurals} "\
- "#{'translations'.pluralize(metadata_entry.expected_plurals)}"
+ if entry.has_plural? && entry.all_translations.size != metadata_entry.expected_forms
+ errors << "should have #{metadata_entry.expected_forms} "\
+ "#{'translations'.pluralize(metadata_entry.expected_forms)}"
end
end
def validate_newlines(errors, entry)
- if entry.msgid_contains_newlines?
+ if entry.msgid_has_multiple_lines?
errors << 'is defined over multiple lines, this breaks some tooling.'
end
- if entry.plural_id_contains_newlines?
+ if entry.plural_id_has_multiple_lines?
errors << 'plural is defined over multiple lines, this breaks some tooling.'
end
- if entry.translations_contain_newlines?
+ if entry.translations_have_multiple_lines?
errors << 'has translations defined over multiple lines, this breaks some tooling.'
end
end
def validate_variables(errors, entry)
if entry.has_singular_translation?
+ validate_variables_in_message(errors, entry.msgid, entry.msgid)
+
validate_variables_in_message(errors, entry.msgid, entry.singular_translation)
end
if entry.has_plural?
+ validate_variables_in_message(errors, entry.plural_id, entry.plural_id)
+
entry.plural_translations.each do |translation|
validate_variables_in_message(errors, entry.plural_id, translation)
end
@@ -117,41 +124,98 @@ module Gitlab
end
def validate_variables_in_message(errors, message_id, message_translation)
- message_id = join_message(message_id)
required_variables = message_id.scan(VARIABLE_REGEX)
validate_unnamed_variables(errors, required_variables)
- validate_translation(errors, message_id, required_variables)
validate_variable_usage(errors, message_translation, required_variables)
end
- def validate_translation(errors, message_id, used_variables)
+ def validate_translation(errors, entry)
+ Gitlab::I18n.with_locale(locale) do
+ if entry.has_plural?
+ translate_plural(entry)
+ else
+ translate_singular(entry)
+ end
+ end
+
+ # `sprintf` could raise an `ArgumentError` when invalid passing something
+ # other than a Hash when using named variables
+ #
+ # `sprintf` could raise `TypeError` when passing a wrong type when using
+ # unnamed variables
+ #
+ # FastGettext::Translation could raise `RuntimeError` (raised as a string),
+ # or as subclassess `NoTextDomainConfigured` & `InvalidFormat`
+ #
+ # `FastGettext::Translation` could raise `ArgumentError` as subclassess
+ # `InvalidEncoding`, `IllegalSequence` & `InvalidCharacter`
+ rescue ArgumentError, TypeError, RuntimeError => e
+ errors << "Failure translating to #{locale}: #{e.message}"
+ end
+
+ def translate_singular(entry)
+ used_variables = entry.msgid.scan(VARIABLE_REGEX)
variables = fill_in_variables(used_variables)
- begin
- Gitlab::I18n.with_locale(locale) do
- translated = if message_id.include?('|')
- FastGettext::Translation.s_(message_id)
- else
- FastGettext::Translation._(message_id)
- end
+ translation = if entry.msgid.include?('|')
+ FastGettext::Translation.s_(entry.msgid)
+ else
+ FastGettext::Translation._(entry.msgid)
+ end
- translated % variables
+ translation % variables if used_variables.any?
+ end
+
+ def translate_plural(entry)
+ used_variables = entry.plural_id.scan(VARIABLE_REGEX)
+ variables = fill_in_variables(used_variables)
+
+ numbers_covering_all_plurals.map do |number|
+ translation = FastGettext::Translation.n_(entry.msgid, entry.plural_id, number)
+
+ translation % variables if used_variables.any?
+ end
+ end
+
+ def numbers_covering_all_plurals
+ @numbers_covering_all_plurals ||= calculate_numbers_covering_all_plurals
+ end
+
+ def calculate_numbers_covering_all_plurals
+ required_numbers = []
+ discovered_indexes = []
+ counter = 0
+
+ while discovered_indexes.size < metadata_entry.forms_to_test && counter < Gitlab::I18n::MetadataEntry::MAX_FORMS_TO_TEST
+ index_for_count = index_for_pluralization(counter)
+
+ unless discovered_indexes.include?(index_for_count)
+ discovered_indexes << index_for_count
+ required_numbers << counter
end
- # `sprintf` could raise an `ArgumentError` when invalid passing something
- # other than a Hash when using named variables
- #
- # `sprintf` could raise `TypeError` when passing a wrong type when using
- # unnamed variables
- #
- # FastGettext::Translation could raise `RuntimeError` (raised as a string),
- # or as subclassess `NoTextDomainConfigured` & `InvalidFormat`
- #
- # `FastGettext::Translation` could raise `ArgumentError` as subclassess
- # `InvalidEncoding`, `IllegalSequence` & `InvalidCharacter`
- rescue ArgumentError, TypeError, RuntimeError => e
- errors << "Failure translating to #{locale} with #{variables}: #{e.message}"
+ counter += 1
+ end
+
+ required_numbers
+ end
+
+ def index_for_pluralization(counter)
+ # This calls the C function that defines the pluralization rule, it can
+ # return a boolean (`false` represents 0, `true` represents 1) or an integer
+ # that specifies the plural form to be used for the given number
+ pluralization_result = Gitlab::I18n.with_locale(locale) do
+ FastGettext.pluralisation_rule.call(counter)
+ end
+
+ case pluralization_result
+ when false
+ 0
+ when true
+ 1
+ else
+ pluralization_result
end
end
@@ -172,14 +236,18 @@ module Gitlab
end
def validate_unnamed_variables(errors, variables)
- if variables.size > 1 && variables.any? { |variable_name| unnamed_variable?(variable_name) }
+ unnamed_variables, named_variables = variables.partition { |name| unnamed_variable?(name) }
+
+ if unnamed_variables.any? && named_variables.any?
+ errors << 'is combining named variables with unnamed variables'
+ end
+
+ if unnamed_variables.size > 1
errors << 'is combining multiple unnamed variables'
end
end
def validate_variable_usage(errors, translation, required_variables)
- translation = join_message(translation)
-
# We don't need to validate when the message is empty.
# In this case we fall back to the default, which has all the the
# required variables.
@@ -205,10 +273,6 @@ module Gitlab
def validate_flags(errors, entry)
errors << "is marked #{entry.flag}" if entry.flag
end
-
- def join_message(message)
- Array(message).join
- end
end
end
end
diff --git a/lib/gitlab/i18n/translation_entry.rb b/lib/gitlab/i18n/translation_entry.rb
index e6c95afca7e..54adb98f42d 100644
--- a/lib/gitlab/i18n/translation_entry.rb
+++ b/lib/gitlab/i18n/translation_entry.rb
@@ -11,11 +11,11 @@ module Gitlab
end
def msgid
- entry_data[:msgid]
+ @msgid ||= Array(entry_data[:msgid]).join
end
def plural_id
- entry_data[:msgid_plural]
+ @plural_id ||= Array(entry_data[:msgid_plural]).join
end
def has_plural?
@@ -23,12 +23,11 @@ module Gitlab
end
def singular_translation
- all_translations.first if has_singular_translation?
+ all_translations.first.to_s if has_singular_translation?
end
def all_translations
- @all_translations ||= entry_data.fetch_values(*translation_keys)
- .reject(&:empty?)
+ @all_translations ||= translation_entries.map { |translation| Array(translation).join }
end
def translated?
@@ -54,16 +53,16 @@ module Gitlab
nplurals > 1 || !has_plural?
end
- def msgid_contains_newlines?
- msgid.is_a?(Array)
+ def msgid_has_multiple_lines?
+ entry_data[:msgid].is_a?(Array)
end
- def plural_id_contains_newlines?
- plural_id.is_a?(Array)
+ def plural_id_has_multiple_lines?
+ entry_data[:msgid_plural].is_a?(Array)
end
- def translations_contain_newlines?
- all_translations.any? { |translation| translation.is_a?(Array) }
+ def translations_have_multiple_lines?
+ translation_entries.any? { |translation| translation.is_a?(Array) }
end
def msgid_contains_unescaped_chars?
@@ -84,6 +83,11 @@ module Gitlab
private
+ def translation_entries
+ @translation_entries ||= entry_data.fetch_values(*translation_keys)
+ .reject(&:empty?)
+ end
+
def translation_keys
@translation_keys ||= entry_data.keys.select { |key| key.to_s =~ /\Amsgstr(\[\d+\])?\z/ }
end
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index e5191f5c7f9..61653044433 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -30,6 +30,7 @@ module Gitlab
dashboard
deploy.html
explore
+ favicon.ico
favicon.png
files
groups
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index 18540e64d4c..ecff6ab5d5e 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -11,6 +11,7 @@ module Gitlab
lib/gitlab/etag_caching/
lib/gitlab/metrics/
lib/gitlab/middleware/
+ ee/lib/gitlab/middleware/
lib/gitlab/performance_bar/
lib/gitlab/request_profiler/
lib/gitlab/profiler.rb
@@ -98,11 +99,7 @@ module Gitlab
super
- backtrace = Rails.backtrace_cleaner.clean(caller)
-
- backtrace.each do |caller_line|
- next if caller_line.match(Regexp.union(IGNORE_BACKTRACES))
-
+ Gitlab::Profiler.clean_backtrace(caller).each do |caller_line|
stripped_caller_line = caller_line.sub("#{Rails.root}/", '')
super(" ↳ #{stripped_caller_line}")
@@ -112,6 +109,12 @@ module Gitlab
end
end
+ def self.clean_backtrace(backtrace)
+ Array(Rails.backtrace_cleaner.clean(backtrace)).reject do |line|
+ line.match(Regexp.union(IGNORE_BACKTRACES))
+ end
+ end
+
def self.with_custom_logger(logger)
original_colorize_logging = ActiveSupport::LogSubscriber.colorize_logging
original_activerecord_logger = ActiveRecord::Base.logger
diff --git a/lib/gitlab/search/parsed_query.rb b/lib/gitlab/search/parsed_query.rb
new file mode 100644
index 00000000000..23595f23f01
--- /dev/null
+++ b/lib/gitlab/search/parsed_query.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module Search
+ class ParsedQuery
+ attr_reader :term, :filters
+
+ def initialize(term, filters)
+ @term = term
+ @filters = filters
+ end
+
+ def filter_results(results)
+ filters = @filters.reject { |filter| filter[:matcher].nil? }
+ return unless filters
+
+ results.select do |result|
+ filters.all? do |filter|
+ filter[:matcher].call(filter, result)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/search/query.rb b/lib/gitlab/search/query.rb
new file mode 100644
index 00000000000..8583bce7792
--- /dev/null
+++ b/lib/gitlab/search/query.rb
@@ -0,0 +1,55 @@
+module Gitlab
+ module Search
+ class Query < SimpleDelegator
+ def initialize(query, filter_opts = {}, &block)
+ @raw_query = query.dup
+ @filters = []
+ @filter_options = { default_parser: :downcase.to_proc }.merge(filter_opts)
+
+ self.instance_eval(&block) if block_given?
+
+ @query = Gitlab::Search::ParsedQuery.new(*extract_filters)
+ # set the ParsedQuery as our default delegator thanks to SimpleDelegator
+ super(@query)
+ end
+
+ private
+
+ def filter(name, **attributes)
+ filter = { parser: @filter_options[:default_parser], name: name }.merge(attributes)
+
+ @filters << filter
+ end
+
+ def filter_options(**options)
+ @filter_options.merge!(options)
+ end
+
+ def extract_filters
+ fragments = []
+
+ filters = @filters.each_with_object([]) do |filter, parsed_filters|
+ match = @raw_query.split.find { |part| part =~ /\A#{filter[:name]}:/ }
+ next unless match
+
+ input = match.split(':')[1..-1].join
+ next if input.empty?
+
+ filter[:value] = parse_filter(filter, input)
+ filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?')
+ fragments << match
+
+ parsed_filters << filter
+ end
+
+ query = (@raw_query.split - fragments).join(' ')
+
+ [query, filters]
+ end
+
+ def parse_filter(filter, input)
+ filter[:parser].call(input)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index 4a87f43597e..b2d75aac1d0 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -24,6 +24,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
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 4b8aae4f5a2..5cedd9e84c2 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -1,5 +1,4 @@
-# Gitaly note: JV: two sets of straightforward RPC's. 1 Hard RPC: fork_repository.
-# SSH key operations are not part of Gitaly so will never be migrated.
+# Gitaly note: SSH key operations are not part of Gitaly so will never be migrated.
require 'securerandom'
@@ -153,8 +152,6 @@ module Gitlab
#
# Ex.
# mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new")
- #
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/873
def mv_repository(storage, path, new_path)
return false if path.empty? || new_path.empty?
@@ -169,19 +166,11 @@ module Gitlab
#
# Ex.
# fork_repository("nfs-file06", "gitlab/gitlab-ci", "nfs-file07", "new-namespace/gitlab-ci")
- #
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/817
def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path)
forked_from_relative_path = "#{forked_from_disk_path}.git"
fork_args = [forked_to_storage, "#{forked_to_disk_path}.git"]
- gitaly_migrate(:fork_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- GitalyGitlabProjects.new(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
- else
- gitlab_projects(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
- end
- end
+ GitalyGitlabProjects.new(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
end
# Removes a repository from file system, using rm_diretory which is an alias
@@ -193,8 +182,6 @@ module Gitlab
#
# Ex.
# remove_repository("/path/to/storage", "gitlab/gitlab-ci")
- #
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/873
def remove_repository(storage, name)
return false if name.empty?
diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb
index da24a36603e..9beb442bfa3 100644
--- a/lib/peek/rblineprof/custom_controller_helpers.rb
+++ b/lib/peek/rblineprof/custom_controller_helpers.rb
@@ -41,7 +41,7 @@ module Peek
]
end.sort_by{ |a,b,c,d,e,f| -f }
- output = "<div class='modal-dialog modal-lg'><div class='modal-content'>"
+ output = "<div class='modal-dialog modal-xl'><div class='modal-content'>"
output << "<div class='modal-header'>"
output << "<h4>Line profiling: #{human_description(params[:lineprofiler])}</h4>"
output << "<button class='close' type='button' data-dismiss='modal' aria-label='close'><span aria-hidden='true'>&times;</span></button>"
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index 247d7be7d78..d52c419d66b 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -50,6 +50,32 @@ namespace :gettext do
end
end
+ task :updated_check do
+ # Removing all pre-translated files speeds up `gettext:find` as the
+ # files don't need to be merged.
+ `rm locale/*/gitlab.po`
+
+ # `gettext:find` writes touches to temp files to `stderr` which would cause
+ # `static-analysis` to report failures. We can ignore these
+ silence_stream(STDERR) { Rake::Task['gettext:find'].invoke }
+
+ changed_files = `git diff --name-only`.lines.map(&:strip)
+
+ # reset the locale folder for potential next tasks
+ `git checkout -- locale`
+
+ if changed_files.include?('locale/gitlab.pot')
+ raise <<~MSG
+ Newly translated strings found, please add them to `gitlab.pot` by running:
+
+ bundle exec rake gettext:find; git checkout -- locale/*/gitlab.po;
+
+ Then commit and push the resulting changes to `locale/gitlab.pot`.
+
+ MSG
+ end
+ end
+
def report_errors_for_file(file, errors_for_file)
puts "Errors in `#{file}`:"
diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake
index 8b86a5c72a5..b5a9cddaacb 100644
--- a/lib/tasks/lint.rake
+++ b/lib/tasks/lint.rake
@@ -27,6 +27,7 @@ unless Rails.env.production?
scss_lint
flay
gettext:lint
+ gettext:updated_check
lint:static_verification
].each do |task|
pid = Process.fork do
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e8a5a15f410..db5c183bac3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-06-12 18:57+1000\n"
-"PO-Revision-Date: 2018-06-12 18:57+1000\n"
+"POT-Creation-Date: 2018-06-13 14:05+0200\n"
+"PO-Revision-Date: 2018-06-13 14:05+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -133,12 +133,12 @@ msgid "- show less"
msgstr ""
msgid "1 %{type} addition"
-msgid_plural "%d %{type} additions"
+msgid_plural "%{count} %{type} additions"
msgstr[0] ""
msgstr[1] ""
msgid "1 %{type} modification"
-msgid_plural "%d %{type} modifications"
+msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
@@ -2252,10 +2252,10 @@ msgstr ""
msgid "Gitaly"
msgstr ""
-msgid "Gitaly|Address"
+msgid "Gitaly Servers"
msgstr ""
-msgid "Gitaly Servers"
+msgid "Gitaly|Address"
msgstr ""
msgid "Go Back"
@@ -2419,6 +2419,15 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr ""
+msgid "ImageDiffViewer|2-up"
+msgstr ""
+
+msgid "ImageDiffViewer|Onion skin"
+msgstr ""
+
+msgid "ImageDiffViewer|Swipe"
+msgstr ""
+
msgid "Import"
msgstr ""
diff --git a/package.json b/package.json
index 4e1ca905ee8..cfe347c6eaa 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"dev-server": "nodemon -w 'config/webpack.config.js' --exec 'webpack-dev-server --config config/webpack.config.js'",
"eslint": "eslint --max-warnings 0 --ext .js,.vue .",
"eslint-fix": "eslint --max-warnings 0 --ext .js,.vue --fix .",
- "eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html .",
+ "eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html --no-inline-config .",
"karma": "BABEL_ENV=${BABEL_ENV:=karma} karma start --single-run true config/karma.config.js",
"karma-coverage": "BABEL_ENV=coverage karma start --single-run true config/karma.config.js",
"karma-start": "BABEL_ENV=karma karma start config/karma.config.js",
@@ -130,7 +130,7 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "3.0.0",
"nodemon": "^1.17.3",
- "prettier": "1.11.1",
+ "prettier": "1.12.1",
"webpack-dev-server": "^3.1.4"
}
}
diff --git a/public/favicon.png b/public/favicon.png
deleted file mode 100644
index 845e0ec34a5..00000000000
--- a/public/favicon.png
+++ /dev/null
Binary files differ
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 596205fe540..26c99efc53d 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -26,53 +26,58 @@ module QA
end
def initialize
+ # The login page is usually the entry point for all the scenarios so
+ # we need to wait for the instance to start. That said, in some cases
+ # we are already logged-in so we check both cases here.
wait(max: 500) do
- page.has_css?('.application')
+ page.has_css?('.login-page') ||
+ Page::Menu::Main.act { has_personal_area? }
end
end
- def set_initial_password_if_present
- if page.has_content?('Change your password')
- fill_in :user_password, with: Runtime::User.password
- fill_in :user_password_confirmation, with: Runtime::User.password
- click_button 'Change your password'
+ def sign_in_using_credentials
+ # Don't try to log-in if we're already logged-in
+ return if Page::Menu::Main.act { has_personal_area? }
+
+ using_wait_time 0 do
+ set_initial_password_if_present
+
+ if Runtime::User.ldap_user?
+ sign_in_using_ldap_credentials
+ else
+ sign_in_using_gitlab_credentials
+ end
end
end
- def sign_in_using_credentials
- if Runtime::User.ldap_user?
- sign_in_using_ldap_credentials
- else
- sign_in_using_gitlab_credentials
- end
+ def self.path
+ '/users/sign_in'
end
- def sign_in_using_ldap_credentials
- using_wait_time 0 do
- set_initial_password_if_present
+ private
- click_link 'LDAP'
+ def sign_in_using_ldap_credentials
+ click_link 'LDAP'
- fill_in :username, with: Runtime::User.ldap_username
- fill_in :password, with: Runtime::User.ldap_password
- click_button 'Sign in'
- end
+ fill_in :username, with: Runtime::User.ldap_username
+ fill_in :password, with: Runtime::User.ldap_password
+ click_button 'Sign in'
end
def sign_in_using_gitlab_credentials
- using_wait_time 0 do
- set_initial_password_if_present
-
- click_link 'Standard' if page.has_content?('LDAP')
+ click_link 'Standard' if page.has_content?('LDAP')
- fill_in :user_login, with: Runtime::User.name
- fill_in :user_password, with: Runtime::User.password
- click_button 'Sign in'
- end
+ fill_in :user_login, with: Runtime::User.name
+ fill_in :user_password, with: Runtime::User.password
+ click_button 'Sign in'
end
- def self.path
- '/users/sign_in'
+ def set_initial_password_if_present
+ return unless page.has_content?('Change your password')
+
+ fill_in :user_password, with: Runtime::User.password
+ fill_in :user_password_confirmation, with: Runtime::User.password
+ click_button 'Change your password'
end
end
end
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/menu/main.rb
index 644fedecc90..fda9c45c091 100644
--- a/qa/qa/page/menu/main.rb
+++ b/qa/qa/page/menu/main.rb
@@ -55,7 +55,8 @@ module QA
end
def has_personal_area?
- page.has_selector?('.qa-user-avatar')
+ # No need to wait, either we're logged-in, or not.
+ using_wait_time(0) { page.has_selector?('.qa-user-avatar') }
end
private
diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js
index 39de77bc333..6e4e36b9b2d 100644
--- a/scripts/frontend/prettier.js
+++ b/scripts/frontend/prettier.js
@@ -9,6 +9,8 @@ const getStagedFiles = require('./frontend_script_utils').getStagedFiles;
const mode = process.argv[2] || 'check';
const shouldSave = mode === 'save' || mode === 'save-all';
const allFiles = mode === 'check-all' || mode === 'save-all';
+let dirPath = process.argv[3] || '';
+if (dirPath && dirPath.charAt(dirPath.length - 1) !== '/') dirPath += '/';
const config = {
patterns: ['**/*.js', '**/*.vue', '**/*.scss'],
@@ -39,9 +41,10 @@ prettierIgnore.add(
const availableExtensions = Object.keys(config.parsers);
-console.log(`Loading ${allFiles ? 'All' : 'Staged'} Files ...`);
+console.log(`Loading ${allFiles ? 'All' : 'Selected'} Files ...`);
-const stagedFiles = allFiles ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
+const stagedFiles =
+ allFiles || dirPath ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
if (stagedFiles) {
if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) {
@@ -60,6 +63,13 @@ if (allFiles) {
const patterns = config.patterns;
const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
files = glob.sync(globPattern, { ignore }).filter(f => allFiles || stagedFiles.includes(f));
+} else if (dirPath) {
+ const ignore = config.ignore;
+ const patterns = config.patterns.map(item => {
+ return dirPath + item;
+ });
+ const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
+ files = glob.sync(globPattern, { ignore });
} else {
files = stagedFiles.filter(f => availableExtensions.includes(f.split('.').pop()));
}
@@ -73,12 +83,11 @@ if (!files.length) {
console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`);
-prettier
- .resolveConfig('.')
- .then(options => {
- console.log('Found options : ', options);
- files.forEach(file => {
- try {
+files.forEach(file => {
+ try {
+ prettier
+ .resolveConfig(file)
+ .then(options => {
const fileExtension = file.split('.').pop();
Object.assign(options, {
parser: config.parsers[fileExtension],
@@ -101,17 +110,17 @@ prettier
}
console.log(`Prettify Manually : ${file}`);
}
- } catch (error) {
- didError = true;
- console.log(`\n\nError with ${file}: ${error.message}`);
- }
- });
-
- if (didWarn || didError) {
- process.exit(1);
- }
- })
- .catch(e => {
- console.log(`Error on loading the Config File: ${e.message}`);
- process.exit(1);
- });
+ })
+ .catch(e => {
+ console.log(`Error on loading the Config File: ${e.message}`);
+ process.exit(1);
+ });
+ } catch (error) {
+ didError = true;
+ console.log(`\n\nError with ${file}: ${error.message}`);
+ }
+});
+
+if (didWarn || didError) {
+ process.exit(1);
+}
diff --git a/scripts/trigger-build b/scripts/trigger-build
new file mode 100755
index 00000000000..526f5164ede
--- /dev/null
+++ b/scripts/trigger-build
@@ -0,0 +1,181 @@
+#!/usr/bin/env ruby
+
+require 'net/http'
+require 'json'
+require 'cgi'
+
+module Trigger
+ OMNIBUS_PROJECT_PATH = 'gitlab-org/omnibus-gitlab'.freeze
+ CNG_PROJECT_PATH = 'gitlab-org/build/CNG'.freeze
+ TOKEN = ENV['BUILD_TRIGGER_TOKEN']
+
+ def self.ee?
+ ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
+ end
+
+ class Omnibus
+ def initialize
+ @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::OMNIBUS_PROJECT_PATH)}/trigger/pipeline")
+ @params = env_params.merge(file_params).merge(token: Trigger::TOKEN)
+ end
+
+ def invoke!
+ res = Net::HTTP.post_form(@uri, @params)
+ id = JSON.parse(res.body)['id']
+ project = Trigger::OMNIBUS_PROJECT_PATH
+
+ if id
+ puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}"
+ puts "Waiting for downstream pipeline status"
+ else
+ raise "Trigger failed! The response from the trigger is: #{res.body}"
+ end
+
+ Trigger::Pipeline.new(project, id)
+ end
+
+ private
+
+ def env_params
+ {
+ "ref" => ENV["OMNIBUS_BRANCH"] || "master",
+ "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
+ "variables[ALTERNATIVE_SOURCES]" => true,
+ "variables[ee]" => Trigger.ee? ? 'true' : 'false',
+ "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
+ "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
+ }
+ end
+
+ def file_params
+ Hash.new.tap do |params|
+ Dir.glob("*_VERSION").each do |version_file|
+ params["variables[#{version_file}]"] = File.read(version_file).strip
+ end
+ end
+ end
+ end
+
+ class CNG
+ def initialize
+ @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::CNG_PROJECT_PATH)}/trigger/pipeline")
+ @ref_name = ENV['CI_COMMIT_REF_NAME']
+ @username = ENV['GITLAB_USER_NAME']
+ @project_name = ENV['CI_PROJECT_NAME']
+ @job_id = ENV['CI_JOB_ID']
+ @params = env_params.merge(file_params).merge(token: Trigger::TOKEN)
+ end
+
+ #
+ # Trigger a pipeline
+ #
+ def invoke!
+ res = Net::HTTP.post_form(@uri, @params)
+ id = JSON.parse(res.body)['id']
+ project = Trigger::CNG_PROJECT_PATH
+
+ if id
+ puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}"
+ puts "Waiting for downstream pipeline status"
+ else
+ raise "Trigger failed! The response from the trigger is: #{res.body}"
+ end
+
+ Trigger::Pipeline.new(project, id)
+ end
+
+ private
+
+ def env_params
+ params = {
+ "ref" => ENV["CNG_BRANCH"] || "master",
+ "variables[TRIGGERED_USER]" => @username,
+ "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{@project_name}/-/jobs/#{@job_id}"
+ }
+
+ if Trigger.ee?
+ params["variables[GITLAB_EE_VERSION]"] = @ref_name
+ params["variables[EE_PIPELINE]"] = 'true'
+ else
+ params["variables[GITLAB_CE_VERSION]"] = @ref_name
+ params["variables[CE_PIPELINE]"] = 'true'
+ end
+
+ params
+ end
+
+ # Read version files from all components
+ def file_params
+ Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
+ raw_version = File.read(version_file).strip
+ # if the version matches semver format, treat it as a tag and prepend `v`
+ version = if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
+ "v#{raw_version}"
+ else
+ raw_version
+ end
+
+ params["variables[#{version_file}]"] = version
+ end
+ end
+ end
+
+ class Pipeline
+ INTERVAL = 60 # seconds
+ MAX_DURATION = 3600 * 3 # 3 hours
+
+ def initialize(project, id)
+ @start = Time.now.to_i
+ @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/pipelines/#{id}")
+ end
+
+ def wait!
+ loop do
+ raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
+
+ case status
+ when :created, :pending, :running
+ print "."
+ sleep INTERVAL
+ when :success
+ puts "Pipeline succeeded in #{duration} minutes!"
+ break
+ else
+ raise "Pipeline did not succeed!"
+ end
+
+ STDOUT.flush
+ end
+ end
+
+ def timeout?
+ Time.now.to_i > (@start + MAX_DURATION)
+ end
+
+ def duration
+ (Time.now.to_i - @start) / 60
+ end
+
+ def status
+ req = Net::HTTP::Get.new(@uri)
+ req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
+
+ res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http|
+ http.request(req)
+ end
+
+ JSON.parse(res.body)['status'].to_s.to_sym
+ end
+ end
+end
+
+case ARGV[0]
+when 'omnibus'
+ Trigger::Omnibus.new.invoke!.wait!
+when 'cng'
+ Trigger::CNG.new.invoke!.wait!
+else
+ puts "Please provide a valid option:
+ omnibus - Triggers a pipeline that builds the omnibus-gitlab package
+ cng - Triggers a pipeline that builds images used by the GitLab helm chart"
+end
diff --git a/scripts/trigger-build-cloud-native b/scripts/trigger-build-cloud-native
deleted file mode 100755
index b6ca75a588d..00000000000
--- a/scripts/trigger-build-cloud-native
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'gitlab'
-
-#
-# Configure credentials to be used with gitlab gem
-#
-Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
-end
-
-#
-# The remote project
-#
-GITLAB_CNG_REPO = 'gitlab-org/build/CNG'.freeze
-
-def ee?
- ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
-end
-
-def read_file_version(filename)
- raw_version = File.read(filename).strip
-
- # if the version matches semver format, treat it as a tag and prepend `v`
- if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
- "v#{raw_version}"
- else
- raw_version
- end
-end
-
-def params
- params = {
- 'GITLAB_SHELL_VERSION' => read_file_version('GITLAB_SHELL_VERSION'),
- 'GITALY_VERSION' => read_file_version('GITALY_SERVER_VERSION'),
- 'TRIGGERED_USER' => ENV['GITLAB_USER_NAME'],
- 'TRIGGER_SOURCE' => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
- }
-
- if ee?
- params['EE_PIPELINE'] = 'true'
- params['GITLAB_EE_VERSION'] = ENV['CI_COMMIT_REF_NAME']
- else
- params['CE_PIPELINE'] = 'true'
- params['GITLAB_CE_VERSION'] = ENV['CI_COMMIT_REF_NAME']
- end
-
- params
-end
-
-#
-# Trigger a pipeline
-#
-def trigger_pipeline
- # Create the cross project pipeline using CI_JOB_TOKEN
- pipeline = Gitlab.run_trigger(GITLAB_CNG_REPO, ENV['CI_JOB_TOKEN'], 'master', params)
-
- puts "Triggered https://gitlab.com/#{GITLAB_CNG_REPO}/pipelines/#{pipeline.id}"
-end
-
-trigger_pipeline
diff --git a/scripts/trigger-build-omnibus b/scripts/trigger-build-omnibus
deleted file mode 100755
index 95f35b44f5a..00000000000
--- a/scripts/trigger-build-omnibus
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'net/http'
-require 'json'
-require 'cgi'
-
-module Omnibus
- PROJECT_PATH = 'gitlab-org/omnibus-gitlab'.freeze
-
- class Trigger
- TOKEN = ENV['BUILD_TRIGGER_TOKEN']
- TRIGGERER = ENV['CI_PROJECT_NAME']
-
- def initialize
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Omnibus::PROJECT_PATH)}/trigger/pipeline")
- @params = env_params.merge(file_params).merge(token: TOKEN)
- end
-
- def invoke!
- res = Net::HTTP.post_form(@uri, @params)
- id = JSON.parse(res.body)['id']
-
- if id
- puts "Triggered https://gitlab.com/#{Omnibus::PROJECT_PATH}/pipelines/#{id}"
- puts "Waiting for downstream pipeline status"
- else
- raise "Trigger failed! The response from the trigger is: #{res.body}"
- end
-
- Omnibus::Pipeline.new(id)
- end
-
- private
-
- def ee?
- TRIGGERER == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
- end
-
- def env_params
- {
- "ref" => ENV["OMNIBUS_BRANCH"] || "master",
- "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
- "variables[ALTERNATIVE_SOURCES]" => true,
- "variables[ee]" => ee? ? 'true' : 'false',
- "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
- "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
- }
- end
-
- def file_params
- Hash.new.tap do |params|
- Dir.glob("*_VERSION").each do |version_file|
- params["variables[#{version_file}]"] = File.read(version_file).strip
- end
- end
- end
- end
-
- class Pipeline
- INTERVAL = 60 # seconds
- MAX_DURATION = 3600 * 3 # 3 hours
-
- def initialize(id)
- @start = Time.now.to_i
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Omnibus::PROJECT_PATH)}/pipelines/#{id}")
- end
-
- def wait!
- loop do
- raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
-
- case status
- when :created, :pending, :running
- print "."
- sleep INTERVAL
- when :success
- puts "Omnibus pipeline succeeded in #{duration} minutes!"
- break
- else
- raise "Omnibus pipeline did not succeed!"
- end
-
- STDOUT.flush
- end
- end
-
- def timeout?
- Time.now.to_i > (@start + MAX_DURATION)
- end
-
- def duration
- (Time.now.to_i - @start) / 60
- end
-
- def status
- req = Net::HTTP::Get.new(@uri)
- req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
-
- res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http|
- http.request(req)
- end
-
- JSON.parse(res.body)['status'].to_s.to_sym
- end
- end
-end
-
-Omnibus::Trigger.new.invoke!.wait!
diff --git a/spec/controllers/concerns/internal_redirect_spec.rb b/spec/controllers/concerns/internal_redirect_spec.rb
index a0ee13b2352..7e23b56356e 100644
--- a/spec/controllers/concerns/internal_redirect_spec.rb
+++ b/spec/controllers/concerns/internal_redirect_spec.rb
@@ -54,6 +54,31 @@ describe InternalRedirect do
end
end
+ describe '#sanitize_redirect' do
+ let(:valid_path) { '/hello/world?hello=world' }
+ let(:valid_url) { "http://test.host#{valid_path}" }
+
+ it 'returns `nil` for invalid paths' do
+ invalid_path = '//not/valid'
+
+ expect(controller.sanitize_redirect(invalid_path)).to eq nil
+ end
+
+ it 'returns `nil` for invalid urls' do
+ input = 'http://test.host:3000/invalid'
+
+ expect(controller.sanitize_redirect(input)).to eq nil
+ end
+
+ it 'returns input for valid paths' do
+ expect(controller.sanitize_redirect(valid_path)).to eq valid_path
+ end
+
+ it 'returns path for valid urls' do
+ expect(controller.sanitize_redirect(valid_url)).to eq valid_path
+ end
+ end
+
describe '#host_allowed?' do
it 'allows uris with the same host and port' do
expect(controller.host_allowed?(URI('http://test.host/test'))).to be(true)
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 00a7df6ccc8..9e696e9cb29 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -55,6 +55,25 @@ describe Projects::BlobController do
expect(json_response).to have_key 'raw_path'
end
end
+
+ context "with viewer=none" do
+ let(:id) { 'master/README.md' }
+
+ before do
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: id,
+ format: :json,
+ viewer: 'none')
+ end
+
+ it do
+ expect(response).to be_ok
+ expect(json_response).not_to have_key 'html'
+ expect(json_response).to have_key 'raw_path'
+ end
+ end
end
context 'with tree path' do
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 011843baffc..812833cc86b 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -29,7 +29,7 @@ describe Projects::ImportsController do
context 'when import is in progress' do
before do
- project.update_attribute(:import_status, :started)
+ project.update_attributes(import_status: :started)
end
it 'renders template' do
@@ -47,7 +47,7 @@ describe Projects::ImportsController do
context 'when import failed' do
before do
- project.update_attribute(:import_status, :failed)
+ project.update_attributes(import_status: :failed)
end
it 'redirects to new_namespace_project_import_path' do
@@ -59,7 +59,7 @@ describe Projects::ImportsController do
context 'when import finished' do
before do
- project.update_attribute(:import_status, :finished)
+ project.update_attributes(import_status: :finished)
end
context 'when project is a fork' do
@@ -108,7 +108,7 @@ describe Projects::ImportsController do
context 'when import never happened' do
before do
- project.update_attribute(:import_status, :none)
+ project.update_attributes(import_status: :none)
end
it 'redirects to namespace_project_path' do
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 22858de0475..7f5f0b76c51 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -234,7 +234,7 @@ describe Projects::MergeRequestsController do
body = JSON.parse(response.body)
expect(body['assignee'].keys)
- .to match_array(%w(name username avatar_url))
+ .to match_array(%w(name username avatar_url id state web_url))
end
end
@@ -337,7 +337,12 @@ describe Projects::MergeRequestsController do
context 'when the sha parameter matches the source SHA' do
def merge_with_sha(params = {})
- post :merge, base_params.merge(sha: merge_request.diff_head_sha).merge(params)
+ post_params = base_params.merge(sha: merge_request.diff_head_sha).merge(params)
+ if Gitlab.rails5?
+ post :merge, params: post_params, as: :json
+ else
+ post :merge, post_params
+ end
end
it 'returns :success' do
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index 11f54eef531..8d2fa6a1740 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -71,7 +71,7 @@ describe Projects::PagesController do
{
namespace_id: project.namespace,
project_id: project,
- project: { pages_https_only: false }
+ project: { pages_https_only: 'false' }
}
end
@@ -96,7 +96,7 @@ describe Projects::PagesController do
it 'calls the update service' do
expect(Projects::UpdateService)
.to receive(:new)
- .with(project, user, request_params[:project])
+ .with(project, user, ActionController::Parameters.new(request_params[:project]).permit!)
.and_return(update_service)
patch :update, request_params
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 3506305f755..4cdaa54e0bc 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -310,9 +310,19 @@ describe Projects::PipelineSchedulesController do
end
def go
- put :update, namespace_id: project.namespace.to_param,
- project_id: project, id: pipeline_schedule,
- schedule: schedule
+ if Gitlab.rails5?
+ put :update, params: { namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: pipeline_schedule,
+ schedule: schedule },
+ as: :html
+
+ else
+ put :update, namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: pipeline_schedule,
+ schedule: schedule
+ end
end
end
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 555b186fe31..2b61e0d4a85 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -257,15 +257,15 @@ describe SessionsController do
end
end
- describe '#new' do
+ describe "#new" do
before do
set_devise_mapping(context: @request)
end
- it 'redirects correctly for referer on same host with params' do
- search_path = '/search?search=seed_project'
- allow(controller.request).to receive(:referer)
- .and_return('http://%{host}%{path}' % { host: 'test.host', path: search_path })
+ it "redirects correctly for referer on same host with params" do
+ host = "test.host"
+ search_path = "/search?search=seed_project"
+ request.headers[:HTTP_REFERER] = "http://#{host}#{search_path}"
get(:new, redirect_to_referer: :yes)
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 1df2c954893..eb94d395a9e 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -580,23 +580,6 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(404)
end
end
-
- context 'has a valid filename on the version file' do
- it 'successfully returns the file' do
- get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png'
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"'
- end
- end
-
- context 'has an invalid filename on the version file' do
- it 'returns a 404' do
- get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png'
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
end
end
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index ed47f7ed390..29280bd6e06 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -65,7 +65,11 @@ feature 'Dashboard Groups page', :js do
fill_in 'filter', with: group.name
wait_for_requests
+ expect(page).to have_content(group.name)
+ expect(page).not_to have_content(nested_group.parent.name)
+
fill_in 'filter', with: ''
+ page.find('[name="filter"]').send_keys(:enter)
wait_for_requests
expect(page).to have_content(group.name)
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index 801a33979ff..ad02b454aee 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -35,7 +35,11 @@ describe 'Explore Groups page', :js do
fill_in 'filter', with: group.name
wait_for_requests
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(public_group.full_name)
+
fill_in 'filter', with: ""
+ page.find('[name="filter"]').send_keys(:enter)
wait_for_requests
expect(page).to have_content(group.full_name)
diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb
index 5d6cd44ad1c..90d02f7e40f 100644
--- a/spec/features/ics/dashboard_issues_spec.rb
+++ b/spec/features/ics/dashboard_issues_spec.rb
@@ -11,13 +11,25 @@ describe 'Dashboard Issues Calendar Feed' do
end
context 'when authenticated' do
- it 'renders calendar feed' do
- sign_in user
- visit issues_dashboard_path(:ics)
+ context 'with no referer' do
+ it 'renders calendar feed' do
+ sign_in user
+ visit issues_dashboard_path(:ics)
- expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
- expect(body).to have_text('BEGIN:VCALENDAR')
+ expect(response_headers['Content-Type']).to have_content('text/calendar')
+ expect(body).to have_text('BEGIN:VCALENDAR')
+ end
+ end
+
+ context 'with GitLab as the referer' do
+ it 'renders calendar feed as text/plain' do
+ sign_in user
+ page.driver.header('Referer', issues_dashboard_url(host: Settings.gitlab.base_url))
+ visit issues_dashboard_path(:ics)
+
+ expect(response_headers['Content-Type']).to have_content('text/plain')
+ expect(body).to have_text('BEGIN:VCALENDAR')
+ end
end
end
@@ -28,7 +40,6 @@ describe 'Dashboard Issues Calendar Feed' do
visit issues_dashboard_path(:ics, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
@@ -38,7 +49,6 @@ describe 'Dashboard Issues Calendar Feed' do
visit issues_dashboard_path(:ics, feed_token: user.feed_token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb
index 0a049be2ffe..24de5b4b7c6 100644
--- a/spec/features/ics/group_issues_spec.rb
+++ b/spec/features/ics/group_issues_spec.rb
@@ -13,13 +13,25 @@ describe 'Group Issues Calendar Feed' do
end
context 'when authenticated' do
- it 'renders calendar feed' do
- sign_in user
- visit issues_group_path(group, :ics)
+ context 'with no referer' do
+ it 'renders calendar feed' do
+ sign_in user
+ visit issues_group_path(group, :ics)
- expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
- expect(body).to have_text('BEGIN:VCALENDAR')
+ expect(response_headers['Content-Type']).to have_content('text/calendar')
+ expect(body).to have_text('BEGIN:VCALENDAR')
+ end
+ end
+
+ context 'with GitLab as the referer' do
+ it 'renders calendar feed as text/plain' do
+ sign_in user
+ page.driver.header('Referer', issues_group_url(group, host: Settings.gitlab.base_url))
+ visit issues_group_path(group, :ics)
+
+ expect(response_headers['Content-Type']).to have_content('text/plain')
+ expect(body).to have_text('BEGIN:VCALENDAR')
+ end
end
end
@@ -30,7 +42,6 @@ describe 'Group Issues Calendar Feed' do
visit issues_group_path(group, :ics, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
@@ -40,7 +51,6 @@ describe 'Group Issues Calendar Feed' do
visit issues_group_path(group, :ics, feed_token: user.feed_token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb
index b99e9607f1d..2ca3d52a5be 100644
--- a/spec/features/ics/project_issues_spec.rb
+++ b/spec/features/ics/project_issues_spec.rb
@@ -12,13 +12,25 @@ describe 'Project Issues Calendar Feed' do
end
context 'when authenticated' do
- it 'renders calendar feed' do
- sign_in user
- visit project_issues_path(project, :ics)
+ context 'with no referer' do
+ it 'renders calendar feed' do
+ sign_in user
+ visit project_issues_path(project, :ics)
- expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
- expect(body).to have_text('BEGIN:VCALENDAR')
+ expect(response_headers['Content-Type']).to have_content('text/calendar')
+ expect(body).to have_text('BEGIN:VCALENDAR')
+ end
+ end
+
+ context 'with GitLab as the referer' do
+ it 'renders calendar feed as text/plain' do
+ sign_in user
+ page.driver.header('Referer', project_issues_url(project, host: Settings.gitlab.base_url))
+ visit project_issues_path(project, :ics)
+
+ expect(response_headers['Content-Type']).to have_content('text/plain')
+ expect(body).to have_text('BEGIN:VCALENDAR')
+ end
end
end
@@ -29,7 +41,6 @@ describe 'Project Issues Calendar Feed' do
visit project_issues_path(project, :ics, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
@@ -39,7 +50,6 @@ describe 'Project Issues Calendar Feed' do
visit project_issues_path(project, :ics, feed_token: user.feed_token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
- expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index cbd0949c192..c8115db9212 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -31,7 +31,7 @@ describe 'Dropdown assignee', :js do
describe 'behavior' do
it 'opens when the search bar has assignee:' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
@@ -44,6 +44,7 @@ describe 'Dropdown assignee', :js do
it 'should show loading indicator when opened' do
slow_requests do
+ # We aren't using `input_filtered_search` because we want to see the loading indicator
filtered_search.set('assignee:')
expect(page).to have_css('#js-dropdown-assignee .filter-dropdown-loading', visible: true)
@@ -51,19 +52,19 @@ describe 'Dropdown assignee', :js do
end
it 'should hide loading indicator when loaded' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading')
end
it 'should load all the assignees when opened' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(dropdown_assignee_size).to eq(4)
end
it 'shows current user at top of dropdown' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name)
end
@@ -71,7 +72,7 @@ describe 'Dropdown assignee', :js do
describe 'filtering' do
before do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
@@ -79,23 +80,21 @@ describe 'Dropdown assignee', :js do
end
it 'filters by name' do
- filtered_search.send_keys('j')
+ input_filtered_search('jac', submit: false, extra_space: false)
- expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name)
end
it 'filters by case insensitive name' do
- filtered_search.send_keys('J')
+ input_filtered_search('JAC', submit: false, extra_space: false)
- expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name)
end
it 'filters by username with symbol' do
- filtered_search.send_keys('@ot')
+ input_filtered_search('@ott', submit: false, extra_space: false)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -103,7 +102,7 @@ describe 'Dropdown assignee', :js do
end
it 'filters by case insensitive username with symbol' do
- filtered_search.send_keys('@OT')
+ input_filtered_search('@OTT', submit: false, extra_space: false)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -111,7 +110,9 @@ describe 'Dropdown assignee', :js do
end
it 'filters by username without symbol' do
- filtered_search.send_keys('ot')
+ input_filtered_search('ott', submit: false, extra_space: false)
+
+ wait_for_requests
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -119,7 +120,9 @@ describe 'Dropdown assignee', :js do
end
it 'filters by case insensitive username without symbol' do
- filtered_search.send_keys('OT')
+ input_filtered_search('OTT', submit: false, extra_space: false)
+
+ wait_for_requests
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -129,7 +132,7 @@ describe 'Dropdown assignee', :js do
describe 'selecting from dropdown' do
before do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
end
it 'fills in the assignee username when the assignee has not been filtered' do
@@ -143,7 +146,7 @@ describe 'Dropdown assignee', :js do
end
it 'fills in the assignee username when the assignee has been filtered' do
- filtered_search.send_keys('roo')
+ input_filtered_search('roo', submit: false, extra_space: false)
click_assignee(user.name)
wait_for_requests
@@ -165,7 +168,7 @@ describe 'Dropdown assignee', :js do
describe 'selecting from dropdown without Ajax call' do
before do
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
end
after do
@@ -183,31 +186,31 @@ describe 'Dropdown assignee', :js do
describe 'input has existing content' do
it 'opens assignee dropdown with existing search term' do
- filtered_search.set('searchTerm assignee:')
+ input_filtered_search('searchTerm assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing author' do
- filtered_search.set('author:@user assignee:')
+ input_filtered_search('author:@user assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing label' do
- filtered_search.set('label:~bug assignee:')
+ input_filtered_search('label:~bug assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing milestone' do
- filtered_search.set('milestone:%v1.0 assignee:')
+ input_filtered_search('milestone:%v1.0 assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing my-reaction' do
- filtered_search.set('my-reaction:star assignee:')
+ input_filtered_search('my-reaction:star assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
@@ -215,8 +218,7 @@ describe 'Dropdown assignee', :js do
describe 'caching requests' do
it 'caches requests after the first load' do
- filtered_search.set('assignee')
- filtered_search.send_keys(':')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
initial_size = dropdown_assignee_size
expect(initial_size).to be > 0
@@ -224,8 +226,7 @@ describe 'Dropdown assignee', :js do
new_user = create(:user)
project.add_master(new_user)
find('.filtered-search-box .clear-search').click
- filtered_search.set('assignee')
- filtered_search.send_keys(':')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(dropdown_assignee_size).to eq(initial_size)
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index dacca494755..17818beb947 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -226,7 +226,9 @@ feature 'Issues > User uses quick actions', :js do
it 'does not move the issue' do
add_note("/move #{project_unauthorized.full_path}")
- expect(page).not_to have_content 'Commands applied'
+ wait_for_requests
+
+ expect(page).to have_content 'Commands applied'
expect(issue.reload).to be_open
end
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 4700ada1aae..5573148f8bc 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -34,7 +34,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- expect(page).to have_selector('span.badge', text: label.title)
+ expect(page).to have_selector('.badge', text: label.title)
end
end
@@ -45,7 +45,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- expect(page).not_to have_selector('span.badge', text: child_group_label.title)
+ expect(page).not_to have_selector('.badge', text: child_group_label.title)
end
end
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 43a23c42f83..1552a3512dd 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -22,7 +22,8 @@ describe 'Project deploy keys', :js do
accept_confirm { find('.ic-remove').click() }
- expect(page).not_to have_selector('.fa-spinner', count: 0)
+ wait_for_requests
+
expect(page).to have_selector('.deploy-key', count: 0)
end
end
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index c1307ab640f..9bfcb1e816a 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -166,8 +166,7 @@ feature 'Diff file viewer', :js do
context 'expanding the diff' do
before do
- # We can't use `click_link` because the "link" doesn't have an `href`.
- find('a.click-to-expand').click
+ click_button 'Click to expand it.'
wait_for_requests
end
diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json
index f7bc137c90c..cf257ac00de 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_basic.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json
@@ -14,7 +14,21 @@
"subscribed": { "type": ["boolean", "null"] },
"participants": { "type": "array" },
"allow_collaboration": { "type": "boolean"},
- "allow_maintainer_to_push": { "type": "boolean"}
+ "allow_maintainer_to_push": { "type": "boolean"},
+ "assignee": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "user.json" }
+ ]
+ },
+ "milestone": {
+ "type": [ "object", "null" ]
+ },
+ "labels": {
+ "type": [ "array", "null" ]
+ },
+ "task_status": { "type": "string" },
+ "task_status_short": { "type": "string" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/branch.json b/spec/fixtures/api/schemas/public_api/v4/branch.json
index a3581178974..a8891680d06 100644
--- a/spec/fixtures/api/schemas/public_api/v4/branch.json
+++ b/spec/fixtures/api/schemas/public_api/v4/branch.json
@@ -14,7 +14,8 @@
"merged": { "type": "boolean" },
"protected": { "type": "boolean" },
"developers_can_push": { "type": "boolean" },
- "developers_can_merge": { "type": "boolean" }
+ "developers_can_merge": { "type": "boolean" },
+ "can_push": { "type": "boolean" }
},
"additionalProperties": false
}
diff --git a/spec/graphql/resolvers/merge_request_resolver_spec.rb b/spec/graphql/resolvers/merge_request_resolver_spec.rb
index af015533209..73993b3a039 100644
--- a/spec/graphql/resolvers/merge_request_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_request_resolver_spec.rb
@@ -10,49 +10,36 @@ describe Resolvers::MergeRequestResolver do
set(:other_project) { create(:project, :repository) }
set(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) }
- let(:full_path) { project.full_path }
let(:iid_1) { merge_request_1.iid }
let(:iid_2) { merge_request_2.iid }
- let(:other_full_path) { other_project.full_path }
let(:other_iid) { other_merge_request.iid }
describe '#resolve' do
it 'batch-resolves merge requests by target project full path and IID' do
- path = full_path # avoid database query
-
result = batch(max_queries: 2) do
- [resolve_mr(path, iid_1), resolve_mr(path, iid_2)]
+ [resolve_mr(project, iid_1), resolve_mr(project, iid_2)]
end
expect(result).to contain_exactly(merge_request_1, merge_request_2)
end
it 'can batch-resolve merge requests from different projects' do
- path = project.full_path # avoid database queries
- other_path = other_full_path
-
result = batch(max_queries: 3) do
- [resolve_mr(path, iid_1), resolve_mr(path, iid_2), resolve_mr(other_path, other_iid)]
+ [resolve_mr(project, iid_1), resolve_mr(project, iid_2), resolve_mr(other_project, other_iid)]
end
expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request)
end
it 'resolves an unknown iid to nil' do
- result = batch { resolve_mr(full_path, -1) }
-
- expect(result).to be_nil
- end
-
- it 'resolves a known iid for an unknown full_path to nil' do
- result = batch { resolve_mr('unknown/project', iid_1) }
+ result = batch { resolve_mr(project, -1) }
expect(result).to be_nil
end
end
- def resolve_mr(full_path, iid)
- resolve(described_class, args: { full_path: full_path, iid: iid })
+ def resolve_mr(project, iid)
+ resolve(described_class, obj: project, args: { iid: iid })
end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index e0f89105b86..b4eeca2e3f1 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -2,4 +2,13 @@ require 'spec_helper'
describe GitlabSchema.types['Project'] do
it { expect(described_class.graphql_name).to eq('Project') }
+
+ describe 'nested merge request' do
+ it { expect(described_class).to have_graphql_field(:merge_request) }
+
+ it 'authorizes the merge request' do
+ expect(described_class.fields['mergeRequest'])
+ .to require_graphql_authorizations(:read_merge_request)
+ end
+ end
end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index 8488252fd59..e1df6f9811d 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -5,7 +5,7 @@ describe GitlabSchema.types['Query'] do
expect(described_class.graphql_name).to eq('Query')
end
- it { is_expected.to have_graphql_fields(:project, :merge_request, :echo) }
+ it { is_expected.to have_graphql_fields(:project, :echo) }
describe 'project field' do
subject { described_class.fields['project'] }
@@ -20,18 +20,4 @@ describe GitlabSchema.types['Query'] do
is_expected.to require_graphql_authorizations(:read_project)
end
end
-
- describe 'merge_request field' do
- subject { described_class.fields['mergeRequest'] }
-
- it 'finds MRs by project and IID' do
- is_expected.to have_graphql_arguments(:full_path, :iid)
- is_expected.to have_graphql_type(Types::MergeRequestType)
- is_expected.to have_graphql_resolver(Resolvers::MergeRequestResolver)
- end
-
- it 'authorizes with read_merge_request' do
- is_expected.to require_graphql_authorizations(:read_merge_request)
- end
- end
end
diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js
index f920c4ca945..8b79624d9f4 100644
--- a/spec/javascripts/blob/viewer/index_spec.js
+++ b/spec/javascripts/blob/viewer/index_spec.js
@@ -32,7 +32,7 @@ describe('Blob viewer', () => {
afterEach(() => {
mock.restore();
- location.hash = '';
+ window.location.hash = '';
});
it('loads source file after switching views', (done) => {
@@ -49,7 +49,7 @@ describe('Blob viewer', () => {
});
it('loads source file when line number is in hash', (done) => {
- location.hash = '#L1';
+ window.location.hash = '#L1';
new BlobViewer();
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js
index 93dc60d59fe..6f679369289 100644
--- a/spec/javascripts/bootstrap_linked_tabs_spec.js
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js
@@ -36,7 +36,7 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
describe('on click', () => {
it('should change the url according to the clicked tab', () => {
- const historySpy = spyOn(history, 'replaceState').and.callFake(() => {});
+ const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {});
const linkedTabs = new LinkedTabs({
action: 'show',
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index 60d100e8544..28b89157bd3 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -56,7 +56,7 @@ describe('Commits List', () => {
beforeEach(() => {
commitsList.searchField.val('');
- spyOn(history, 'replaceState').and.stub();
+ spyOn(window.history, 'replaceState').and.stub();
mock = new MockAdapter(axios);
mock.onGet('/h5bp/html5-boilerplate/commits/master').reply(200, {
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 615168645b4..6968fbc7ce7 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -220,7 +220,7 @@ describe('Environment', () => {
);
component = mountComponent(EnvironmentsComponent, mockData);
- spyOn(history, 'pushState').and.stub();
+ spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index f5ce4df0bfe..51d4213c38f 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -177,7 +177,7 @@ describe('Environments Folder View', () => {
});
component = mountComponent(Component, mockData);
- spyOn(history, 'pushState').and.stub();
+ spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js
index 4e93fd91751..108e0064c47 100644
--- a/spec/javascripts/gl_field_errors_spec.js
+++ b/spec/javascripts/gl_field_errors_spec.js
@@ -8,7 +8,9 @@ describe('GL Style Field Errors', function() {
beforeEach(function() {
loadFixtures('static/gl_field_errors.html.raw');
- const $form = this.$form = $('form.gl-show-field-errors');
+ const $form = $('form.gl-show-field-errors');
+
+ this.$form = $form;
this.fieldErrors = new GlFieldErrors($form);
});
diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js
index 323fee3767e..f6c3ce5aecc 100644
--- a/spec/javascripts/helpers/user_mock_data_helper.js
+++ b/spec/javascripts/helpers/user_mock_data_helper.js
@@ -1,7 +1,7 @@
export default {
createNumberRandomUsers(numberUsers) {
const users = [];
- for (let i = 0; i < numberUsers; i = i += 1) {
+ for (let i = 0; i < numberUsers; i += 1) {
users.push(
{
avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index 8f7cf24c22f..bf96170f703 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -93,14 +93,14 @@ describe('Multi-file editor commit sidebar list item', () => {
describe('is active', () => {
it('does not add active class when dont keys match', () => {
- expect(vm.$el.classList).not.toContain('is-active');
+ expect(vm.$el.querySelector('.is-active')).toBe(null);
});
it('adds active class when keys match', done => {
vm.keyPrefix = 'staged';
vm.$nextTick(() => {
- expect(vm.$el.classList).toContain('is-active');
+ expect(vm.$el.querySelector('.is-active')).not.toBe(null);
done();
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
index 6fb52378386..b786be55019 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
@@ -16,6 +16,7 @@ describe('Multi-file editor commit sidebar list', () => {
iconName: 'staged',
action: 'stageAllChanges',
actionBtnText: 'stage all',
+ actionBtnIcon: 'history',
itemActionComponent: 'stage-button',
activeFileKey: 'staged-testing',
keyPrefix: 'staged',
@@ -42,7 +43,7 @@ describe('Multi-file editor commit sidebar list', () => {
});
it('renders list', () => {
- expect(vm.$el.querySelectorAll('li').length).toBe(1);
+ expect(vm.$el.querySelectorAll('.multi-file-commit-list > li').length).toBe(1);
});
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
index 6bf8710bda7..a5b906da8a1 100644
--- a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
@@ -39,7 +39,7 @@ describe('IDE stage file button', () => {
});
it('calls store with discard button', () => {
- vm.$el.querySelectorAll('.btn')[1].click();
+ vm.$el.querySelector('.dropdown-menu button').click();
expect(vm.discardFileChanges).toHaveBeenCalledWith(f.path);
});
diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js
index 531bcd6e540..6bf309fb4bf 100644
--- a/spec/javascripts/ide/components/repo_commit_section_spec.js
+++ b/spec/javascripts/ide/components/repo_commit_section_spec.js
@@ -111,7 +111,7 @@ describe('RepoCommitSection', () => {
});
it('renders a commit section', () => {
- const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
+ const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list > li')];
const allFiles = vm.$store.state.changedFiles.concat(vm.$store.state.stagedFiles);
expect(changedFileElements.length).toEqual(4);
@@ -140,22 +140,26 @@ describe('RepoCommitSection', () => {
vm.$el.querySelector('.multi-file-discard-btn .btn').click();
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
- 1,
- );
+ expect(
+ vm.$el
+ .querySelector('.ide-commit-list-container')
+ .querySelectorAll('.multi-file-commit-list > li').length,
+ ).toBe(1);
done();
});
});
it('discards a single file', done => {
- vm.$el.querySelectorAll('.multi-file-discard-btn .btn')[1].click();
+ vm.$el.querySelector('.multi-file-discard-btn .dropdown-menu button').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
- expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
- 1,
- );
+ expect(
+ vm.$el
+ .querySelector('.ide-commit-list-container')
+ .querySelectorAll('.multi-file-commit-list > li').length,
+ ).toBe(1);
done();
});
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index d318521d0a0..2256deb7dac 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -315,6 +315,17 @@ describe('RepoEditor', () => {
done();
});
});
+
+ it('calls updateDimensions when rightPane is updated', done => {
+ vm.$store.state.rightPane = 'testing';
+
+ vm.$nextTick(() => {
+ expect(vm.editor.updateDimensions).toHaveBeenCalled();
+ expect(vm.editor.updateDiffView).toHaveBeenCalled();
+
+ done();
+ });
+ });
});
describe('show tabs', () => {
diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js
index c1932284d53..c2cb964ea87 100644
--- a/spec/javascripts/ide/lib/editor_spec.js
+++ b/spec/javascripts/ide/lib/editor_spec.js
@@ -263,4 +263,23 @@ describe('Multi-file editor library', () => {
expect(instance.isDiffEditorType).toBe(false);
});
});
+
+ it('sets quickSuggestions to false when language is markdown', () => {
+ instance.createInstance(holder);
+
+ spyOn(instance.instance, 'updateOptions').and.callThrough();
+
+ const model = instance.createModel({
+ ...file(),
+ key: 'index.md',
+ path: 'index.md',
+ });
+
+ instance.attachModel(model);
+
+ expect(instance.instance.updateOptions).toHaveBeenCalledWith({
+ readOnly: false,
+ quickSuggestions: false,
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index 7bebc2288e3..5746683917e 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -166,12 +166,12 @@ describe('IDE store file actions', () => {
});
it('resets location.hash for line highlighting', done => {
- location.hash = 'test';
+ window.location.hash = 'test';
store
.dispatch('setFileActive', localFile.path)
.then(() => {
- expect(location.hash).not.toBe('test');
+ expect(window.location.hash).not.toBe('test');
done();
})
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index a2869ff378b..133ad627f34 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -108,77 +108,6 @@ describe('IDE commit module actions', () => {
});
});
- describe('checkCommitStatus', () => {
- beforeEach(() => {
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
- });
-
- it('calls service', done => {
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }),
- );
-
- store
- .dispatch('commit/checkCommitStatus')
- .then(() => {
- expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
-
- done();
- })
- .catch(done.fail);
- });
-
- it('returns true if current ref does not equal returned ID', done => {
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }),
- );
-
- store
- .dispatch('commit/checkCommitStatus')
- .then(val => {
- expect(val).toBeTruthy();
-
- done();
- })
- .catch(done.fail);
- });
-
- it('returns false if current ref equals returned ID', done => {
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '1' },
- },
- }),
- );
-
- store
- .dispatch('commit/checkCommitStatus')
- .then(val => {
- expect(val).toBeFalsy();
-
- done();
- })
- .catch(done.fail);
- });
- });
-
describe('updateFilesAfterCommit', () => {
const data = {
id: '123',
@@ -314,6 +243,7 @@ describe('IDE commit module actions', () => {
...file('changed'),
type: 'blob',
active: true,
+ lastCommitSha: '123456789',
};
store.state.stagedFiles.push(f);
store.state.changedFiles = [
@@ -366,6 +296,7 @@ describe('IDE commit module actions', () => {
file_path: jasmine.anything(),
content: jasmine.anything(),
encoding: jasmine.anything(),
+ last_commit_id: undefined,
},
],
start_branch: 'master',
@@ -376,6 +307,32 @@ describe('IDE commit module actions', () => {
.catch(done.fail);
});
+ it('sends lastCommit ID when not creating new branch', done => {
+ store.state.commit.commitAction = '1';
+
+ store
+ .dispatch('commit/commitChanges')
+ .then(() => {
+ expect(service.commit).toHaveBeenCalledWith('abcproject', {
+ branch: jasmine.anything(),
+ commit_message: 'testing 123',
+ actions: [
+ {
+ action: 'update',
+ file_path: jasmine.anything(),
+ content: jasmine.anything(),
+ encoding: jasmine.anything(),
+ last_commit_id: '123456789',
+ },
+ ],
+ start_branch: undefined,
+ });
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
it('sets last Commit Msg', done => {
store
.dispatch('commit/commitChanges')
diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
index 03ec08d05c3..fa4c18931e5 100644
--- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
@@ -208,18 +208,19 @@ describe('IDE merge requests actions', () => {
expect(commit.calls.argsFor(1)).toEqual(['SET_CURRENT_MERGE_REQUEST', '1', { root: true }]);
expect(commit.calls.argsFor(2)).toEqual(['RESET_OPEN_FILES', null, { root: true }]);
- expect(dispatch.calls.argsFor(0)).toEqual([
- 'pipelines/resetLatestPipeline',
+ expect(dispatch.calls.argsFor(0)).toEqual(['setCurrentBranchId', '', { root: true }]);
+ expect(dispatch.calls.argsFor(1)).toEqual([
+ 'pipelines/stopPipelinePolling',
null,
{ root: true },
]);
- expect(dispatch.calls.argsFor(1)).toEqual(['setCurrentBranchId', '', { root: true }]);
- expect(dispatch.calls.argsFor(2)).toEqual([
- 'pipelines/stopPipelinePolling',
+ expect(dispatch.calls.argsFor(2)).toEqual(['setRightPane', null, { root: true }]);
+ expect(dispatch.calls.argsFor(3)).toEqual([
+ 'pipelines/resetLatestPipeline',
null,
{ root: true },
]);
- expect(dispatch.calls.argsFor(3)).toEqual([
+ expect(dispatch.calls.argsFor(4)).toEqual([
'pipelines/clearEtagPoll',
null,
{ root: true },
diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
index f2f8e780cd1..f47e69d6e5b 100644
--- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
@@ -18,6 +18,7 @@ import actions, {
receiveJobTraceError,
receiveJobTraceSuccess,
fetchJobTrace,
+ resetLatestPipeline,
} from '~/ide/stores/modules/pipelines/actions';
import state from '~/ide/stores/modules/pipelines/state';
import * as types from '~/ide/stores/modules/pipelines/mutation_types';
@@ -416,4 +417,20 @@ describe('IDE pipelines actions', () => {
});
});
});
+
+ describe('resetLatestPipeline', () => {
+ it('commits reset mutations', done => {
+ testAction(
+ resetLatestPipeline,
+ null,
+ mockedState,
+ [
+ { type: types.RECEIVE_LASTEST_PIPELINE_SUCCESS, payload: null },
+ { type: types.SET_DETAIL_JOB, payload: null },
+ ],
+ [],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index f38ac6dd82f..a7bd443af51 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -1,4 +1,5 @@
import * as utils from '~/ide/stores/utils';
+import { file } from '../helpers';
describe('Multi-file store utils', () => {
describe('setPageTitle', () => {
@@ -63,4 +64,59 @@ describe('Multi-file store utils', () => {
expect(foundEntry).toBeUndefined();
});
});
+
+ describe('createCommitPayload', () => {
+ it('returns API payload', () => {
+ const state = {
+ commitMessage: 'commit message',
+ };
+ const rootState = {
+ stagedFiles: [
+ {
+ ...file('staged'),
+ path: 'staged',
+ content: 'updated file content',
+ lastCommitSha: '123456789',
+ },
+ {
+ ...file('newFile'),
+ path: 'added',
+ tempFile: true,
+ content: 'new file content',
+ base64: true,
+ lastCommitSha: '123456789',
+ },
+ ],
+ currentBranchId: 'master',
+ };
+ const payload = utils.createCommitPayload({
+ branch: 'master',
+ newBranch: false,
+ state,
+ rootState,
+ });
+
+ expect(payload).toEqual({
+ branch: 'master',
+ commit_message: 'commit message',
+ actions: [
+ {
+ action: 'update',
+ file_path: 'staged',
+ content: 'updated file content',
+ encoding: 'text',
+ last_commit_id: '123456789',
+ },
+ {
+ action: 'create',
+ file_path: 'added',
+ content: 'new file content',
+ encoding: 'base64',
+ last_commit_id: '123456789',
+ },
+ ],
+ start_branch: undefined,
+ });
+ });
+ });
});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index bf1f0c822fe..eb5e0bddb74 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -145,7 +145,7 @@ describe('Issuable output', () => {
resolve({
data: {
confidential: false,
- web_url: location.pathname,
+ web_url: window.location.pathname,
},
});
}));
@@ -177,7 +177,7 @@ describe('Issuable output', () => {
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
data: {
- web_url: location.pathname,
+ web_url: window.location.pathname,
confidential: vm.isConfidential,
},
});
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 2d7cc3443cf..a9ec7f42a9d 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -40,13 +40,13 @@ describe('common_utils', () => {
});
it('should decode params', () => {
- history.pushState('', '', '?label_name%5B%5D=test');
+ window.history.pushState('', '', '?label_name%5B%5D=test');
expect(
commonUtils.getUrlParamsArray()[0],
).toBe('label_name[]=test');
- history.pushState('', '', '?');
+ window.history.pushState('', '', '?');
});
});
diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js
index cfd037633e9..2ffdec7314d 100644
--- a/spec/javascripts/notes/components/noteable_note_spec.js
+++ b/spec/javascripts/notes/components/noteable_note_spec.js
@@ -32,7 +32,6 @@ describe('issue_note', () => {
it('should render note header content', () => {
expect(vm.$el.querySelector('.note-header .note-header-author-name').textContent.trim()).toEqual(note.author.name);
- expect(vm.$el.querySelector('.note-header .note-headline-meta').textContent.trim()).toContain('commented');
});
it('should render note actions', () => {
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 648fb3e9bd3..acbf23e2007 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -974,7 +974,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
).toBeFalsy();
expect(
$tempNoteHeader
- .find('.d-none.d-sm-block')
+ .find('.d-none.d-sm-inline-block')
.text()
.trim(),
).toEqual(currentUserFullname);
@@ -1020,7 +1020,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
const $tempNoteHeader = $tempNote.find('.note-header');
expect(
$tempNoteHeader
- .find('.d-none.d-sm-block')
+ .find('.d-none.d-sm-inline-block')
.text()
.trim(),
).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;');
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index ff17602da2b..50141bd99b4 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -427,7 +427,7 @@ describe('Pipelines', () => {
describe('methods', () => {
beforeEach(() => {
- spyOn(history, 'pushState').and.stub();
+ spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/javascripts/profile/account/components/update_username_spec.js
index bac306edf5a..5311499fb73 100644
--- a/spec/javascripts/profile/account/components/update_username_spec.js
+++ b/spec/javascripts/profile/account/components/update_username_spec.js
@@ -90,7 +90,8 @@ describe('UpdateUsername component', () => {
it('confirmation modal should escape usernames properly', done => {
const { modalBody } = findElements();
- vm.username = vm.newUsername = '<i>Italic</i>';
+ vm.username = '<i>Italic</i>';
+ vm.newUsername = vm.username;
Vue.nextTick()
.then(() => {
diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js
index 4fba36bd4de..c1a69bd7018 100644
--- a/spec/javascripts/settings_panels_spec.js
+++ b/spec/javascripts/settings_panels_spec.js
@@ -9,11 +9,11 @@ describe('Settings Panels', () => {
describe('initSettingsPane', () => {
afterEach(() => {
- location.hash = '';
+ window.location.hash = '';
});
it('should expand linked hash fragment panel', () => {
- location.hash = '#autodevops-settings';
+ window.location.hash = '#autodevops-settings';
const pipelineSettingsPanel = document.querySelector('#autodevops-settings');
// Our test environment automatically expands everything so we need to clear that out first
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 2411d33a496..994011b262a 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -39,7 +39,8 @@ jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
beforeAll(() => jasmine.addMatchers(customMatchers));
// globalize common libraries
-window.$ = window.jQuery = $;
+window.$ = $;
+window.jQuery = window.$;
// stub expected globals
window.gl = window.gl || {};
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index 3e2fd71b5b8..efa5c878678 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -39,7 +39,8 @@ describe('MRWidgetMerged', () => {
readableClosedAt: '',
},
updatedAt: 'mergedUpdatedAt',
- shortMergeCommitSha: 'asdf1234',
+ shortMergeCommitSha: '958c0475',
+ mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed',
mergeCommitPath: 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
sourceBranch: 'bar',
targetBranch,
@@ -153,7 +154,7 @@ describe('MRWidgetMerged', () => {
it('shows button to copy commit SHA to clipboard', () => {
expect(selectors.copyMergeShaButton).toExist();
- expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.shortMergeCommitSha);
+ expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.mergeCommitSha);
});
it('shows merge commit SHA link', () => {
diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js
index af9693c48fd..98fee9a74a5 100644
--- a/spec/javascripts/vue_shared/components/expand_button_spec.js
+++ b/spec/javascripts/vue_shared/components/expand_button_spec.js
@@ -19,7 +19,7 @@ describe('expand button', () => {
});
it('renders a collpased button', () => {
- expect(vm.$el.textContent.trim()).toEqual('...');
+ expect(vm.$children[0].iconTestClass).toEqual('ic-ellipsis_h');
});
it('hides expander on click', done => {
diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
index 23be8d93b81..e4737714312 100644
--- a/spec/javascripts/vue_shared/components/gl_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -208,6 +208,14 @@ describe('GlModal', () => {
expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(true);
});
+ it('should render modal-xl', () => {
+ vm = mountComponent(modalComponent, {
+ modalSize: 'xl',
+ });
+
+ expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-xl')).toEqual(true);
+ });
+
it('should not add modal size classes when md size is passed', () => {
vm = mountComponent(modalComponent, {
modalSize: 'md',
diff --git a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
index 8224dc5a6b9..b645e49bd43 100644
--- a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
+++ b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
@@ -11,4 +11,8 @@ describe Banzai::Filter::BlockquoteFenceFilter do
expect(output).to eq(expected)
end
+
+ it 'allows trailing whitespace on blockquote fence lines' do
+ expect(filter(">>> \ntest\n>>> ")).to eq("> test")
+ end
end
diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb
index 136646bd4ee..454ad1589b9 100644
--- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb
@@ -99,7 +99,7 @@ describe Gitlab::Auth::UserAuthFinders do
context 'when the request format is empty' do
it 'the method call does not modify the original value' do
- env['action_dispatch.request.formats'] = nil
+ env.delete('action_dispatch.request.formats')
find_user_from_feed_token
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index 0d2074eed22..0dee683350f 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -114,7 +114,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
it 'does not drop the temporary tracking table after processing the batch, if there are still untracked rows' do
subject.perform(1, untracked_files_for_uploads.last.id - 1)
- expect(ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads)).to be_truthy
+ expect(ActiveRecord::Base.connection.data_source_exists?(:untracked_files_for_uploads)).to be_truthy
end
it 'drops the temporary tracking table after processing the batch, if there are no untracked rows left' do
diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb
index a65012d2314..0e0788ce974 100644
--- a/spec/lib/gitlab/checks/force_push_spec.rb
+++ b/spec/lib/gitlab/checks/force_push_spec.rb
@@ -1,21 +1,17 @@
require 'spec_helper'
describe Gitlab::Checks::ForcePush do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository.raw }
+ set(:project) { create(:project, :repository) }
- context "exit code checking", :skip_gitaly_mock do
- it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do
- allow(repository).to receive(:popen).and_return(['normal output', 0])
+ describe '.force_push?' do
+ it 'returns false if the repo is empty' do
+ allow(project).to receive(:empty_repo?).and_return(true)
- expect { described_class.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error
+ expect(described_class.force_push?(project, 'HEAD', 'HEAD~')).to be(false)
end
- it "raises a GitError error if the `popen` call to git returns a non-zero exit code" do
- allow(repository).to receive(:popen).and_return(['error', 1])
-
- expect { described_class.force_push?(project, 'oldrev', 'newrev') }
- .to raise_error(Gitlab::Git::Repository::GitError)
+ it 'checks if old rev is an anchestor' do
+ expect(described_class.force_push?(project, 'HEAD', 'HEAD~')).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index bc5a5e43103..2e204da307d 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -49,7 +49,7 @@ describe Gitlab::Ci::Config do
describe '.new' do
it 'raises error' do
expect { config }.to raise_error(
- Gitlab::Ci::Config::Loader::FormatError,
+ ::Gitlab::Ci::Config::Loader::FormatError,
/Invalid configuration format/
)
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
index c5a4d9b4778..284aed91e29 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Populate do
- set(:project) { create(:project) }
+ set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:pipeline) do
@@ -174,7 +174,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end
let(:pipeline) do
- build(:ci_pipeline, ref: 'master', config: config)
+ build(:ci_pipeline, ref: 'master', project: project, config: config)
end
it_behaves_like 'a correct pipeline'
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
index c53294d091c..a8dc5356413 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
- set(:project) { create(:project) }
+ set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:command) do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index ecb16daec96..fa5327c26f0 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab
module Ci
- describe YamlProcessor, :lib do
+ describe YamlProcessor do
subject { described_class.new(config) }
describe 'our current .gitlab-ci.yml' do
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 8ac36ae8bab..8bb246aa4bd 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -314,8 +314,13 @@ describe Gitlab::Database do
describe '.cached_table_exists?' do
it 'only retrieves data once per table' do
- expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original
- expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original
+ if Gitlab.rails5?
+ expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:projects).once.and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:bogus_table_name).once.and_call_original
+ else
+ expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original
+ end
2.times do
expect(described_class.cached_table_exists?(:projects)).to be_truthy
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
index 08c4a474217..f36111a4946 100644
--- a/spec/lib/gitlab/favicon_spec.rb
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Favicon, :request_store do
it 'uses the custom favicon if a favicon appearance is present' do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
- expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png}
+ expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/dk.png}
end
end
diff --git a/spec/lib/gitlab/file_finder_spec.rb b/spec/lib/gitlab/file_finder_spec.rb
index d6d9e4001a3..b49c5817131 100644
--- a/spec/lib/gitlab/file_finder_spec.rb
+++ b/spec/lib/gitlab/file_finder_spec.rb
@@ -3,11 +3,29 @@ require 'spec_helper'
describe Gitlab::FileFinder do
describe '#find' do
let(:project) { create(:project, :public, :repository) }
+ subject { described_class.new(project, project.default_branch) }
it_behaves_like 'file finder' do
- subject { described_class.new(project, project.default_branch) }
let(:expected_file_by_name) { 'files/images/wm.svg' }
let(:expected_file_by_content) { 'CHANGELOG' }
end
+
+ it 'filters by name' do
+ results = subject.find('files filename:wm.svg')
+
+ expect(results.count).to eq(1)
+ end
+
+ it 'filters by path' do
+ results = subject.find('white path:images')
+
+ expect(results.count).to eq(1)
+ end
+
+ it 'filters by extension' do
+ results = subject.find('files extension:svg')
+
+ expect(results.count).to eq(1)
+ end
end
end
diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
index 267056b96e6..2100690f873 100644
--- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb
+++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
@@ -1,154 +1,156 @@
require 'spec_helper'
describe Gitlab::Git::CommitterWithHooks, seed_helper: true do
- shared_examples 'calling wiki hooks' do
- let(:project) { create(:project) }
- let(:user) { project.owner }
- let(:project_wiki) { ProjectWiki.new(project, user) }
- let(:wiki) { project_wiki.wiki }
- let(:options) do
- {
- id: user.id,
- username: user.username,
- name: user.name,
- email: user.email,
- message: 'commit message'
- }
- end
-
- subject { described_class.new(wiki, options) }
+ # TODO https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'needs to be moved to gitaly-ruby test suite' do
+ shared_examples 'calling wiki hooks' do
+ let(:project) { create(:project) }
+ let(:user) { project.owner }
+ let(:project_wiki) { ProjectWiki.new(project, user) }
+ let(:wiki) { project_wiki.wiki }
+ let(:options) do
+ {
+ id: user.id,
+ username: user.username,
+ name: user.name,
+ email: user.email,
+ message: 'commit message'
+ }
+ end
- before do
- project_wiki.create_page('home', 'test content')
- end
+ subject { described_class.new(wiki, options) }
- shared_examples 'failing pre-receive hook' do
before do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update')
- expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
+ project_wiki.create_page('home', 'test content')
end
- it 'raises exception' do
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
- end
+ shared_examples 'failing pre-receive hook' do
+ before do
+ expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, ''])
+ expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update')
+ expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
+ end
- it 'does not create a new commit inside the repository' do
- current_rev = find_current_rev
+ it 'raises exception' do
+ expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
+ end
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
+ it 'does not create a new commit inside the repository' do
+ current_rev = find_current_rev
- expect(current_rev).to eq find_current_rev
- end
- end
+ expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
- shared_examples 'failing update hook' do
- before do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
+ expect(current_rev).to eq find_current_rev
+ end
end
- it 'raises exception' do
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
- end
+ shared_examples 'failing update hook' do
+ before do
+ expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
+ expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, ''])
+ expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
+ end
- it 'does not create a new commit inside the repository' do
- current_rev = find_current_rev
+ it 'raises exception' do
+ expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
+ end
- expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
+ it 'does not create a new commit inside the repository' do
+ current_rev = find_current_rev
- expect(current_rev).to eq find_current_rev
- end
- end
+ expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
- shared_examples 'failing post-receive hook' do
- before do
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, ''])
- expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, ''])
+ expect(current_rev).to eq find_current_rev
+ end
end
- it 'does not raise exception' do
- expect { subject.commit }.not_to raise_error
- end
+ shared_examples 'failing post-receive hook' do
+ before do
+ expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
+ expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, ''])
+ expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, ''])
+ end
+
+ it 'does not raise exception' do
+ expect { subject.commit }.not_to raise_error
+ end
- it 'creates the commit' do
- current_rev = find_current_rev
+ it 'creates the commit' do
+ current_rev = find_current_rev
- subject.commit
+ subject.commit
- expect(current_rev).not_to eq find_current_rev
+ expect(current_rev).not_to eq find_current_rev
+ end
end
- end
- shared_examples 'when hooks call succceeds' do
- let(:hook) { double(:hook) }
+ shared_examples 'when hooks call succceeds' do
+ let(:hook) { double(:hook) }
- it 'calls the three hooks' do
- expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
- expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil])
+ it 'calls the three hooks' do
+ expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
+ expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil])
- subject.commit
- end
+ subject.commit
+ end
- it 'creates the commit' do
- current_rev = find_current_rev
+ it 'creates the commit' do
+ current_rev = find_current_rev
- subject.commit
+ subject.commit
- expect(current_rev).not_to eq find_current_rev
+ expect(current_rev).not_to eq find_current_rev
+ end
end
- end
- context 'when creating a page' do
- before do
- project_wiki.create_page('index', 'test content')
+ context 'when creating a page' do
+ before do
+ project_wiki.create_page('index', 'test content')
+ end
+
+ it_behaves_like 'failing pre-receive hook'
+ it_behaves_like 'failing update hook'
+ it_behaves_like 'failing post-receive hook'
+ it_behaves_like 'when hooks call succceeds'
end
- it_behaves_like 'failing pre-receive hook'
- it_behaves_like 'failing update hook'
- it_behaves_like 'failing post-receive hook'
- it_behaves_like 'when hooks call succceeds'
- end
+ context 'when updating a page' do
+ before do
+ project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown)
+ end
- context 'when updating a page' do
- before do
- project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown)
+ it_behaves_like 'failing pre-receive hook'
+ it_behaves_like 'failing update hook'
+ it_behaves_like 'failing post-receive hook'
+ it_behaves_like 'when hooks call succceeds'
end
- it_behaves_like 'failing pre-receive hook'
- it_behaves_like 'failing update hook'
- it_behaves_like 'failing post-receive hook'
- it_behaves_like 'when hooks call succceeds'
- end
+ context 'when deleting a page' do
+ before do
+ project_wiki.delete_page(find_page('home'))
+ end
- context 'when deleting a page' do
- before do
- project_wiki.delete_page(find_page('home'))
+ it_behaves_like 'failing pre-receive hook'
+ it_behaves_like 'failing update hook'
+ it_behaves_like 'failing post-receive hook'
+ it_behaves_like 'when hooks call succceeds'
end
- it_behaves_like 'failing pre-receive hook'
- it_behaves_like 'failing update hook'
- it_behaves_like 'failing post-receive hook'
- it_behaves_like 'when hooks call succceeds'
- end
+ def find_current_rev
+ wiki.gollum_wiki.repo.commits.first&.sha
+ end
- def find_current_rev
- wiki.gollum_wiki.repo.commits.first&.sha
+ def find_page(name)
+ wiki.page(title: name)
+ end
end
- def find_page(name)
- wiki.page(title: name)
+ context 'when Gitaly is enabled' do
+ it_behaves_like 'calling wiki hooks'
end
- end
-
- # TODO: Uncomment once Gitaly updates the ruby vendor code
- # context 'when Gitaly is enabled' do
- # it_behaves_like 'calling wiki hooks'
- # end
- context 'when Gitaly is disabled', :skip_gitaly_mock do
- it_behaves_like 'calling wiki hooks'
+ context 'when Gitaly is disabled', :disable_gitaly do
+ it_behaves_like 'calling wiki hooks'
+ end
end
end
diff --git a/spec/lib/gitlab/git/lfs_changes_spec.rb b/spec/lib/gitlab/git/lfs_changes_spec.rb
index d0dd8c6303f..c5e7ab959b2 100644
--- a/spec/lib/gitlab/git/lfs_changes_spec.rb
+++ b/spec/lib/gitlab/git/lfs_changes_spec.rb
@@ -1,50 +1,19 @@
require 'spec_helper'
describe Gitlab::Git::LfsChanges do
- let(:project) { create(:project, :repository) }
+ set(:project) { create(:project, :repository) }
let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
let(:blob_object_id) { '0c304a93cb8430108629bbbcaa27db3343299bc0' }
subject { described_class.new(project.repository, newrev) }
describe '#new_pointers' do
- shared_examples 'new pointers' do
- it 'filters new objects to find lfs pointers' do
- expect(subject.new_pointers(not_in: []).first.id).to eq(blob_object_id)
- end
-
- it 'limits new_objects using object_limit' do
- expect(subject.new_pointers(object_limit: 1)).to eq([])
- end
- end
-
- context 'with gitaly enabled' do
- it_behaves_like 'new pointers'
+ it 'filters new objects to find lfs pointers' do
+ expect(subject.new_pointers(not_in: []).first.id).to eq(blob_object_id)
end
- context 'with gitaly disabled', :skip_gitaly_mock do
- it_behaves_like 'new pointers'
-
- it 'uses rev-list to find new objects' do
- rev_list = double
- allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list)
-
- expect(rev_list).to receive(:new_objects).and_return([])
-
- subject.new_pointers
- end
- end
- end
-
- describe '#all_pointers', :skip_gitaly_mock do
- it 'uses rev-list to find all objects' do
- rev_list = double
- allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list)
- allow(rev_list).to receive(:all_objects).and_yield([blob_object_id])
-
- expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [blob_object_id])
-
- subject.all_pointers
+ it 'limits new_objects using object_limit' do
+ expect(subject.new_pointers(object_limit: 1)).to eq([])
end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 5bae99101e6..595482f76d5 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1114,7 +1114,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#count_commits' do
- shared_examples 'extended commit counting' do
+ describe 'extended commit counting' do
context 'with after timestamp' do
it 'returns the number of commits after timestamp' do
options = { ref: 'master', after: Time.iso8601('2013-03-03T20:15:01+00:00') }
@@ -1199,14 +1199,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
end
-
- context 'when Gitaly count_commits feature is enabled' do
- it_behaves_like 'extended commit counting'
- end
-
- context 'when Gitaly count_commits feature is disabled', :disable_gitaly do
- it_behaves_like 'extended commit counting'
- end
end
describe '#autocrlf' do
@@ -1688,70 +1680,52 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#languages' do
- shared_examples 'languages' do
- it 'returns exactly the expected results' do
- languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6')
- expected_languages = [
- { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" },
- { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
- { value: 7.9, label: "HTML", color: "#e34c26", highlight: "#e34c26" },
- { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" }
- ]
+ it 'returns exactly the expected results' do
+ languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6')
+ expected_languages = [
+ { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
+ { value: 7.9, label: "HTML", color: "#e34c26", highlight: "#e34c26" },
+ { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" }
+ ]
- expect(languages.size).to eq(expected_languages.size)
+ expect(languages.size).to eq(expected_languages.size)
- expected_languages.size.times do |i|
- a = expected_languages[i]
- b = languages[i]
+ expected_languages.size.times do |i|
+ a = expected_languages[i]
+ b = languages[i]
- expect(a.keys.sort).to eq(b.keys.sort)
- expect(a[:value]).to be_within(0.1).of(b[:value])
+ expect(a.keys.sort).to eq(b.keys.sort)
+ expect(a[:value]).to be_within(0.1).of(b[:value])
- non_float_keys = a.keys - [:value]
- expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys))
- end
- end
-
- it "uses the repository's HEAD when no ref is passed" do
- lang = repository.languages.first
-
- expect(lang[:label]).to eq('Ruby')
+ non_float_keys = a.keys - [:value]
+ expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys))
end
end
- it_behaves_like 'languages'
+ it "uses the repository's HEAD when no ref is passed" do
+ lang = repository.languages.first
- context 'with rugged', :skip_gitaly_mock do
- it_behaves_like 'languages'
+ expect(lang[:label]).to eq('Ruby')
end
end
describe '#license_short_name' do
- shared_examples 'acquiring the Licensee license key' do
- subject { repository.license_short_name }
-
- context 'when no license file can be found' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository.raw_repository }
+ subject { repository.license_short_name }
- before do
- project.repository.delete_file(project.owner, 'LICENSE', message: 'remove license', branch_name: 'master')
- end
-
- it { is_expected.to be_nil }
- end
+ context 'when no license file can be found' do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository.raw_repository }
- context 'when an mit license is found' do
- it { is_expected.to eq('mit') }
+ before do
+ project.repository.delete_file(project.owner, 'LICENSE', message: 'remove license', branch_name: 'master')
end
- end
- context 'when gitaly is enabled' do
- it_behaves_like 'acquiring the Licensee license key'
+ it { is_expected.to be_nil }
end
- context 'when gitaly is disabled', :disable_gitaly do
- it_behaves_like 'acquiring the Licensee license key'
+ context 'when an mit license is found' do
+ it { is_expected.to eq('mit') }
end
end
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
index 95dc47e2a00..b752c3e8341 100644
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ b/spec/lib/gitlab/git/rev_list_spec.rb
@@ -93,14 +93,4 @@ describe Gitlab::Git::RevList do
expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2])
end
end
-
- context "#missed_ref" do
- let(:rev_list) { described_class.new(repository, oldrev: 'oldrev', newrev: 'newrev') }
-
- it 'calls out to `popen`' do
- stub_popen_rev_list('--max-count=1', 'oldrev', '^newrev', with_lazy_block: false, output: "sha1\nsha2")
-
- expect(rev_list.missed_ref).to eq(%w[sha1 sha2])
- end
- end
end
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index 35b06b14620..b63658e1b3b 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -6,9 +6,7 @@ describe Gitlab::Git::Wiki do
let(:project_wiki) { ProjectWiki.new(project, user) }
subject { project_wiki.wiki }
- # Remove skip_gitaly_mock flag when gitaly_find_page when
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/42039 is solved
- describe '#page', :skip_gitaly_mock do
+ describe '#page' do
before do
create_page('page1', 'content')
create_page('foo/page1', 'content foo/page1')
@@ -25,7 +23,7 @@ describe Gitlab::Git::Wiki do
end
end
- describe '#delete_page', :skip_gitaly_mock do
+ describe '#delete_page' do
after do
destroy_page('page1')
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 0d5f6a0b576..ff32025253a 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -934,6 +934,22 @@ describe Gitlab::GitAccess do
expect(project.repository).to receive(:clean_stale_repository_files).and_call_original
expect { push_access_check }.not_to raise_error
end
+
+ it 'avoids N+1 queries', :request_store do
+ # Run this once to establish a baseline. Cached queries should get
+ # cached, so that when we introduce another change we shouldn't see
+ # additional queries.
+ access.check('git-receive-pack', changes)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ access.check('git-receive-pack', changes)
+ end
+
+ changes = ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature']
+
+ # There is still an N+1 query with protected branches
+ expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(1)
+ end
end
end
diff --git a/spec/lib/gitlab/i18n/metadata_entry_spec.rb b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
index ab71d6454a9..a399517cc04 100644
--- a/spec/lib/gitlab/i18n/metadata_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::I18n::MetadataEntry do
- describe '#expected_plurals' do
+ describe '#expected_forms' do
it 'returns the number of plurals' do
data = {
msgid: "",
@@ -22,7 +22,7 @@ describe Gitlab::I18n::MetadataEntry do
}
entry = described_class.new(data)
- expect(entry.expected_plurals).to eq(2)
+ expect(entry.expected_forms).to eq(2)
end
it 'returns 0 for the POT-metadata' do
@@ -45,7 +45,7 @@ describe Gitlab::I18n::MetadataEntry do
}
entry = described_class.new(data)
- expect(entry.expected_plurals).to eq(0)
+ expect(entry.expected_forms).to eq(0)
end
end
end
diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb
index 3a962ba7f22..3dbc23d2aaf 100644
--- a/spec/lib/gitlab/i18n/po_linter_spec.rb
+++ b/spec/lib/gitlab/i18n/po_linter_spec.rb
@@ -1,10 +1,31 @@
require 'spec_helper'
require 'simple_po_parser'
+# Disabling this cop to allow for multi-language examples in comments
+# rubocop:disable Style/AsciiComments
describe Gitlab::I18n::PoLinter do
let(:linter) { described_class.new(po_path) }
let(:po_path) { 'spec/fixtures/valid.po' }
+ def fake_translation(msgid:, translation:, plural_id: nil, plurals: [])
+ data = { msgid: msgid, msgid_plural: plural_id }
+
+ if plural_id
+ [translation, *plurals].each_with_index do |plural, index|
+ allow(FastGettext::Translation).to receive(:n_).with(msgid, plural_id, index).and_return(plural)
+ data.merge!("msgstr[#{index}]" => plural)
+ end
+ else
+ allow(FastGettext::Translation).to receive(:_).with(msgid).and_return(translation)
+ data[:msgstr] = translation
+ end
+
+ Gitlab::I18n::TranslationEntry.new(
+ data,
+ plurals.size + 1
+ )
+ end
+
describe '#errors' do
it 'only calls validation once' do
expect(linter).to receive(:validate_po).once.and_call_original
@@ -155,9 +176,8 @@ describe Gitlab::I18n::PoLinter do
describe '#validate_entries' do
it 'keeps track of errors for entries' do
- fake_invalid_entry = Gitlab::I18n::TranslationEntry.new(
- { msgid: "Hello %{world}", msgstr: "Bonjour %{monde}" }, 2
- )
+ fake_invalid_entry = fake_translation(msgid: "Hello %{world}",
+ translation: "Bonjour %{monde}")
allow(linter).to receive(:translation_entries) { [fake_invalid_entry] }
expect(linter).to receive(:validate_entry)
@@ -177,6 +197,7 @@ describe Gitlab::I18n::PoLinter do
expect(linter).to receive(:validate_newlines).with([], fake_entry)
expect(linter).to receive(:validate_number_of_plurals).with([], fake_entry)
expect(linter).to receive(:validate_unescaped_chars).with([], fake_entry)
+ expect(linter).to receive(:validate_translation).with([], fake_entry)
linter.validate_entry(fake_entry)
end
@@ -185,7 +206,7 @@ describe Gitlab::I18n::PoLinter do
describe '#validate_number_of_plurals' do
it 'validates when there are an incorrect number of translations' do
fake_metadata = double
- allow(fake_metadata).to receive(:expected_plurals).and_return(2)
+ allow(fake_metadata).to receive(:expected_forms).and_return(2)
allow(linter).to receive(:metadata_entry).and_return(fake_metadata)
fake_entry = Gitlab::I18n::TranslationEntry.new(
@@ -201,13 +222,16 @@ describe Gitlab::I18n::PoLinter do
end
describe '#validate_variables' do
- it 'validates both signular and plural in a pluralized string when the entry has a singular' do
- pluralized_entry = Gitlab::I18n::TranslationEntry.new(
- { msgid: 'Hello %{world}',
- msgid_plural: 'Hello all %{world}',
- 'msgstr[0]' => 'Bonjour %{world}',
- 'msgstr[1]' => 'Bonjour tous %{world}' },
- 2
+ before do
+ allow(linter).to receive(:validate_variables_in_message).and_call_original
+ end
+
+ it 'validates both singular and plural in a pluralized string when the entry has a singular' do
+ pluralized_entry = fake_translation(
+ msgid: 'Hello %{world}',
+ translation: 'Bonjour %{world}',
+ plural_id: 'Hello all %{world}',
+ plurals: ['Bonjour tous %{world}']
)
expect(linter).to receive(:validate_variables_in_message)
@@ -221,11 +245,10 @@ describe Gitlab::I18n::PoLinter do
end
it 'only validates plural when there is no separate singular' do
- pluralized_entry = Gitlab::I18n::TranslationEntry.new(
- { msgid: 'Hello %{world}',
- msgid_plural: 'Hello all %{world}',
- 'msgstr[0]' => 'Bonjour %{world}' },
- 1
+ pluralized_entry = fake_translation(
+ msgid: 'Hello %{world}',
+ translation: 'Bonjour %{world}',
+ plural_id: 'Hello all %{world}'
)
expect(linter).to receive(:validate_variables_in_message)
@@ -235,37 +258,65 @@ describe Gitlab::I18n::PoLinter do
end
it 'validates the message variables' do
- entry = Gitlab::I18n::TranslationEntry.new(
- { msgid: 'Hello', msgstr: 'Bonjour' },
- 2
- )
+ entry = fake_translation(msgid: 'Hello', translation: 'Bonjour')
expect(linter).to receive(:validate_variables_in_message)
.with([], 'Hello', 'Bonjour')
linter.validate_variables([], entry)
end
+
+ it 'validates variable usage in message ids' do
+ entry = fake_translation(
+ msgid: 'Hello %{world}',
+ translation: 'Bonjour %{world}',
+ plural_id: 'Hello all %{world}',
+ plurals: ['Bonjour tous %{world}']
+ )
+
+ expect(linter).to receive(:validate_variables_in_message)
+ .with([], 'Hello %{world}', 'Hello %{world}')
+ .and_call_original
+ expect(linter).to receive(:validate_variables_in_message)
+ .with([], 'Hello all %{world}', 'Hello all %{world}')
+ .and_call_original
+
+ linter.validate_variables([], entry)
+ end
end
describe '#validate_variables_in_message' do
it 'detects when a variables are used incorrectly' do
errors = []
- expected_errors = ['<hello %{world} %d> is missing: [%{hello}]',
- '<hello %{world} %d> is using unknown variables: [%{world}]',
- 'is combining multiple unnamed variables']
+ expected_errors = ['<%d hello %{world} %s> is missing: [%{hello}]',
+ '<%d hello %{world} %s> is using unknown variables: [%{world}]',
+ 'is combining multiple unnamed variables',
+ 'is combining named variables with unnamed variables']
- linter.validate_variables_in_message(errors, '%{hello} world %d', 'hello %{world} %d')
+ linter.validate_variables_in_message(errors, '%d %{hello} world %s', '%d hello %{world} %s')
expect(errors).to include(*expected_errors)
end
+
+ it 'does not allow combining 1 `%d` unnamed variable with named variables' do
+ errors = []
+
+ linter.validate_variables_in_message(errors,
+ '%{type} detected %d vulnerability',
+ '%{type} detecteerde %d kwetsbaarheid')
+
+ expect(errors).not_to be_empty
+ end
end
describe '#validate_translation' do
+ let(:entry) { fake_translation(msgid: 'Hello %{world}', translation: 'Bonjour %{world}') }
+
it 'succeeds with valid variables' do
errors = []
- linter.validate_translation(errors, 'Hello %{world}', ['%{world}'])
+ linter.validate_translation(errors, entry)
expect(errors).to be_empty
end
@@ -275,43 +326,80 @@ describe Gitlab::I18n::PoLinter do
expect(FastGettext::Translation).to receive(:_) { raise 'broken' }
- linter.validate_translation(errors, 'Hello', [])
+ linter.validate_translation(errors, entry)
- expect(errors).to include('Failure translating to en with []: broken')
+ expect(errors).to include('Failure translating to en: broken')
end
it 'adds an error message when translating fails when translating with context' do
+ entry = fake_translation(msgid: 'Tests|Hello', translation: 'broken')
errors = []
expect(FastGettext::Translation).to receive(:s_) { raise 'broken' }
- linter.validate_translation(errors, 'Tests|Hello', [])
+ linter.validate_translation(errors, entry)
- expect(errors).to include('Failure translating to en with []: broken')
+ expect(errors).to include('Failure translating to en: broken')
end
it "adds an error when trying to translate with incorrect variables when using unnamed variables" do
+ entry = fake_translation(msgid: 'Hello %s', translation: 'Hello %d')
errors = []
- linter.validate_translation(errors, 'Hello %d', ['%s'])
+ linter.validate_translation(errors, entry)
- expect(errors.first).to start_with("Failure translating to en with")
+ expect(errors.first).to start_with("Failure translating to en")
end
it "adds an error when trying to translate with named variables when unnamed variables are expected" do
+ entry = fake_translation(msgid: 'Hello %s', translation: 'Hello %{thing}')
errors = []
- linter.validate_translation(errors, 'Hello %d', ['%{world}'])
+ linter.validate_translation(errors, entry)
- expect(errors.first).to start_with("Failure translating to en with")
+ expect(errors.first).to start_with("Failure translating to en")
end
- it 'adds an error when translated with incorrect variables using named variables' do
- errors = []
+ it 'tests translation for all given forms' do
+ # Fake a language that has 3 forms to translate
+ fake_metadata = double
+ allow(fake_metadata).to receive(:forms_to_test).and_return(3)
+ allow(linter).to receive(:metadata_entry).and_return(fake_metadata)
+ entry = fake_translation(
+ msgid: '%d exception',
+ translation: '%d uitzondering',
+ plural_id: '%d exceptions',
+ plurals: ['%d uitzonderingen', '%d uitzonderingetjes']
+ )
+
+ # Make each count use a different index
+ allow(linter).to receive(:index_for_pluralization).with(0).and_return(0)
+ allow(linter).to receive(:index_for_pluralization).with(1).and_return(1)
+ allow(linter).to receive(:index_for_pluralization).with(2).and_return(2)
+
+ expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 0).and_call_original
+ expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 1).and_call_original
+ expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 2).and_call_original
+
+ linter.validate_translation([], entry)
+ end
+ end
+
+ describe '#numbers_covering_all_plurals' do
+ it 'can correctly find all required numbers to translate to Polish' do
+ # Polish used as an example with 3 different forms:
+ # 0, all plurals except the ones ending in 2,3,4: Kotów
+ # 1: Kot
+ # 2-3-4: Koty
+ # So translating with [0, 1, 2] will give us all different posibilities
+ fake_metadata = double
+ allow(fake_metadata).to receive(:forms_to_test).and_return(4)
+ allow(linter).to receive(:metadata_entry).and_return(fake_metadata)
+ allow(linter).to receive(:locale).and_return('pl_PL')
- linter.validate_translation(errors, 'Hello %{thing}', ['%d'])
+ numbers = linter.numbers_covering_all_plurals
- expect(errors.first).to start_with("Failure translating to en with")
+ expect(numbers).to contain_exactly(0, 1, 2)
end
end
@@ -336,3 +424,4 @@ describe Gitlab::I18n::PoLinter do
end
end
end
+# rubocop:enable Style/AsciiComments
diff --git a/spec/lib/gitlab/i18n/translation_entry_spec.rb b/spec/lib/gitlab/i18n/translation_entry_spec.rb
index f68bc8feff9..b301e6ea443 100644
--- a/spec/lib/gitlab/i18n/translation_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/translation_entry_spec.rb
@@ -109,7 +109,7 @@ describe Gitlab::I18n::TranslationEntry do
data = { msgid: %w(hello world) }
entry = described_class.new(data, 2)
- expect(entry.msgid_contains_newlines?).to be_truthy
+ expect(entry.msgid_has_multiple_lines?).to be_truthy
end
end
@@ -118,7 +118,7 @@ describe Gitlab::I18n::TranslationEntry do
data = { msgid_plural: %w(hello world) }
entry = described_class.new(data, 2)
- expect(entry.plural_id_contains_newlines?).to be_truthy
+ expect(entry.plural_id_has_multiple_lines?).to be_truthy
end
end
@@ -127,7 +127,7 @@ describe Gitlab::I18n::TranslationEntry do
data = { msgstr: %w(hello world) }
entry = described_class.new(data, 2)
- expect(entry.translations_contain_newlines?).to be_truthy
+ expect(entry.translations_have_multiple_lines?).to be_truthy
end
end
diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb
index 548eb28fe4d..4059188fba1 100644
--- a/spec/lib/gitlab/profiler_spec.rb
+++ b/spec/lib/gitlab/profiler_spec.rb
@@ -135,6 +135,51 @@ describe Gitlab::Profiler do
end
end
+ describe '.clean_backtrace' do
+ it 'uses the Rails backtrace cleaner' do
+ backtrace = []
+
+ expect(Rails.backtrace_cleaner).to receive(:clean).with(backtrace)
+
+ described_class.clean_backtrace(backtrace)
+ end
+
+ it 'removes lines from IGNORE_BACKTRACES' do
+ backtrace = [
+ "lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'",
+ "lib/gitlab/gitaly_client.rb:331:in `allow_n_plus_1_calls'",
+ "lib/gitlab/gitaly_client.rb:280:in `block in migrate'",
+ "lib/gitlab/metrics/influx_db.rb:103:in `measure'",
+ "lib/gitlab/gitaly_client.rb:278:in `migrate'",
+ "lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'",
+ "lib/gitlab/git/commit.rb:66:in `find'",
+ "app/models/repository.rb:1047:in `find_commit'",
+ "lib/gitlab/metrics/instrumentation.rb:159:in `block in find_commit'",
+ "lib/gitlab/metrics/method_call.rb:36:in `measure'",
+ "lib/gitlab/metrics/instrumentation.rb:159:in `find_commit'",
+ "app/models/repository.rb:113:in `commit'",
+ "lib/gitlab/i18n.rb:50:in `with_locale'",
+ "lib/gitlab/middleware/multipart.rb:95:in `call'",
+ "lib/gitlab/request_profiler/middleware.rb:14:in `call'",
+ "ee/lib/gitlab/database/load_balancing/rack_middleware.rb:37:in `call'",
+ "ee/lib/gitlab/jira/middleware.rb:15:in `call'"
+ ]
+
+ expect(described_class.clean_backtrace(backtrace))
+ .to eq([
+ "lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'",
+ "lib/gitlab/gitaly_client.rb:331:in `allow_n_plus_1_calls'",
+ "lib/gitlab/gitaly_client.rb:280:in `block in migrate'",
+ "lib/gitlab/gitaly_client.rb:278:in `migrate'",
+ "lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'",
+ "lib/gitlab/git/commit.rb:66:in `find'",
+ "app/models/repository.rb:1047:in `find_commit'",
+ "app/models/repository.rb:113:in `commit'",
+ "ee/lib/gitlab/jira/middleware.rb:15:in `call'"
+ ])
+ end
+ end
+
describe '.with_custom_logger' do
context 'when the logger is set' do
it 'uses the replacement logger for the duration of the block' do
diff --git a/spec/lib/gitlab/search/query_spec.rb b/spec/lib/gitlab/search/query_spec.rb
new file mode 100644
index 00000000000..2d00428fffa
--- /dev/null
+++ b/spec/lib/gitlab/search/query_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe Gitlab::Search::Query do
+ let(:query) { 'base filter:wow anotherfilter:noway name:maybe other:mmm leftover' }
+ let(:subject) do
+ described_class.new(query) do
+ filter :filter
+ filter :name, parser: :upcase.to_proc
+ filter :other
+ end
+ end
+
+ it { expect(described_class).to be < SimpleDelegator }
+
+ it 'leaves undefined filters in the main query' do
+ expect(subject.term).to eq('base anotherfilter:noway leftover')
+ end
+
+ it 'parses filters' do
+ expect(subject.filters.count).to eq(3)
+ expect(subject.filters.map { |f| f[:value] }).to match_array(%w[wow MAYBE mmm])
+ end
+
+ context 'with an empty filter' do
+ let(:query) { 'some bar name: baz' }
+
+ it 'ignores empty filters' do
+ expect(subject.term).to eq('some bar name: baz')
+ end
+ end
+
+ context 'with a pipe' do
+ let(:query) { 'base | nofilter' }
+
+ it 'does not escape the pipe' do
+ expect(subject.term).to eq(query)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 155e1663298..c435f988cdd 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -498,34 +498,18 @@ describe Gitlab::Shell do
)
end
- context 'with gitaly' do
- it 'returns true when the command succeeds' do
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository)
- .with(repository.raw_repository) { :gitaly_response_object }
-
- is_expected.to be_truthy
- end
-
- it 'return false when the command fails' do
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository)
- .with(repository.raw_repository) { raise GRPC::BadStatus, 'bla' }
+ it 'returns true when the command succeeds' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository)
+ .with(repository.raw_repository) { :gitaly_response_object }
- is_expected.to be_falsy
- end
+ is_expected.to be_truthy
end
- context 'without gitaly', :disable_gitaly do
- it 'returns true when the command succeeds' do
- expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { true }
-
- is_expected.to be_truthy
- end
-
- it 'return false when the command fails' do
- expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { false }
+ it 'return false when the command fails' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository)
+ .with(repository.raw_repository) { raise GRPC::BadStatus, 'bla' }
- is_expected.to be_falsy
- end
+ is_expected.to be_falsy
end
end
@@ -665,7 +649,7 @@ describe Gitlab::Shell do
subject do
gitlab_shell.fetch_remote(repository.raw_repository, remote_name,
- forced: true, no_tags: true, ssh_auth: ssh_auth)
+ forced: true, no_tags: true, ssh_auth: ssh_auth)
end
it 'passes the correct params to the gitaly service' do
diff --git a/spec/migrations/remove_soft_removed_objects_spec.rb b/spec/migrations/remove_soft_removed_objects_spec.rb
index fb70c284f5e..d0bde98b80e 100644
--- a/spec/migrations/remove_soft_removed_objects_spec.rb
+++ b/spec/migrations/remove_soft_removed_objects_spec.rb
@@ -3,6 +3,18 @@ require Rails.root.join('db', 'post_migrate', '20171207150343_remove_soft_remove
describe RemoveSoftRemovedObjects, :migration do
describe '#up' do
+ let!(:groups) do
+ table(:namespaces).tap do |t|
+ t.inheritance_column = nil
+ end
+ end
+
+ let!(:routes) do
+ table(:routes).tap do |t|
+ t.inheritance_column = nil
+ end
+ end
+
it 'removes various soft removed objects' do
5.times do
create_with_deleted_at(:issue)
@@ -28,19 +40,20 @@ describe RemoveSoftRemovedObjects, :migration do
it 'removes routes of soft removed personal namespaces' do
namespace = create_with_deleted_at(:namespace)
- group = create(:group) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ group = groups.create!(name: 'group', path: 'group_path', type: 'Group')
+ routes.create!(source_id: group.id, source_type: 'Group', name: 'group', path: 'group_path')
- expect(Route.where(source: namespace).exists?).to eq(true)
- expect(Route.where(source: group).exists?).to eq(true)
+ expect(routes.where(source_id: namespace.id).exists?).to eq(true)
+ expect(routes.where(source_id: group.id).exists?).to eq(true)
run_migration
- expect(Route.where(source: namespace).exists?).to eq(false)
- expect(Route.where(source: group).exists?).to eq(true)
+ expect(routes.where(source_id: namespace.id).exists?).to eq(false)
+ expect(routes.where(source_id: group.id).exists?).to eq(true)
end
it 'schedules the removal of soft removed groups' do
- group = create_with_deleted_at(:group)
+ group = create_deleted_group
admin = create(:user, admin: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs
expect_any_instance_of(GroupDestroyWorker)
@@ -51,7 +64,7 @@ describe RemoveSoftRemovedObjects, :migration do
end
it 'does not remove soft removed groups when no admin user could be found' do
- create_with_deleted_at(:group)
+ create_deleted_group
expect_any_instance_of(GroupDestroyWorker)
.not_to receive(:perform)
@@ -74,4 +87,13 @@ describe RemoveSoftRemovedObjects, :migration do
row
end
+
+ def create_deleted_group
+ group = groups.create!(name: 'group', path: 'group_path', type: 'Group')
+ routes.create!(source_id: group.id, source_type: 'Group', name: 'group', path: 'group_path')
+
+ groups.where(id: group.id).update_all(deleted_at: 1.year.ago)
+
+ group
+ end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index cbcf1e55979..b5a6d959ccb 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -54,14 +54,6 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it { is_expected.to eq('Sample data in db') }
end
-
- context 'when data_store is others' do
- before do
- build_trace_chunk.send(:write_attribute, :data_store, -1)
- end
-
- it { expect { subject }.to raise_error('Unsupported data store') }
- end
end
describe '#set_data' do
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index b9f1c7dd5df..6c637533c6b 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -478,7 +478,7 @@ describe JiraService do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
- expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$}
+ expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/dk.png$}
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index bc9cce6b0c3..a2f8fac2f38 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2339,6 +2339,22 @@ describe Project do
end
end
+ describe '#any_lfs_file_locks?', :request_store do
+ set(:project) { create(:project) }
+
+ it 'returns false when there are no LFS file locks' do
+ expect(project.any_lfs_file_locks?).to be_falsey
+ end
+
+ it 'returns a cached true when there are LFS file locks' do
+ create(:lfs_file_lock, project: project)
+
+ expect(project.lfs_file_locks).to receive(:any?).once.and_call_original
+
+ 2.times { expect(project.any_lfs_file_locks?).to be_truthy }
+ end
+ end
+
describe '#protected_for?' do
let(:project) { create(:project) }
diff --git a/spec/requests/api/graphql/merge_request_query_spec.rb b/spec/requests/api/graphql/merge_request_query_spec.rb
deleted file mode 100644
index 12b1d5d18a2..00000000000
--- a/spec/requests/api/graphql/merge_request_query_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'spec_helper'
-
-describe 'getting merge request information' do
- include GraphqlHelpers
-
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, source_project: project) }
- let(:current_user) { create(:user) }
-
- let(:query) do
- attributes = {
- 'fullPath' => merge_request.project.full_path,
- 'iid' => merge_request.iid
- }
- graphql_query_for('mergeRequest', attributes)
- end
-
- context 'when the user has access to the merge request' do
- before do
- project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
- end
-
- it 'returns the merge request' do
- expect(graphql_data['mergeRequest']).not_to be_nil
- end
-
- # This is a field coming from the `MergeRequestPresenter`
- it 'includes a web_url' do
- expect(graphql_data['mergeRequest']['webUrl']).to be_present
- end
-
- it_behaves_like 'a working graphql query'
- end
-
- context 'when the user does not have access to the merge request' do
- before do
- post_graphql(query, current_user: current_user)
- end
-
- it 'returns an empty field' do
- post_graphql(query, current_user: current_user)
-
- expect(graphql_data['mergeRequest']).to be_nil
- end
-
- it_behaves_like 'a working graphql query'
- end
-end
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index 8196bcfa87c..796ffc9d569 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -13,27 +13,76 @@ describe 'getting project information' do
context 'when the user has access to the project' do
before do
project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
end
it 'includes the project' do
+ post_graphql(query, current_user: current_user)
+
expect(graphql_data['project']).not_to be_nil
end
- it_behaves_like 'a working graphql query'
- end
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
- context 'when the user does not have access to the project' do
- before do
- post_graphql(query, current_user: current_user)
+ context 'when requesting a nested merge request' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:merge_request_graphql_data) { graphql_data['project']['mergeRequest'] }
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('mergeRequest', iid: merge_request.iid)
+ )
+ end
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
+
+ it 'contains merge request information' do
+ post_graphql(query, current_user: current_user)
+
+ expect(merge_request_graphql_data).not_to be_nil
+ end
+
+ # This is a field coming from the `MergeRequestPresenter`
+ it 'includes a web_url' do
+ post_graphql(query, current_user: current_user)
+
+ expect(merge_request_graphql_data['webUrl']).to be_present
+ end
+
+ context 'when the user does not have access to the merge request' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it 'returns nil' do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+
+ post_graphql(query)
+
+ expect(merge_request_graphql_data).to be_nil
+ end
+ end
end
+ end
+ context 'when the user does not have access to the project' do
it 'returns an empty field' do
post_graphql(query, current_user: current_user)
expect(graphql_data['project']).to be_nil
end
- it_behaves_like 'a working graphql query'
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 7d923932309..da23fdd7dca 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -138,10 +138,15 @@ describe API::Groups do
context "when using sorting" do
let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") }
+ let(:group4) { create(:group, name: "same-name", path: "y#{group1.path}") }
+ let(:group5) { create(:group, name: "same-name") }
let(:response_groups) { json_response.map { |group| group['name'] } }
+ let(:response_groups_ids) { json_response.map { |group| group['id'] } }
before do
group3.add_owner(user1)
+ group4.add_owner(user1)
+ group5.add_owner(user1)
end
it "sorts by name ascending by default" do
@@ -150,7 +155,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq([group3.name, group1.name])
+ expect(response_groups).to eq(Group.visible_to_user(user1).order(:name).pluck(:name))
end
it "sorts in descending order when passed" do
@@ -159,16 +164,52 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq([group1.name, group3.name])
+ expect(response_groups).to eq(Group.visible_to_user(user1).order(name: :desc).pluck(:name))
end
- it "sorts by the order_by param" do
+ it "sorts by path in order_by param" do
get api("/groups", user1), order_by: "path"
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(response_groups).to eq([group1.name, group3.name])
+ expect(response_groups).to eq(Group.visible_to_user(user1).order(:path).pluck(:name))
+ end
+
+ it "sorts by id in the order_by param" do
+ get api("/groups", user1), order_by: "id"
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq(Group.visible_to_user(user1).order(:id).pluck(:name))
+ end
+
+ it "sorts also by descending id with pagination fix" do
+ get api("/groups", user1), order_by: "id", sort: "desc"
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq(Group.visible_to_user(user1).order(id: :desc).pluck(:name))
+ end
+
+ it "sorts identical keys by id for good pagination" do
+ get api("/groups", user1), search: "same-name", order_by: "name"
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort)
+ end
+
+ it "sorts descending identical keys by id for good pagination" do
+ get api("/groups", user1), search: "same-name", order_by: "name", sort: "desc"
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort)
end
end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 16e6f19773f..e7639599874 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -351,11 +351,13 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
context 'when valid token is provided' do
context 'when Runner is not active' do
let(:runner) { create(:ci_runner, :inactive) }
+ let(:update_value) { runner.ensure_runner_queue_value }
it 'returns 204 error' do
request_job
- expect(response).to have_gitlab_http_status 204
+ expect(response).to have_gitlab_http_status(204)
+ expect(response.header['X-GitLab-Last-Update']).to eq(update_value)
end
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index aca4aa40027..f8e468be170 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -312,6 +312,30 @@ describe API::Search do
end
it_behaves_like 'response is correct', schema: 'public_api/v4/blobs', size: 2
+
+ context 'filters' do
+ it 'by filename' do
+ get api("/projects/#{repo_project.id}/search", user), scope: 'blobs', search: 'mon filename:PROCESS.md'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(2)
+ expect(json_response.first['filename']).to eq('PROCESS.md')
+ end
+
+ it 'by path' do
+ get api("/projects/#{repo_project.id}/search", user), scope: 'blobs', search: 'mon path:markdown'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(8)
+ end
+
+ it 'by extension' do
+ get api("/projects/#{repo_project.id}/search", user), scope: 'blobs', search: 'mon extension:md'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(11)
+ end
+ end
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8417b340de5..dac609e2545 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -87,6 +87,7 @@ RSpec.configure do |config|
config.include LiveDebugger, :js
config.include MigrationsHelpers, :migration
config.include RedisHelpers
+ config.include Rails.application.routes.url_helpers, type: :routing
if ENV['CI']
# This includes the first try, i.e. tests will be run 4 times before failing.
diff --git a/spec/support/helpers/features/notes_helpers.rb b/spec/support/helpers/features/notes_helpers.rb
index 1a1d5853a7a..2b9f8b30c60 100644
--- a/spec/support/helpers/features/notes_helpers.rb
+++ b/spec/support/helpers/features/notes_helpers.rb
@@ -13,7 +13,7 @@ module Spec
module Features
module NotesHelpers
def add_note(text)
- Sidekiq::Testing.fake! do
+ perform_enqueued_jobs do
page.within(".js-main-target-form") do
fill_in("note[note]", with: text)
find(".js-comment-submit-button").click
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 30ff9a1196a..0930b9da368 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -34,14 +34,20 @@ module GraphqlHelpers
end
def graphql_query_for(name, attributes = {}, fields = nil)
+ <<~QUERY
+ {
+ #{query_graphql_field(name, attributes, fields)}
+ }
+ QUERY
+ end
+
+ def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes)
<<~QUERY
- {
#{name}(#{attributes}) {
#{fields}
}
- }
QUERY
end
@@ -50,12 +56,15 @@ module GraphqlHelpers
return "" unless type
type.fields.map do |name, field|
+ # We can't guess arguments, so skip fields that require them
+ next if field.arguments.any?
+
if scalar?(field)
name
else
"#{name} { #{all_graphql_fields_for(field_type(field))} }"
end
- end.join("\n")
+ end.compact.join("\n")
end
def attributes_to_graphql(attributes)
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 84abec75c26..0bc235701eb 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -10,10 +10,6 @@ module MigrationsHelpers
ActiveRecord::Migrator.migrations_paths
end
- def table_exists?(name)
- ActiveRecord::Base.connection.table_exists?(name)
- end
-
def migrations
ActiveRecord::Migrator.migrations(migrations_paths)
end
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index ba7a1c8cde0..d23cbaf4beb 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -13,6 +13,12 @@ RSpec::Matchers.define :have_graphql_fields do |*expected|
end
end
+RSpec::Matchers.define :have_graphql_field do |field_name|
+ match do |kls|
+ expect(kls.fields.keys).to include(GraphqlHelpers.fieldnamerize(field_name))
+ end
+end
+
RSpec::Matchers.define :have_graphql_arguments do |*expected|
include GraphqlHelpers
diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index 2228e872926..7c34c7b4977 100644
--- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
@@ -245,6 +245,70 @@ RSpec.shared_examples 'slack or mattermost notifications' do
end
end
+ describe 'Push events' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository, creator: user) }
+
+ before do
+ allow(chat_service).to receive_messages(
+ project: project,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ context 'only notify for the default branch' do
+ context 'when enabled' do
+ before do
+ chat_service.notify_only_default_branch = true
+ end
+
+ it 'does not notify push events if they are not for the default branch' do
+ ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
+ push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
+
+ chat_service.execute(push_sample_data)
+
+ expect(WebMock).not_to have_requested(:post, webhook_url)
+ end
+
+ it 'notifies about push events for the default branch' do
+ push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
+
+ chat_service.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it 'still notifies about pushed tags' do
+ ref = "#{Gitlab::Git::TAG_REF_PREFIX}test"
+ push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
+
+ chat_service.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+ end
+
+ context 'when disabled' do
+ before do
+ chat_service.notify_only_default_branch = false
+ end
+
+ it 'notifies about all push events' do
+ ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
+ push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
+
+ chat_service.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+ end
+ end
+ end
+
describe "Note events" do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, creator: user) }
@@ -394,23 +458,6 @@ RSpec.shared_examples 'slack or mattermost notifications' do
expect(result).to be_falsy
end
-
- it 'does not notify push events if they are not for the default branch' do
- ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
- push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
-
- chat_service.execute(push_sample_data)
-
- expect(WebMock).not_to have_requested(:post, webhook_url)
- end
-
- it 'notifies about push events for the default branch' do
- push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
-
- chat_service.execute(push_sample_data)
-
- expect(WebMock).to have_requested(:post, webhook_url).once
- end
end
context 'when disabled' do
diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb
deleted file mode 100644
index 37deea8ab90..00000000000
--- a/spec/uploaders/favicon_uploader_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe FaviconUploader do
- include CarrierWave::Test::Matchers
-
- let(:uploader) { described_class.new(build_stubbed(:user)) }
-
- after do
- uploader.remove!
- end
-
- def upload_fixture(filename)
- fixture_file_upload("spec/fixtures/#{filename}")
- end
-
- context 'versions' do
- before do
- uploader.store!(upload_fixture('dk.png'))
- end
-
- it 'has the correct format' do
- expect(uploader.favicon_main).to be_format('png')
- end
-
- it 'has the correct dimensions' do
- expect(uploader.favicon_main).to have_dimensions(32, 32)
- end
- end
-end
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 9e3b99b3502..2106959e23c 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -13,7 +13,7 @@ describe 'Every Sidekiq worker' do
file_worker_queues = Gitlab::SidekiqConfig.worker_queues.to_set
worker_queues = Gitlab::SidekiqConfig.workers.map(&:queue).to_set
- worker_queues << ActionMailer::DeliveryJob.queue_name
+ worker_queues << ActionMailer::DeliveryJob.new.queue_name
worker_queues << 'default'
missing_from_file = worker_queues - file_worker_queues
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 5d83397e8df..ac8716ecfb1 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -92,16 +92,6 @@ describe RepositoryForkWorker do
end
it_behaves_like 'RepositoryForkWorker performing'
-
- it 'logs a message about forking with old-style arguments' do
- allow(subject).to receive(:gitlab_shell).and_return(shell)
- expect(shell).to receive(:fork_repository) { true }
-
- allow(Rails.logger).to receive(:info).with(anything) # To compensate for other logs
- expect(Rails.logger).to receive(:info).with("Project #{fork_project.id} is being forked using old-style arguments.")
-
- perform!
- end
end
end
end
diff --git a/yarn.lock b/yarn.lock
index 65d78173a5b..cefd7c9a62e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -270,11 +270,7 @@ acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
-acorn@^5.0.0, acorn@^5.3.0:
- version "5.5.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
-
-acorn@^5.5.0:
+acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
version "5.6.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
@@ -4002,14 +3998,10 @@ icss-utils@^2.1.0:
dependencies:
postcss "^6.0.1"
-ieee754@^1.1.11:
+ieee754@^1.1.11, ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
-ieee754@^1.1.4:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
-
iferr@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
@@ -6247,11 +6239,7 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
-prettier@1.11.1:
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
-
-prettier@^1.11.1:
+prettier@1.12.1, prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"