Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc3
-rw-r--r--.haml-lint.yml8
-rw-r--r--CHANGELOG.md24
-rw-r--r--CONTRIBUTING.md36
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock11
-rw-r--r--app/assets/javascripts/copy_as_gfm.js.es6355
-rw-r--r--app/assets/javascripts/diff.js.es66
-rw-r--r--app/assets/javascripts/dispatcher.js.es63
-rw-r--r--app/assets/javascripts/droplab/droplab.js67
-rw-r--r--app/assets/javascripts/droplab/droplab_ajax.js45
-rw-r--r--app/assets/javascripts/droplab/droplab_ajax_filter.js62
-rw-r--r--app/assets/javascripts/droplab/droplab_filter.js18
-rw-r--r--app/assets/javascripts/environments/environments_bundle.js.es61
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js.es62
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js.es619
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es615
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es611
-rw-r--r--app/assets/javascripts/gl_dropdown.js14
-rw-r--r--app/assets/javascripts/issues_bulk_assignment.js.es62
-rw-r--r--app/assets/javascripts/labels_select.js6
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js.es670
-rw-r--r--app/assets/javascripts/line_highlighter.js5
-rw-r--r--app/assets/javascripts/notes.js21
-rw-r--r--app/assets/javascripts/profile/profile.js.es61
-rw-r--r--app/assets/javascripts/search_autocomplete.js.es67
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js54
-rw-r--r--app/assets/javascripts/version_check_image.js.es610
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es647
-rw-r--r--app/assets/javascripts/vue_pipelines_index/stage.js.es67
-rw-r--r--app/assets/javascripts/vue_pipelines_index/store.js.es618
-rw-r--r--app/assets/stylesheets/framework/avatar.scss2
-rw-r--r--app/assets/stylesheets/framework/blocks.scss12
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss3
-rw-r--r--app/assets/stylesheets/framework/filters.scss29
-rw-r--r--app/assets/stylesheets/framework/lists.scss3
-rw-r--r--app/assets/stylesheets/highlight/dark.scss13
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss12
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss12
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss15
-rw-r--r--app/assets/stylesheets/highlight/white.scss16
-rw-r--r--app/assets/stylesheets/pages/issuable.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss26
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss12
-rw-r--r--app/assets/stylesheets/pages/projects.scss26
-rw-r--r--app/assets/stylesheets/pages/tree.scss4
-rw-r--r--app/controllers/admin/application_settings_controller.rb6
-rw-r--r--app/controllers/autocomplete_controller.rb5
-rw-r--r--app/controllers/explore/projects_controller.rb1
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/git_http_client_controller.rb12
-rw-r--r--app/controllers/projects/git_http_controller.rb6
-rw-r--r--app/controllers/projects/refs_controller.rb6
-rw-r--r--app/helpers/compare_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb2
-rw-r--r--app/helpers/version_check_helper.rb3
-rw-r--r--app/mailers/emails/notes.rb8
-rw-r--r--app/models/ability.rb11
-rw-r--r--app/models/application_setting.rb84
-rw-r--r--app/models/concerns/cache_markdown_field.rb7
-rw-r--r--app/models/concerns/mentionable.rb10
-rw-r--r--app/models/concerns/participable.rb7
-rw-r--r--app/models/concerns/routable.rb15
-rw-r--r--app/models/concerns/taskable.rb8
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/member.rb20
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/namespace.rb27
-rw-r--r--app/models/note.rb19
-rw-r--r--app/models/project.rb11
-rw-r--r--app/models/project_group_link.rb3
-rw-r--r--app/models/repository.rb13
-rw-r--r--app/models/route.rb7
-rw-r--r--app/models/user.rb13
-rw-r--r--app/presenters/README.md6
-rw-r--r--app/services/application_settings/base_service.rb7
-rw-r--r--app/services/application_settings/update_service.rb7
-rw-r--r--app/services/ci/register_build_service.rb62
-rw-r--r--app/services/notes/create_service.rb7
-rw-r--r--app/services/notes/post_process_service.rb3
-rw-r--r--app/services/notes/slash_commands_service.rb2
-rw-r--r--app/services/notification_service.rb22
-rw-r--r--app/services/projects/create_service.rb26
-rw-r--r--app/services/projects/update_service.rb2
-rw-r--r--app/services/user_project_access_changed_service.rb2
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb3
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml2
-rw-r--r--app/views/admin/identities/_identity.html.haml2
-rw-r--r--app/views/admin/runners/index.html.haml2
-rw-r--r--app/views/admin/runners/show.html.haml2
-rw-r--r--app/views/admin/system_info/show.html.haml10
-rw-r--r--app/views/admin/users/show.html.haml2
-rw-r--r--app/views/ci/lints/show.html.haml2
-rw-r--r--app/views/ci/status/_badge.html.haml3
-rw-r--r--app/views/discussions/_discussion.html.haml3
-rw-r--r--app/views/discussions/_parallel_diff_discussion.html.haml4
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml2
-rw-r--r--app/views/groups/group_members/index.html.haml2
-rw-r--r--app/views/groups/issues.html.haml2
-rw-r--r--app/views/groups/merge_requests.html.haml2
-rw-r--r--app/views/groups/milestones/index.html.haml2
-rw-r--r--app/views/import/_githubish_status.html.haml2
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml2
-rw-r--r--app/views/import/fogbugz/status.html.haml2
-rw-r--r--app/views/import/google_code/status.html.haml2
-rw-r--r--app/views/notify/_reassigned_issuable_email.html.haml4
-rw-r--r--app/views/notify/closed_issue_email.html.haml2
-rw-r--r--app/views/notify/closed_issue_email.text.haml2
-rw-r--r--app/views/notify/closed_merge_request_email.html.haml2
-rw-r--r--app/views/notify/closed_merge_request_email.text.haml2
-rw-r--r--app/views/notify/issue_status_changed_email.html.haml2
-rw-r--r--app/views/notify/merge_request_status_email.html.haml2
-rw-r--r--app/views/notify/merge_request_status_email.text.haml2
-rw-r--r--app/views/notify/merged_merge_request_email.html.haml2
-rw-r--r--app/views/notify/merged_merge_request_email.text.haml2
-rw-r--r--app/views/notify/note_personal_snippet_email.html.haml1
-rw-r--r--app/views/notify/note_personal_snippet_email.text.erb8
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml2
-rw-r--r--app/views/notify/pipeline_success_email.html.haml4
-rw-r--r--app/views/notify/project_was_not_exported_email.text.haml4
-rw-r--r--app/views/notify/repository_push_email.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml4
-rw-r--r--app/views/profiles/show.html.haml2
-rw-r--r--app/views/projects/_md_preview.html.haml2
-rw-r--r--app/views/projects/_merge_request_merge_settings.html.haml2
-rw-r--r--app/views/projects/blob/_editor.html.haml2
-rw-r--r--app/views/projects/blob/_image.html.haml4
-rw-r--r--app/views/projects/blob/_upload.html.haml2
-rw-r--r--app/views/projects/branches/_branch.html.haml2
-rw-r--r--app/views/projects/branches/index.html.haml2
-rw-r--r--app/views/projects/builds/_header.html.haml4
-rw-r--r--app/views/projects/buttons/_download.html.haml2
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml8
-rw-r--r--app/views/projects/commit/_commit_box.html.haml3
-rw-r--r--app/views/projects/commits/_commits.html.haml2
-rw-r--r--app/views/projects/commits/show.html.haml7
-rw-r--r--app/views/projects/compare/show.html.haml4
-rw-r--r--app/views/projects/deployments/_deployment.html.haml4
-rw-r--r--app/views/projects/diffs/_content.html.haml2
-rw-r--r--app/views/projects/diffs/_file_header.html.haml2
-rw-r--r--app/views/projects/diffs/_image.html.haml6
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml4
-rw-r--r--app/views/projects/diffs/_stats.html.haml2
-rw-r--r--app/views/projects/edit.html.haml15
-rw-r--r--app/views/projects/forks/index.html.haml2
-rw-r--r--app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml2
-rw-r--r--app/views/projects/graphs/commits.html.haml10
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml6
-rw-r--r--app/views/projects/merge_requests/_show.html.haml9
-rw-r--r--app/views/projects/merge_requests/show/_versions.html.haml8
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml4
-rw-r--r--app/views/projects/notes/_note.html.haml2
-rw-r--r--app/views/projects/project_members/_group_members.html.haml2
-rw-r--r--app/views/projects/project_members/_groups.html.haml2
-rw-r--r--app/views/projects/project_members/_shared_group_members.html.haml4
-rw-r--r--app/views/projects/project_members/_team.html.haml2
-rw-r--r--app/views/projects/releases/edit.html.haml2
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml4
-rw-r--r--app/views/projects/services/_form.html.haml2
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml2
-rw-r--r--app/views/projects/tree/_tree_content.html.haml4
-rw-r--r--app/views/projects/wikis/git_access.html.haml7
-rw-r--r--app/views/search/results/_empty.html.haml2
-rw-r--r--app/views/search/results/_merge_request.html.haml2
-rw-r--r--app/views/search/results/_snippet_blob.html.haml2
-rw-r--r--app/views/search/results/_snippet_title.html.haml2
-rw-r--r--app/views/shared/_confirm_modal.html.haml2
-rw-r--r--app/views/shared/_milestones_filter.html.haml6
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml4
-rw-r--r--app/views/shared/tokens/_scopes_form.html.haml4
-rw-r--r--app/views/users/calendar_activities.html.haml2
-rw-r--r--app/views/users/show.html.haml8
-rw-r--r--app/workers/authorized_projects_worker.rb7
-rw-r--r--changelogs/unreleased/25312-search-input-cmd-click-issue.yml4
-rw-r--r--changelogs/unreleased/26068_tasklist_issue.yml4
-rw-r--r--changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml4
-rw-r--r--changelogs/unreleased/26445-accessible-piplelines-buttons.yml4
-rw-r--r--changelogs/unreleased/26447-fix-tab-list-order.yml4
-rw-r--r--changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml4
-rw-r--r--changelogs/unreleased/26775-fix-auto-complete-initial-loading.yml4
-rw-r--r--changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml4
-rw-r--r--changelogs/unreleased/26844-new-search-bar-performs-a-new-request-for-each-tag.yml4
-rw-r--r--changelogs/unreleased/26947-build-status-self-link.yml4
-rw-r--r--changelogs/unreleased/27013-regression-in-commit-title-bar.yml4
-rw-r--r--changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml4
-rw-r--r--changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml4
-rw-r--r--changelogs/unreleased/27044-fix-explore-sorting-on-trending.yml4
-rw-r--r--changelogs/unreleased/27066-textarea-border.yml4
-rw-r--r--changelogs/unreleased/27178-update-builds-link-in-project-settings.yml4
-rw-r--r--changelogs/unreleased/27259-label-for-references-the-wrong-associated-text-input.yml4
-rw-r--r--changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml4
-rw-r--r--changelogs/unreleased/add_project_update_hook.yml4
-rw-r--r--changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml4
-rw-r--r--changelogs/unreleased/copy-as-md.yml4
-rw-r--r--changelogs/unreleased/display-project-id.yml4
-rw-r--r--changelogs/unreleased/fix-26518.yml4
-rw-r--r--changelogs/unreleased/fix-cancel-integration-settings.yml4
-rw-r--r--changelogs/unreleased/fix_broken_diff_discussions.yml4
-rw-r--r--changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml4
-rw-r--r--changelogs/unreleased/issue-filter-click-to-search.yml4
-rw-r--r--changelogs/unreleased/issue_27211.yml4
-rw-r--r--changelogs/unreleased/label-select-toggle.yml4
-rw-r--r--changelogs/unreleased/merge-dropdown-this-context.yml4
-rw-r--r--changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml4
-rw-r--r--changelogs/unreleased/mr-tabs-container-offset.yml4
-rw-r--r--changelogs/unreleased/newline-eslint-rule.yml4
-rw-r--r--changelogs/unreleased/no_project_notes.yml4
-rw-r--r--changelogs/unreleased/refresh-authorizations-fork-join.yml4
-rw-r--r--changelogs/unreleased/revert-filter-assigned-to-me.yml4
-rw-r--r--changelogs/unreleased/sh-fix-annotated-tags-pointing-to-blob.yml4
-rw-r--r--changelogs/unreleased/small-screen-fullscreen-button.yml4
-rw-r--r--changelogs/unreleased/tc-only-mr-button-if-allowed.yml4
-rw-r--r--config/database.yml.mysql8
-rw-r--r--config/initializers/metrics.rb219
-rw-r--r--config/initializers/rspec_profiling.rb14
-rw-r--r--config/initializers/sidekiq.rb9
-rw-r--r--config/routes/group.rb6
-rw-r--r--db/fixtures/development/01_admin.rb2
-rw-r--r--db/fixtures/development/04_project.rb2
-rw-r--r--db/fixtures/development/05_users.rb2
-rw-r--r--db/fixtures/development/06_teams.rb2
-rw-r--r--db/fixtures/development/07_milestones.rb2
-rw-r--r--db/fixtures/development/09_issues.rb2
-rw-r--r--db/fixtures/development/10_merge_requests.rb2
-rw-r--r--db/fixtures/development/11_keys.rb22
-rw-r--r--db/fixtures/development/12_snippets.rb2
-rw-r--r--db/fixtures/development/13_comments.rb2
-rw-r--r--db/fixtures/development/14_pipelines.rb2
-rw-r--r--db/fixtures/development/15_award_emoji.rb2
-rw-r--r--db/fixtures/development/16_protected_branches.rb2
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb2
-rw-r--r--db/migrate/20161223034433_add_estimate_to_issuables_ce.rb12
-rw-r--r--db/migrate/20161223034646_create_timelogs_ce.rb6
-rw-r--r--doc/README.md3
-rw-r--r--doc/administration/auth/ldap.md11
-rw-r--r--doc/administration/raketasks/maintenance.md1
-rw-r--r--doc/administration/repository_checks.md10
-rw-r--r--doc/administration/repository_storage_paths.md102
-rw-r--r--doc/administration/repository_storages.md101
-rw-r--r--doc/api/enviroments.md4
-rw-r--r--doc/ci/examples/README.md6
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md156
-rw-r--r--doc/ci/yaml/README.md2
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/gemfile.md14
-rw-r--r--doc/development/performance.md35
-rw-r--r--doc/gitlab-basics/add-image.md50
-rw-r--r--doc/install/database_mysql.md205
-rw-r--r--doc/system_hooks/system_hooks.md19
-rw-r--r--doc/update/8.15-to-8.16.md2
-rw-r--r--doc/user/profile/account/two_factor_authentication.md118
-rw-r--r--features/steps/project/merge_requests/revert.rb7
-rw-r--r--features/support/env.rb3
-rw-r--r--lib/api/branches.rb7
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb5
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb4
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb6
-rw-r--r--lib/banzai/filter/user_reference_filter.rb4
-rw-r--r--lib/banzai/filter/video_link_filter.rb3
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb6
-rw-r--r--lib/banzai/renderer.rb4
-rw-r--r--lib/ci/api/builds.rb25
-rw-r--r--lib/gitlab/ci/trace_reader.rb1
-rw-r--r--lib/gitlab/current_settings.rb44
-rw-r--r--lib/gitlab/github_import/project_creator.rb4
-rw-r--r--lib/gitlab/import_sources.rb2
-rw-r--r--lib/gitlab/job_waiter.rb27
-rw-r--r--lib/gitlab/sidekiq_status.rb66
-rw-r--r--lib/gitlab/sidekiq_status/client_middleware.rb10
-rw-r--r--lib/gitlab/sidekiq_status/server_middleware.rb13
-rw-r--r--lib/gitlab/view/presenter/base.rb2
-rw-r--r--lib/gitlab/view/presenter/delegated.rb4
-rw-r--r--lib/gitlab/visibility_level.rb1
-rw-r--r--lib/tasks/gitlab/info.rake5
-rw-r--r--spec/controllers/admin/groups_controller_spec.rb2
-rw-r--r--spec/controllers/admin/projects_controller_spec.rb2
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb2
-rw-r--r--spec/controllers/blob_controller_spec.rb2
-rw-r--r--spec/controllers/ci/projects_controller_spec.rb2
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb2
-rw-r--r--spec/controllers/explore/projects_controller_spec.rb27
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/groups_controller_spec.rb2
-rw-r--r--spec/controllers/health_check_controller_spec.rb6
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb4
-rw-r--r--spec/controllers/import/fogbugz_controller_spec.rb4
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb4
-rw-r--r--spec/controllers/import/google_code_controller_spec.rb4
-rw-r--r--spec/controllers/notification_settings_controller_spec.rb2
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blame_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb2
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb2
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb2
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb2
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb2
-rw-r--r--spec/controllers/projects/cycle_analytics_controller_spec.rb2
-rw-r--r--spec/controllers/projects/discussions_controller_spec.rb6
-rw-r--r--spec/controllers/projects/find_file_controller_spec.rb2
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb2
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb6
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb4
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb4
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb2
-rw-r--r--spec/controllers/projects/refs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/releases_controller_spec.rb2
-rw-r--r--spec/controllers/projects/repositories_controller_spec.rb2
-rw-r--r--spec/controllers/projects/services_controller_spec.rb2
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb2
-rw-r--r--spec/controllers/projects/tags_controller_spec.rb2
-rw-r--r--spec/controllers/projects/templates_controller_spec.rb2
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb2
-rw-r--r--spec/controllers/projects/uploads_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb35
-rw-r--r--spec/controllers/uploads_controller_spec.rb2
-rw-r--r--spec/controllers/users_controller_spec.rb4
-rw-r--r--spec/factories/ci/runners.rb4
-rw-r--r--spec/factories/deploy_keys_projects.rb2
-rw-r--r--spec/factories/events.rb2
-rw-r--r--spec/factories/file_uploader.rb2
-rw-r--r--spec/factories/groups.rb3
-rw-r--r--spec/factories/issues.rb2
-rw-r--r--spec/factories/labels.rb2
-rw-r--r--spec/factories/merge_requests.rb2
-rw-r--r--spec/factories/milestones.rb2
-rw-r--r--spec/factories/notes.rb20
-rw-r--r--spec/factories/project_group_links.rb2
-rw-r--r--spec/factories/project_members.rb2
-rw-r--r--spec/factories/project_snippets.rb2
-rw-r--r--spec/factories/releases.rb2
-rw-r--r--spec/factories/sent_notifications.rb2
-rw-r--r--spec/factories/services.rb2
-rw-r--r--spec/factories/todos.rb2
-rw-r--r--spec/factories/trending_project.rb6
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb3
-rw-r--r--spec/features/admin/admin_health_check_spec.rb9
-rw-r--r--spec/features/admin/admin_runners_spec.rb3
-rw-r--r--spec/features/admin/admin_settings_spec.rb5
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb9
-rw-r--r--spec/features/copy_as_gfm_spec.rb432
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb34
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb18
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb27
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb17
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb16
-rw-r--r--spec/features/issues/form_spec.rb16
-rw-r--r--spec/features/projects/merge_request_button_spec.rb108
-rw-r--r--spec/features/projects/project_settings_spec.rb4
-rw-r--r--spec/finders/branches_finder_spec.rb2
-rw-r--r--spec/finders/contributed_projects_finder_spec.rb4
-rw-r--r--spec/finders/group_projects_finder_spec.rb10
-rw-r--r--spec/finders/joined_groups_finder_spec.rb2
-rw-r--r--spec/finders/merge_requests_finder_spec.rb6
-rw-r--r--spec/finders/notes_finder_spec.rb4
-rw-r--r--spec/finders/personal_projects_finder_spec.rb6
-rw-r--r--spec/finders/pipelines_finder_spec.rb2
-rw-r--r--spec/finders/projects_finder_spec.rb8
-rw-r--r--spec/finders/tags_finder_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/helpers/blob_helper_spec.rb2
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb18
-rw-r--r--spec/helpers/graph_helper_spec.rb2
-rw-r--r--spec/helpers/issues_helper_spec.rb2
-rw-r--r--spec/helpers/members_helper_spec.rb2
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb10
-rw-r--r--spec/helpers/milestones_helper_spec.rb14
-rw-r--r--spec/helpers/preferences_helper_spec.rb2
-rw-r--r--spec/helpers/projects_helper_spec.rb4
-rw-r--r--spec/helpers/search_helper_spec.rb6
-rw-r--r--spec/helpers/submodule_helper_spec.rb4
-rw-r--r--spec/helpers/tree_helper_spec.rb2
-rw-r--r--spec/helpers/visibility_level_helper_spec.rb8
-rw-r--r--spec/initializers/metrics_spec.rb16
-rw-r--r--spec/javascripts/abuse_reports_spec.js.es61
-rw-r--r--spec/javascripts/environments/environment_rollback_spec.js.es61
-rw-r--r--spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es66
-rw-r--r--spec/javascripts/gfm_auto_complete_spec.js.es626
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js.es617
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js21
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb14
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/gollum_tags_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/upload_link_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb24
-rw-r--r--spec/lib/banzai/filter/video_link_filter_spec.rb2
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb6
-rw-r--r--spec/lib/constraints/project_url_constrainer_spec.rb2
-rw-r--r--spec/lib/event_filter_spec.rb2
-rw-r--r--spec/lib/extracts_path_spec.rb2
-rw-r--r--spec/lib/gitlab/badge/build/metadata_spec.rb2
-rw-r--r--spec/lib/gitlab/badge/build/status_spec.rb2
-rw-r--r--spec/lib/gitlab/badge/coverage/metadata_spec.rb2
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/blame_spec.rb2
-rw-r--r--spec/lib/gitlab/chat_commands/command_spec.rb2
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb2
-rw-r--r--spec/lib/gitlab/checks/force_push_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace_reader_spec.rb16
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb8
-rw-r--r--spec/lib/gitlab/conflict/file_spec.rb2
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb68
-rw-r--r--spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb10
-rw-r--r--spec/lib/gitlab/data_builder/note_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/push_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/highlight_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/line_mapper_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/parallel_diff_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/create_issue_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/email/message/repository_push_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb4
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb4
-rw-r--r--spec/lib/gitlab/git/hook_spec.rb2
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_spec.rb18
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/branch_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/comment_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/issue_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/label_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/pull_request_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/release_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/graphs/commits_spec.rb2
-rw-r--r--spec/lib/gitlab/highlight_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/import_export_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/repo_bundler_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb2
-rw-r--r--spec/lib/gitlab/job_waiter_spec.rb30
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb12
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb22
-rw-r--r--spec/lib/gitlab/search_results_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb12
-rw-r--r--spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb14
-rw-r--r--spec/lib/gitlab/sidekiq_status_spec.rb50
-rw-r--r--spec/lib/gitlab/template/issue_template_spec.rb2
-rw-r--r--spec/lib/gitlab/template/merge_request_template_spec.rb2
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/view/presenter/delegated_spec.rb14
-rw-r--r--spec/lib/gitlab/view/presenter/factory_spec.rb7
-rw-r--r--spec/lib/gitlab/view/presenter/simple_spec.rb15
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
-rw-r--r--spec/lib/light_url_builder_spec.rb2
-rw-r--r--spec/lib/repository_cache_spec.rb2
-rw-r--r--spec/models/ability_spec.rb37
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb8
-rw-r--r--spec/models/ci/runner_spec.rb7
-rw-r--r--spec/models/commit_range_spec.rb2
-rw-r--r--spec/models/commit_spec.rb14
-rw-r--r--spec/models/commit_status_spec.rb2
-rw-r--r--spec/models/compare_spec.rb2
-rw-r--r--spec/models/concerns/issuable_spec.rb6
-rw-r--r--spec/models/concerns/mentionable_spec.rb32
-rw-r--r--spec/models/concerns/milestoneish_spec.rb2
-rw-r--r--spec/models/concerns/project_features_compatibility_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb10
-rw-r--r--spec/models/cycle_analytics/code_spec.rb2
-rw-r--r--spec/models/cycle_analytics/issue_spec.rb2
-rw-r--r--spec/models/cycle_analytics/plan_spec.rb2
-rw-r--r--spec/models/cycle_analytics/production_spec.rb2
-rw-r--r--spec/models/cycle_analytics/review_spec.rb2
-rw-r--r--spec/models/cycle_analytics/staging_spec.rb3
-rw-r--r--spec/models/cycle_analytics/test_spec.rb2
-rw-r--r--spec/models/deploy_keys_project_spec.rb4
-rw-r--r--spec/models/deployment_spec.rb2
-rw-r--r--spec/models/diff_note_spec.rb4
-rw-r--r--spec/models/environment_spec.rb4
-rw-r--r--spec/models/event_spec.rb6
-rw-r--r--spec/models/forked_project_link_spec.rb4
-rw-r--r--spec/models/global_milestone_spec.rb6
-rw-r--r--spec/models/group_milestone_spec.rb2
-rw-r--r--spec/models/group_spec.rb16
-rw-r--r--spec/models/guest_spec.rb6
-rw-r--r--spec/models/hooks/system_hook_spec.rb2
-rw-r--r--spec/models/hooks/web_hook_spec.rb2
-rw-r--r--spec/models/issue/metrics_spec.rb2
-rw-r--r--spec/models/issue_collection_spec.rb2
-rw-r--r--spec/models/issue_spec.rb20
-rw-r--r--spec/models/member_spec.rb2
-rw-r--r--spec/models/members/project_member_spec.rb8
-rw-r--r--spec/models/merge_request/metrics_spec.rb4
-rw-r--r--spec/models/milestone_spec.rb6
-rw-r--r--spec/models/namespace_spec.rb32
-rw-r--r--spec/models/network/graph_spec.rb2
-rw-r--r--spec/models/note_spec.rb95
-rw-r--r--spec/models/project_feature_spec.rb4
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/project_label_spec.rb2
-rw-r--r--spec/models/project_services/asana_service_spec.rb2
-rw-r--r--spec/models/project_services/assembla_service_spec.rb2
-rw-r--r--spec/models/project_services/campfire_service_spec.rb2
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb2
-rw-r--r--spec/models/project_services/external_wiki_service_spec.rb2
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb2
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb2
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb6
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb6
-rw-r--r--spec/models/project_services/irker_service_spec.rb2
-rw-r--r--spec/models/project_services/jira_service_spec.rb12
-rw-r--r--spec/models/project_services/pipeline_email_service_spec.rb2
-rw-r--r--spec/models/project_services/pushover_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb42
-rw-r--r--spec/models/project_team_spec.rb10
-rw-r--r--spec/models/repository_spec.rb26
-rw-r--r--spec/models/route_spec.rb2
-rw-r--r--spec/models/service_spec.rb16
-rw-r--r--spec/models/snippet_spec.rb6
-rw-r--r--spec/models/todo_spec.rb8
-rw-r--r--spec/models/tree_spec.rb2
-rw-r--r--spec/models/user_spec.rb80
-rw-r--r--spec/requests/api/branches_spec.rb2
-rw-r--r--spec/requests/api/builds_spec.rb2
-rw-r--r--spec/requests/api/commit_statuses_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb2
-rw-r--r--spec/requests/api/files_spec.rb4
-rw-r--r--spec/requests/api/fork_spec.rb21
-rw-r--r--spec/requests/api/internal_spec.rb11
-rw-r--r--spec/requests/api/merge_requests_spec.rb16
-rw-r--r--spec/requests/api/pipelines_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb1
-rw-r--r--spec/requests/api/repositories_spec.rb14
-rw-r--r--spec/requests/api/tags_spec.rb6
-rw-r--r--spec/requests/api/triggers_spec.rb2
-rw-r--r--spec/requests/ci/api/builds_spec.rb14
-rw-r--r--spec/requests/ci/api/triggers_spec.rb6
-rw-r--r--spec/requests/git_http_spec.rb24
-rw-r--r--spec/requests/projects/artifacts_controller_spec.rb2
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb9
-rw-r--r--spec/services/ci/register_build_service_spec.rb55
-rw-r--r--spec/services/notes/create_service_spec.rb51
-rw-r--r--spec/services/notification_service_spec.rb364
-rw-r--r--spec/services/system_hooks_service_spec.rb2
-rw-r--r--spec/services/user_project_access_changed_service_spec.rb12
-rw-r--r--spec/spec_helper.rb6
-rw-r--r--spec/support/cycle_analytics_helpers/test_generation.rb2
-rw-r--r--spec/support/mentionable_shared_examples.rb2
-rw-r--r--spec/support/sidekiq.rb5
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb6
-rw-r--r--spec/support/taskable_shared_examples.rb19
-rw-r--r--spec/views/ci/lints/show.html.haml_spec.rb2
-rw-r--r--spec/views/projects/builds/show.html.haml_spec.rb30
-rw-r--r--spec/workers/authorized_projects_worker_spec.rb12
561 files changed, 4702 insertions, 1728 deletions
diff --git a/.eslintrc b/.eslintrc
index e13f76b213c..9ab0145820d 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -15,6 +15,7 @@
"filenames"
],
"rules": {
- "filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"]
+ "filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
+ "no-multiple-empty-lines": ["error", { "max": 1 }]
}
}
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 7c8a9c4fd17..528f99d08d2 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -46,7 +46,7 @@ linters:
max: 80
MultilinePipe:
- enabled: false
+ enabled: true
MultilineScript:
enabled: true
@@ -77,7 +77,7 @@ linters:
- Style/WhileUntilModifier
RubyComments:
- enabled: false
+ enabled: true
SpaceBeforeScript:
enabled: true
@@ -97,7 +97,7 @@ linters:
enabled: true
UnnecessaryInterpolation:
- enabled: false
+ enabled: true
UnnecessaryStringOutput:
- enabled: false
+ enabled: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aecacbee2f5..9712b32232e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,22 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 8.16.2 (2017-01-25)
+
+- allow issue filter bar to be operated with mouse only. !8681
+- Fix CI requests concurrency for newer runners that prevents from picking pending builds (from 1.9.0-rc5). !8760
+- Add some basic fixes for IE11/Edge.
+- Remove blue border from comment box hover.
+- Fixed bug where links in merge dropdown wouldn't work.
+
+## 8.16.1 (2017-01-23)
+
+- Ensure export files are removed after a namespace is deleted.
+- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
+- Prevent users from creating notes on resources they can't access.
+- Prevent users from deleting system deploy keys via the project deploy key API.
+- Upgrade omniauth gem to 1.3.2.
+
## 8.16.0 (2017-02-22)
- Add LDAP Rake task to rename a provider. !2181
@@ -395,6 +411,14 @@ entry.
- Whitelist next project names: help, ci, admin, search. !8227
- Adds back CSS for progress-bars. !8237
+## 8.14.8 (2017-01-25)
+
+- Accept environment variables from the `pre-receive` script. !7967
+- Milestoneish SQL performance partially improved and memoized. !8146
+- Fix N+1 queries on milestone show pages. !8185
+- Speed up group milestone index by passing group_id to IssuesFinder. !8363
+- Ensure issuable state changes only fire webhooks once.
+
## 8.14.6 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b68c4a67826..d404f1b91df 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -80,11 +80,9 @@ the remaining issues on the GitHub issue tracker.
## I want to contribute!
If you want to contribute to GitLab, but are not sure where to start,
-look for [issues with the label `up-for-grabs`][up-for-grabs]. These issues
-will be of reasonable size and challenge, for anyone to start contributing to
-GitLab.
-
-This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
+look for [issues with the label `Accepting Merge Requests` and weight < 5][accepting-mrs-weight].
+These issues will be of reasonable size and challenge, for anyone to start
+contributing to GitLab.
## Implement design & UI elements
@@ -214,16 +212,19 @@ associated with in the description of the issue.
## Merge requests
We welcome merge requests with fixes and improvements to GitLab code, tests,
-and/or documentation. The features we would really like a merge request for are
-listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
-and [EE][accepting-mrs-ee] but other improvements are also welcome. Please note
-that if an issue is marked for the current milestone either before or while you
-are working on it, a team member may take over the merge request in order to
-ensure the work is finished before the release date.
+and/or documentation. The issues that are specifically suitable for
+community contributions are listed with the label
+[`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
+and [EE][accepting-mrs-ee], but you are free to contribute to any other issue
+you want.
+
+Please note that if an issue is marked for the current milestone either before
+or while you are working on it, a team member may take over the merge request
+in order to ensure the work is finished before the release date.
If you want to add a new feature that is not labeled it is best to first create
a feedback issue (if there isn't one already) and leave a comment asking for it
-to be marked as `Accepting merge requests`. Please include screenshots or
+to be marked as `Accepting Merge Requests`. Please include screenshots or
wireframes if the feature will also change the UI.
Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
@@ -285,14 +286,6 @@ request is as follows:
1. For tests that use Capybara or PhantomJS, see this [article on how
to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
-The **official merge window** is in the beginning of the month from the 1st to
-the 7th day of the month. This is the best time to submit an MR and get
-feedback fast. Before this time the GitLab Inc. team is still dealing with work
-that is created by the monthly release such as regressions requiring patch
-releases. After the 7th it is already getting closer to the release date of the
-next version. This means there is less time to fix the issues created by
-merging large new features.
-
Please keep the change in a single MR **as small as possible**. If you want to
contribute a large feature think very hard what the minimum viable change is.
Can you split the functionality? Can you only submit the backend/API code? Can
@@ -450,8 +443,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[core team]: https://about.gitlab.com/core-team/
[getting-help]: https://about.gitlab.com/getting-help/
[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
-[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
-[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455
+[accepting-mrs-weight]: https://gitlab.com/gitlab-org/gitlab-ce/issues?assignee_id=0&label_name[]=Accepting%20Merge%20Requests&sort=weight_asc
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
[google-group]: https://groups.google.com/forum/#!forum/gitlabhq
diff --git a/Gemfile b/Gemfile
index 4e9cf91c429..37a7666602f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -280,6 +280,7 @@ group :development, :test do
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
+ gem 'rspec_profiling'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'
@@ -322,7 +323,7 @@ group :test do
gem 'email_spec', '~> 1.6.0'
gem 'json-schema', '~> 2.6.2'
gem 'webmock', '~> 1.21.0'
- gem 'test_after_commit', '~> 0.4.2'
+ gem 'test_after_commit', '~> 1.1'
gem 'sham_rack', '~> 1.3.6'
gem 'timecop', '~> 0.8.0'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index c9115982838..671d7788a86 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -642,6 +642,11 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
+ rspec_profiling (0.0.4)
+ activerecord
+ pg
+ rails
+ sqlite3
rubocop (0.46.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
@@ -743,6 +748,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
+ sqlite3 (1.3.11)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
@@ -760,7 +766,7 @@ GEM
teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0)
temple (0.7.7)
- test_after_commit (0.4.2)
+ test_after_commit (1.1.0)
activerecord (>= 3.2)
thin (1.7.0)
daemons (~> 1.0, >= 1.0.9)
@@ -965,6 +971,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
+ rspec_profiling
rubocop (~> 0.46.0)
rubocop-rspec (~> 1.9.1)
ruby-fogbugz (~> 0.2.1)
@@ -997,7 +1004,7 @@ DEPENDENCIES
sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0)
teaspoon-jasmine (~> 2.2.0)
- test_after_commit (~> 0.4.2)
+ test_after_commit (~> 1.1)
thin (~> 1.7.0)
timecop (~> 0.8.0)
truncato (~> 0.7.8)
diff --git a/app/assets/javascripts/copy_as_gfm.js.es6 b/app/assets/javascripts/copy_as_gfm.js.es6
new file mode 100644
index 00000000000..b94125a4210
--- /dev/null
+++ b/app/assets/javascripts/copy_as_gfm.js.es6
@@ -0,0 +1,355 @@
+/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
+/* jshint esversion: 6 */
+
+/*= require lib/utils/common_utils */
+
+(() => {
+ const gfmRules = {
+ // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert
+ // GitLab Flavored Markdown (GFM) to HTML.
+ // These handlers consequently convert that same HTML to GFM to be copied to the clipboard.
+ // Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML
+ // from GFM should have a handler here, in reverse order.
+ // The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb.
+ InlineDiffFilter: {
+ 'span.idiff.addition'(el, text) {
+ return `{+${text}+}`;
+ },
+ 'span.idiff.deletion'(el, text) {
+ return `{-${text}-}`;
+ },
+ },
+ TaskListFilter: {
+ 'input[type=checkbox].task-list-item-checkbox'(el, text) {
+ return `[${el.checked ? 'x' : ' '}]`;
+ },
+ },
+ ReferenceFilter: {
+ 'a.gfm:not([data-link=true])'(el, text) {
+ return el.dataset.original || text;
+ },
+ },
+ AutolinkFilter: {
+ 'a'(el, text) {
+ // Fallback on the regular MarkdownFilter's `a` handler.
+ if (text !== el.getAttribute('href')) return false;
+
+ return text;
+ },
+ },
+ TableOfContentsFilter: {
+ 'ul.section-nav'(el, text) {
+ return '[[_TOC_]]';
+ },
+ },
+ EmojiFilter: {
+ 'img.emoji'(el, text) {
+ return el.getAttribute('alt');
+ },
+ },
+ ImageLinkFilter: {
+ 'a.no-attachment-icon'(el, text) {
+ return text;
+ },
+ },
+ VideoLinkFilter: {
+ '.video-container'(el, text) {
+ const videoEl = el.querySelector('video');
+ if (!videoEl) return false;
+
+ return CopyAsGFM.nodeToGFM(videoEl);
+ },
+ 'video'(el, text) {
+ return `![${el.dataset.title}](${el.getAttribute('src')})`;
+ },
+ },
+ MathFilter: {
+ 'pre.code.math[data-math-style=display]'(el, text) {
+ return `\`\`\`math\n${text.trim()}\n\`\`\``;
+ },
+ 'code.code.math[data-math-style=inline]'(el, text) {
+ return `$\`${text}\`$`;
+ },
+ 'span.katex-display span.katex-mathml'(el, text) {
+ const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
+ if (!mathAnnotation) return false;
+
+ return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``;
+ },
+ 'span.katex-mathml'(el, text) {
+ const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
+ if (!mathAnnotation) return false;
+
+ return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`;
+ },
+ 'span.katex-html'(el, text) {
+ // We don't want to include the content of this element in the copied text.
+ return '';
+ },
+ 'annotation[encoding="application/x-tex"]'(el, text) {
+ return text.trim();
+ },
+ },
+ SanitizationFilter: {
+ 'dl'(el, text) {
+ let lines = text.trim().split('\n');
+ // Add two spaces to the front of subsequent list items lines,
+ // or leave the line entirely blank.
+ lines = lines.map((l) => {
+ const line = l.trim();
+ if (line.length === 0) return '';
+
+ return ` ${line}`;
+ });
+
+ return `<dl>\n${lines.join('\n')}\n</dl>`;
+ },
+ 'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) {
+ const tag = el.nodeName.toLowerCase();
+ return `<${tag}>${text}</${tag}>`;
+ },
+ },
+ SyntaxHighlightFilter: {
+ 'pre.code.highlight'(el, t) {
+ const text = t.trim();
+
+ let lang = el.getAttribute('lang');
+ if (lang === 'plaintext') {
+ lang = '';
+ }
+
+ // Prefixes lines with 4 spaces if the code contains triple backticks
+ if (lang === '' && text.match(/^```/gm)) {
+ return text.split('\n').map((l) => {
+ const line = l.trim();
+ if (line.length === 0) return '';
+
+ return ` ${line}`;
+ }).join('\n');
+ }
+
+ return `\`\`\`${lang}\n${text}\n\`\`\``;
+ },
+ 'pre > code'(el, text) {
+ // Don't wrap code blocks in ``
+ return text;
+ },
+ },
+ MarkdownFilter: {
+ 'br'(el, text) {
+ // Two spaces at the end of a line are turned into a BR
+ return ' ';
+ },
+ 'code'(el, text) {
+ let backtickCount = 1;
+ const backtickMatch = text.match(/`+/);
+ if (backtickMatch) {
+ backtickCount = backtickMatch[0].length + 1;
+ }
+
+ const backticks = Array(backtickCount + 1).join('`');
+ const spaceOrNoSpace = backtickCount > 1 ? ' ' : '';
+
+ return backticks + spaceOrNoSpace + text + spaceOrNoSpace + backticks;
+ },
+ 'blockquote'(el, text) {
+ return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
+ },
+ 'img'(el, text) {
+ return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
+ },
+ 'a.anchor'(el, text) {
+ // Don't render a Markdown link for the anchor link inside a heading
+ return text;
+ },
+ 'a'(el, text) {
+ return `[${text}](${el.getAttribute('href')})`;
+ },
+ 'li'(el, text) {
+ const lines = text.trim().split('\n');
+ const firstLine = `- ${lines.shift()}`;
+ // Add four spaces to the front of subsequent list items lines,
+ // or leave the line entirely blank.
+ const nextLines = lines.map((s) => {
+ if (s.trim().length === 0) return '';
+
+ return ` ${s}`;
+ });
+
+ return `${firstLine}\n${nextLines.join('\n')}`;
+ },
+ 'ul'(el, text) {
+ return text;
+ },
+ 'ol'(el, text) {
+ // LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists.
+ return text.replace(/^- /mg, '1. ');
+ },
+ 'h1'(el, text) {
+ return `# ${text.trim()}`;
+ },
+ 'h2'(el, text) {
+ return `## ${text.trim()}`;
+ },
+ 'h3'(el, text) {
+ return `### ${text.trim()}`;
+ },
+ 'h4'(el, text) {
+ return `#### ${text.trim()}`;
+ },
+ 'h5'(el, text) {
+ return `##### ${text.trim()}`;
+ },
+ 'h6'(el, text) {
+ return `###### ${text.trim()}`;
+ },
+ 'strong'(el, text) {
+ return `**${text}**`;
+ },
+ 'em'(el, text) {
+ return `_${text}_`;
+ },
+ 'del'(el, text) {
+ return `~~${text}~~`;
+ },
+ 'sup'(el, text) {
+ return `^${text}`;
+ },
+ 'hr'(el, text) {
+ return '-----';
+ },
+ 'table'(el, text) {
+ const theadEl = el.querySelector('thead');
+ const tbodyEl = el.querySelector('tbody');
+ if (!theadEl || !tbodyEl) return false;
+
+ const theadText = CopyAsGFM.nodeToGFM(theadEl);
+ const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl);
+
+ return theadText + tbodyText;
+ },
+ 'thead'(el, text) {
+ const cells = _.map(el.querySelectorAll('th'), (cell) => {
+ let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2;
+
+ let before = '';
+ let after = '';
+ switch (cell.style.textAlign) {
+ case 'center':
+ before = ':';
+ after = ':';
+ chars -= 2;
+ break;
+ case 'right':
+ after = ':';
+ chars -= 1;
+ break;
+ default:
+ break;
+ }
+
+ chars = Math.max(chars, 3);
+
+ const middle = Array(chars + 1).join('-');
+
+ return before + middle + after;
+ });
+
+ return `${text}|${cells.join('|')}|`;
+ },
+ 'tr'(el, text) {
+ const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim());
+ return `| ${cells.join(' | ')} |`;
+ },
+ },
+ };
+
+ class CopyAsGFM {
+ constructor() {
+ $(document).on('copy', '.md, .wiki', this.handleCopy);
+ $(document).on('paste', '.js-gfm-input', this.handlePaste);
+ }
+
+ handleCopy(e) {
+ const clipboardData = e.originalEvent.clipboardData;
+ if (!clipboardData) return;
+
+ const documentFragment = window.gl.utils.getSelectedFragment();
+ if (!documentFragment) return;
+
+ // If the documentFragment contains more than just Markdown, don't copy as GFM.
+ if (documentFragment.querySelector('.md, .wiki')) return;
+
+ e.preventDefault();
+ clipboardData.setData('text/plain', documentFragment.textContent);
+
+ const gfm = CopyAsGFM.nodeToGFM(documentFragment);
+ clipboardData.setData('text/x-gfm', gfm);
+ }
+
+ handlePaste(e) {
+ const clipboardData = e.originalEvent.clipboardData;
+ if (!clipboardData) return;
+
+ const gfm = clipboardData.getData('text/x-gfm');
+ if (!gfm) return;
+
+ e.preventDefault();
+
+ window.gl.utils.insertText(e.target, gfm);
+ }
+
+ static nodeToGFM(node) {
+ if (node.nodeType === Node.TEXT_NODE) {
+ return node.textContent;
+ }
+
+ const text = this.innerGFM(node);
+
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+ return text;
+ }
+
+ for (const filter in gfmRules) {
+ const rules = gfmRules[filter];
+
+ for (const selector in rules) {
+ const func = rules[selector];
+
+ if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue;
+
+ const result = func(node, text);
+ if (result === false) continue;
+
+ return result;
+ }
+ }
+
+ return text;
+ }
+
+ static innerGFM(parentNode) {
+ const nodes = parentNode.childNodes;
+
+ const clonedParentNode = parentNode.cloneNode(true);
+ const clonedNodes = Array.prototype.slice.call(clonedParentNode.childNodes, 0);
+
+ for (let i = 0; i < nodes.length; i += 1) {
+ const node = nodes[i];
+ const clonedNode = clonedNodes[i];
+
+ const text = this.nodeToGFM(node);
+
+ // `clonedNode.replaceWith(text)` is not yet widely supported
+ clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode);
+ }
+
+ return clonedParentNode.innerText || clonedParentNode.textContent;
+ }
+ }
+
+ window.gl = window.gl || {};
+ window.gl.CopyAsGFM = CopyAsGFM;
+
+ new CopyAsGFM();
+})();
diff --git a/app/assets/javascripts/diff.js.es6 b/app/assets/javascripts/diff.js.es6
index 5e1a4c948aa..35a029194d0 100644
--- a/app/assets/javascripts/diff.js.es6
+++ b/app/assets/javascripts/diff.js.es6
@@ -1,5 +1,7 @@
/* eslint-disable class-methods-use-this */
+//= require lib/utils/url_utility */
+
(() => {
const UNFOLD_COUNT = 20;
@@ -104,11 +106,11 @@
}
highlighSelectedLine() {
+ const hash = gl.utils.getLocationHash();
const $diffFiles = $('.diff-file');
$diffFiles.find('.hll').removeClass('hll');
- if (window.location.hash !== '') {
- const hash = window.location.hash.replace('#', '');
+ if (hash) {
$diffFiles
.find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`)
.addClass('hll');
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index dcf67a8fd68..529d476ca4e 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -261,6 +261,9 @@
case 'projects:artifacts:browse':
new BuildArtifacts();
break;
+ case 'help:index':
+ gl.VersionCheckImage.bindErrorEvent($('img.js-version-status-badge'));
+ break;
case 'search:show':
new Search();
break;
diff --git a/app/assets/javascripts/droplab/droplab.js b/app/assets/javascripts/droplab/droplab.js
index c79f0230951..8b14191395b 100644
--- a/app/assets/javascripts/droplab/droplab.js
+++ b/app/assets/javascripts/droplab/droplab.js
@@ -58,6 +58,7 @@ var CustomEvent = require('./custom_event_polyfill');
var utils = require('./utils');
var DropDown = function(list) {
+ this.currentIndex = 0;
this.hidden = true;
this.list = list;
this.items = [];
@@ -164,15 +165,21 @@ Object.assign(DropDown.prototype, {
},
show: function() {
- // debugger
- this.list.style.display = 'block';
- this.hidden = false;
+ if (this.hidden) {
+ // debugger
+ this.list.style.display = 'block';
+ this.currentIndex = 0;
+ this.hidden = false;
+ }
},
hide: function() {
- // debugger
- this.list.style.display = 'none';
- this.hidden = true;
+ if (!this.hidden) {
+ // debugger
+ this.list.style.display = 'none';
+ this.currentIndex = 0;
+ this.hidden = true;
+ }
},
destroy: function() {
@@ -478,6 +485,8 @@ Object.assign(HookInput.prototype, {
this.input = function input(e) {
if(self.hasRemovedEvents) return;
+ self.list.show();
+
var inputEvent = new CustomEvent('input.dl', {
detail: {
hook: self,
@@ -487,7 +496,6 @@ Object.assign(HookInput.prototype, {
cancelable: true
});
e.target.dispatchEvent(inputEvent);
- self.list.show();
}
this.keyup = function keyup(e) {
@@ -503,6 +511,8 @@ Object.assign(HookInput.prototype, {
}
function keyEvent(e, keyEventName){
+ self.list.show();
+
var keyEvent = new CustomEvent(keyEventName, {
detail: {
hook: self,
@@ -514,7 +524,6 @@ Object.assign(HookInput.prototype, {
cancelable: true
});
e.target.dispatchEvent(keyEvent);
- self.list.show();
}
this.events = this.events || {};
@@ -572,24 +581,43 @@ require('./window')(function(w){
module.exports = function(){
var currentKey;
var currentFocus;
- var currentIndex = 0;
var isUpArrow = false;
var isDownArrow = false;
var removeHighlight = function removeHighlight(list) {
- var listItems = list.list.querySelectorAll('li');
+ var listItems = Array.prototype.slice.call(list.list.querySelectorAll('li:not(.divider)'), 0);
+ var listItemsTmp = [];
for(var i = 0; i < listItems.length; i++) {
- listItems[i].classList.remove('dropdown-active');
+ var listItem = listItems[i];
+ listItem.classList.remove('dropdown-active');
+
+ if (listItem.style.display !== 'none') {
+ listItemsTmp.push(listItem);
+ }
}
- return listItems;
+ return listItemsTmp;
};
var setMenuForArrows = function setMenuForArrows(list) {
var listItems = removeHighlight(list);
- if(currentIndex>0){
- if(!listItems[currentIndex-1]){
- currentIndex = currentIndex-1;
+ if(list.currentIndex>0){
+ if(!listItems[list.currentIndex-1]){
+ list.currentIndex = list.currentIndex-1;
+ }
+
+ if (listItems[list.currentIndex-1]) {
+ var el = listItems[list.currentIndex-1];
+ var filterDropdownEl = el.closest('.filter-dropdown');
+ el.classList.add('dropdown-active');
+
+ if (filterDropdownEl) {
+ var filterDropdownBottom = filterDropdownEl.offsetHeight;
+ var elOffsetTop = el.offsetTop - 30;
+
+ if (elOffsetTop > filterDropdownBottom) {
+ filterDropdownEl.scrollTop = elOffsetTop - filterDropdownBottom;
+ }
+ }
}
- listItems[currentIndex-1].classList.add('dropdown-active');
}
};
@@ -597,13 +625,13 @@ require('./window')(function(w){
var list = e.detail.hook.list;
removeHighlight(list);
list.show();
- currentIndex = 0;
+ list.currentIndex = 0;
isUpArrow = false;
isDownArrow = false;
};
var selectItem = function selectItem(list) {
var listItems = removeHighlight(list);
- var currentItem = listItems[currentIndex-1];
+ var currentItem = listItems[list.currentIndex-1];
var listEvent = new CustomEvent('click.dl', {
detail: {
list: list,
@@ -617,6 +645,8 @@ require('./window')(function(w){
var keydown = function keydown(e){
var typedOn = e.target;
+ var list = e.detail.hook.list;
+ var currentIndex = list.currentIndex;
isUpArrow = false;
isDownArrow = false;
@@ -648,6 +678,7 @@ require('./window')(function(w){
if(isUpArrow){ currentIndex--; }
if(isDownArrow){ currentIndex++; }
if(currentIndex < 0){ currentIndex = 0; }
+ list.currentIndex = currentIndex;
setMenuForArrows(e.detail.hook.list);
};
diff --git a/app/assets/javascripts/droplab/droplab_ajax.js b/app/assets/javascripts/droplab/droplab_ajax.js
index f20610b3811..c290e1a8355 100644
--- a/app/assets/javascripts/droplab/droplab_ajax.js
+++ b/app/assets/javascripts/droplab/droplab_ajax.js
@@ -9,6 +9,7 @@ require('../window')(function(w){
w.droplabAjax = {
_loadUrlData: function _loadUrlData(url) {
+ var self = this;
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest;
xhr.open('GET', url, true);
@@ -16,6 +17,7 @@ require('../window')(function(w){
if(xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var data = JSON.parse(xhr.responseText);
+ self.cache[url] = data;
return resolve(data);
} else {
return reject([xhr.responseText, xhr.status]);
@@ -26,9 +28,23 @@ require('../window')(function(w){
});
},
+ _loadData: function _loadData(data, config, self) {
+ if (config.loadingTemplate) {
+ var dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]');
+
+ if (dataLoadingTemplate) {
+ dataLoadingTemplate.outerHTML = self.listTemplate;
+ }
+ }
+
+ self.hook.list[config.method].call(self.hook.list, data);
+ },
+
init: function init(hook) {
var self = this;
+ self.cache = self.cache || {};
var config = hook.config.droplabAjax;
+ this.hook = hook;
if (!config || !config.endpoint || !config.method) {
return;
@@ -49,22 +65,23 @@ require('../window')(function(w){
dynamicList.outerHTML = loadingTemplate.outerHTML;
}
- this._loadUrlData(config.endpoint)
- .then(function(d) {
- if (config.loadingTemplate) {
- var dataLoadingTemplate = hook.list.list.querySelector('[data-loading-template]');
-
- if (dataLoadingTemplate) {
- dataLoadingTemplate.outerHTML = self.listTemplate;
- }
- }
- hook.list[config.method].call(hook.list, d);
- }).catch(function(e) {
- throw new droplabAjaxException(e.message || e);
- });
+ if (self.cache[config.endpoint]) {
+ self._loadData(self.cache[config.endpoint], config, self);
+ } else {
+ this._loadUrlData(config.endpoint)
+ .then(function(d) {
+ self._loadData(d, config, self);
+ }).catch(function(e) {
+ throw new droplabAjaxException(e.message || e);
+ });
+ }
},
destroy: function() {
+ if (this.listTemplate) {
+ var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
+ dynamicList.outerHTML = this.listTemplate;
+ }
}
};
});
@@ -76,4 +93,4 @@ module.exports = function(callback) {
};
},{}]},{},[1])(1)
-}); \ No newline at end of file
+});
diff --git a/app/assets/javascripts/droplab/droplab_ajax_filter.js b/app/assets/javascripts/droplab/droplab_ajax_filter.js
index af163f76851..b63d73066cb 100644
--- a/app/assets/javascripts/droplab/droplab_ajax_filter.js
+++ b/app/assets/javascripts/droplab/droplab_ajax_filter.js
@@ -72,31 +72,22 @@ require('../window')(function(w){
var params = config.params || {};
params[config.searchKey] = searchValue;
var self = this;
- this._loadUrlData(config.endpoint + this.buildParams(params)).then(function(data) {
- if (config.loadingTemplate && self.hook.list.data === undefined ||
- self.hook.list.data.length === 0) {
- const dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]');
-
- if (dataLoadingTemplate) {
- dataLoadingTemplate.outerHTML = self.listTemplate;
- }
- }
-
- if (!self.destroyed) {
- var hookListChildren = self.hook.list.list.children;
- var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
-
- if (onlyDynamicList && data.length === 0) {
- self.hook.list.hide();
- }
-
- self.hook.list.setData.call(self.hook.list, data);
- }
- self.notLoading();
- });
+ self.cache = self.cache || {};
+ var url = config.endpoint + this.buildParams(params);
+ var urlCachedData = self.cache[url];
+
+ if (urlCachedData) {
+ self._loadData(urlCachedData, config, self);
+ } else {
+ this._loadUrlData(url)
+ .then(function(data) {
+ self._loadData(data, config, self);
+ });
+ }
},
_loadUrlData: function _loadUrlData(url) {
+ var self = this;
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest;
xhr.open('GET', url, true);
@@ -104,6 +95,7 @@ require('../window')(function(w){
if(xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var data = JSON.parse(xhr.responseText);
+ self.cache[url] = data;
return resolve(data);
} else {
return reject([xhr.responseText, xhr.status]);
@@ -114,6 +106,30 @@ require('../window')(function(w){
});
},
+ _loadData: function _loadData(data, config, self) {
+ if (config.loadingTemplate && self.hook.list.data === undefined ||
+ self.hook.list.data.length === 0) {
+ const dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]');
+
+ if (dataLoadingTemplate) {
+ dataLoadingTemplate.outerHTML = self.listTemplate;
+ }
+ }
+
+ if (!self.destroyed) {
+ var hookListChildren = self.hook.list.list.children;
+ var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
+
+ if (onlyDynamicList && data.length === 0) {
+ self.hook.list.hide();
+ }
+
+ self.hook.list.setData.call(self.hook.list, data);
+ }
+ self.notLoading();
+ self.hook.list.currentIndex = 0;
+ },
+
buildParams: function(params) {
if (!params) return '';
var paramsArray = Object.keys(params).map(function(param) {
@@ -142,4 +158,4 @@ module.exports = function(callback) {
};
},{}]},{},[1])(1)
-}); \ No newline at end of file
+});
diff --git a/app/assets/javascripts/droplab/droplab_filter.js b/app/assets/javascripts/droplab/droplab_filter.js
index 41a220831f9..9b40a3f20a4 100644
--- a/app/assets/javascripts/droplab/droplab_filter.js
+++ b/app/assets/javascripts/droplab/droplab_filter.js
@@ -6,6 +6,8 @@ require('../window')(function(w){
w.droplabFilter = {
keydownWrapper: function(e){
+ var hiddenCount = 0;
+ var dataHiddenCount = 0;
var list = e.detail.hook.list;
var data = list.data;
var value = e.detail.hook.trigger.value.toLowerCase();
@@ -27,10 +29,22 @@ require('../window')(function(w){
};
}
+ dataHiddenCount = data.filter(function(o) {
+ return !o.droplab_hidden;
+ }).length;
+
matches = data.map(function(o) {
return filterFunction(o, value);
});
- list.render(matches);
+
+ hiddenCount = matches.filter(function(o) {
+ return !o.droplab_hidden;
+ }).length;
+
+ if (dataHiddenCount !== hiddenCount) {
+ list.render(matches);
+ list.currentIndex = 0;
+ }
},
init: function init(hookInput) {
@@ -57,4 +71,4 @@ module.exports = function(callback) {
};
},{}]},{},[1])(1)
-}); \ No newline at end of file
+});
diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6
index 9f24a6a4f88..3b003f6f661 100644
--- a/app/assets/javascripts/environments/environments_bundle.js.es6
+++ b/app/assets/javascripts/environments/environments_bundle.js.es6
@@ -3,7 +3,6 @@
//= require ./components/environment
//= require ./vue_resource_interceptor
-
$(() => {
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js.es6 b/app/assets/javascripts/filtered_search/dropdown_utils.js.es6
index 443ac222f70..eeab10fba17 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js.es6
@@ -84,7 +84,7 @@
let inputValue = input.value;
// Replace all spaces inside quote marks with underscores
// This helps with matching the beginning & end of a token:key
- inputValue = inputValue.replace(/"(.*?)"/g, str => str.replace(/\s/g, '_'));
+ inputValue = inputValue.replace(/("(.*?)"|:\s+)/g, str => str.replace(/\s/g, '_'));
// Get the right position for the word selected
// Regex matches first space
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
index ae19bb68360..8d62324b79f 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
@@ -64,13 +64,26 @@
}
checkForEnter(e) {
+ if (e.keyCode === 38 || e.keyCode === 40) {
+ const selectionStart = this.filteredSearchInput.selectionStart;
+
+ e.preventDefault();
+ this.filteredSearchInput.setSelectionRange(selectionStart, selectionStart);
+ }
+
if (e.keyCode === 13) {
+ const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
+ const dropdownEl = dropdown.element;
+ const activeElements = dropdownEl.querySelectorAll('.dropdown-active');
+
e.preventDefault();
- // Prevent droplab from opening dropdown
- this.dropdownManager.destroyDroplab();
+ if (!activeElements.length) {
+ // Prevent droplab from opening dropdown
+ this.dropdownManager.destroyDroplab();
- this.search();
+ this.search();
+ }
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6
index e46373024b6..e6b53cd4b55 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6
@@ -21,6 +21,15 @@
symbol: '~',
}];
+ const alternativeTokenKeys = [{
+ key: 'label',
+ type: 'string',
+ param: 'name',
+ symbol: '~',
+ }];
+
+ const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
+
const conditions = [{
url: 'assignee_id=0',
tokenKey: 'assignee',
@@ -44,6 +53,10 @@
return tokenKeys;
}
+ static getAlternatives() {
+ return alternativeTokenKeys;
+ }
+
static getConditions() {
return conditions;
}
@@ -57,7 +70,7 @@
}
static searchByKeyParam(keyParam) {
- return tokenKeys.find((tokenKey) => {
+ return tokenKeysWithAlternative.find((tokenKey) => {
let tokenKeyParam = tokenKey.key;
if (tokenKey.param) {
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index a1b7b442882..3f23095dad9 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -367,9 +367,14 @@
return $input.trigger('keyup');
},
isLoading(data) {
- if (!data || !data.length) return false;
- if (Array.isArray(data)) data = data[0];
- return data === this.defaultLoadingData[0] || data.name === this.defaultLoadingData[0];
+ var dataToInspect = data;
+ if (data && data.length > 0) {
+ dataToInspect = data[0];
+ }
+
+ var loadingState = this.defaultLoadingData[0];
+ return dataToInspect &&
+ (dataToInspect === loadingState || dataToInspect.name === loadingState);
}
};
}).call(this);
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 7746535d9ed..cc1c0877cdf 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -651,18 +651,14 @@
isMarking = false;
el.removeClass(ACTIVE_CLASS);
if (field && field.length) {
- if (isInput) {
- field.val('');
- } else {
- field.remove();
- }
+ this.clearField(field, isInput);
}
} else if (el.hasClass(INDETERMINATE_CLASS)) {
isMarking = true;
el.addClass(ACTIVE_CLASS);
el.removeClass(INDETERMINATE_CLASS);
if (field && field.length && value == null) {
- field.remove();
+ this.clearField(field, isInput);
}
if ((!field || !field.length) && fieldName) {
this.addInput(fieldName, value, selectedObject);
@@ -676,7 +672,7 @@
}
}
if (field && field.length && value == null) {
- field.remove();
+ this.clearField(field, isInput);
}
// Toggle active class for the tick mark
el.addClass(ACTIVE_CLASS);
@@ -826,6 +822,10 @@
return $(this.el).find(".dropdown-toggle-text").text(this.options.toggleLabel(selected, el, instance));
};
+ GitLabDropdown.prototype.clearField = function(field, isInput) {
+ return isInput ? field.val('') : field.remove();
+ };
+
return GitLabDropdown;
})();
diff --git a/app/assets/javascripts/issues_bulk_assignment.js.es6 b/app/assets/javascripts/issues_bulk_assignment.js.es6
index c260ad03d47..e0ebd36a65c 100644
--- a/app/assets/javascripts/issues_bulk_assignment.js.es6
+++ b/app/assets/javascripts/issues_bulk_assignment.js.es6
@@ -61,7 +61,6 @@
return labels;
}
-
/**
* Will return only labels that were marked previously and the user has unmarked
* @return {Array} Label IDs
@@ -80,7 +79,6 @@
return result;
}
-
/**
* Simple form serialization, it will return just what we need
* Returns key/value pairs from form data
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index fd1e229e30a..70dc0d06b7b 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -336,7 +336,11 @@
.removeClass('is-active');
}
- if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
+ if ($dropdown.hasClass('js-issuable-form-dropdown')) {
+ return;
+ }
+
+ if ($dropdown.hasClass('js-filter-bulk-update')) {
_this.enableBulkLabelDropdown();
_this.setDropdownData($dropdown, isMarking, this.id(label));
return;
diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6
index 6d57d31f380..51993bb3420 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js.es6
+++ b/app/assets/javascripts/lib/utils/common_utils.js.es6
@@ -159,5 +159,75 @@
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
};
+
+ w.gl.utils.getSelectedFragment = () => {
+ const selection = window.getSelection();
+ const documentFragment = selection.getRangeAt(0).cloneContents();
+ if (documentFragment.textContent.length === 0) return null;
+
+ return documentFragment;
+ };
+
+ w.gl.utils.insertText = (target, text) => {
+ // Firefox doesn't support `document.execCommand('insertText', false, text)` on textareas
+
+ const selectionStart = target.selectionStart;
+ const selectionEnd = target.selectionEnd;
+ const value = target.value;
+
+ const textBefore = value.substring(0, selectionStart);
+ const textAfter = value.substring(selectionEnd, value.length);
+ const newText = textBefore + text + textAfter;
+
+ target.value = newText;
+ target.selectionStart = target.selectionEnd = selectionStart + text.length;
+
+ // Trigger autosave
+ $(target).trigger('input');
+
+ // Trigger autosize
+ var event = document.createEvent('Event');
+ event.initEvent('autosize:update', true, false);
+ target.dispatchEvent(event);
+ };
+
+ w.gl.utils.nodeMatchesSelector = (node, selector) => {
+ const matches = Element.prototype.matches ||
+ Element.prototype.matchesSelector ||
+ Element.prototype.mozMatchesSelector ||
+ Element.prototype.msMatchesSelector ||
+ Element.prototype.oMatchesSelector ||
+ Element.prototype.webkitMatchesSelector;
+
+ if (matches) {
+ return matches.call(node, selector);
+ }
+
+ // IE11 doesn't support `node.matches(selector)`
+
+ let parentNode = node.parentNode;
+ if (!parentNode) {
+ parentNode = document.createElement('div');
+ node = node.cloneNode(true);
+ parentNode.appendChild(node);
+ }
+
+ const matchingNodes = parentNode.querySelectorAll(selector);
+ return Array.prototype.indexOf.call(matchingNodes, node) !== -1;
+ };
+
+ /**
+ this will take in the headers from an API response and normalize them
+ this way we don't run into production issues when nginx gives us lowercased header keys
+ */
+ w.gl.utils.normalizeHeaders = (headers) => {
+ const upperCaseHeaders = {};
+
+ Object.keys(headers).forEach((e) => {
+ upperCaseHeaders[e.toUpperCase()] = headers[e];
+ });
+
+ return upperCaseHeaders;
+ };
})(window);
}).call(this);
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index 4620715a521..2f147704c22 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -74,8 +74,9 @@
// If not done this way, the line number anchor will sometimes keep its
// active state even when the event is cancelled, resulting in an ugly border
// around the link and/or a persisted underline text decoration.
- return $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) {
- return event.preventDefault();
+ $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) {
+ event.preventDefault();
+ event.stopPropagation();
});
};
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 06a72efa21d..9db830a7ada 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -220,7 +220,6 @@
})(this));
};
-
/*
Increase @pollingInterval up to 120 seconds on every function call,
if `shouldReset` has a truthy value, 'null' or 'undefined' the variable
@@ -244,7 +243,6 @@
return this.initRefresh();
};
-
Notes.prototype.handleCreateChanges = function(note) {
if (typeof note === 'undefined') {
return;
@@ -294,7 +292,6 @@
}
};
-
/*
Check if note does not exists on page
*/
@@ -307,7 +304,6 @@
return this.view === 'parallel';
};
-
/*
Render note in discussion area.
@@ -358,7 +354,6 @@
return this.updateNotesCount(1);
};
-
/*
Called in response the main target form has been successfully submitted.
@@ -390,7 +385,6 @@
return form.find(".js-note-text").trigger("input");
};
-
/*
Shows the main form and does some setup on it.
@@ -415,7 +409,6 @@
return this.parentTimeline = form.parents('.timeline');
};
-
/*
General note form setup.
@@ -432,7 +425,6 @@
return new Autosave(textarea, ["Note", form.find("#note_noteable_type").val(), form.find("#note_noteable_id").val(), form.find("#note_commit_id").val(), form.find("#note_type").val(), form.find("#note_line_code").val(), form.find("#note_position").val()]);
};
-
/*
Called in response to the new note form being submitted
@@ -448,7 +440,6 @@
return new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', this.parentTimeline);
};
-
/*
Called in response to the new note form being submitted
@@ -473,7 +464,6 @@
this.removeDiscussionNoteForm($form);
};
-
/*
Called in response to the edit note form being submitted
@@ -498,7 +488,6 @@
}
};
-
Notes.prototype.checkContentToAllowEditing = function($el) {
var initialContent = $el.find('.original-note-content').text().trim();
var currentContent = $el.find('.note-textarea').val();
@@ -522,7 +511,6 @@
return isAllowed;
};
-
/*
Called in response to clicking the edit note link
@@ -551,7 +539,6 @@
this.putEditFormInPlace($target);
};
-
/*
Called in response to clicking the edit note link
@@ -596,7 +583,6 @@
return form.find('.js-note-text').val(form.find('form.edit-note').data('original-note'));
};
-
/*
Called in response to deleting a note of any kind.
@@ -636,7 +622,6 @@
return this.updateNotesCount(-1);
};
-
/*
Called in response to clicking the delete attachment link
@@ -653,7 +638,6 @@
return note.find(".current-note-edit-form").remove();
};
-
/*
Called when clicking on the "reply" button for a diff line.
@@ -673,7 +657,6 @@
return this.setupDiscussionNoteForm(replyLink, form);
};
-
/*
Shows the diff or discussion form and does some setup on it.
@@ -715,7 +698,6 @@
.addClass("discussion-form js-discussion-note-form");
};
-
/*
Called when clicking on the "add a comment" button on the side of a diff line.
@@ -772,7 +754,6 @@
}
};
-
/*
Called in response to "cancel" on a diff note form.
@@ -806,7 +787,6 @@
return this.removeDiscussionNoteForm(form);
};
-
/*
Called after an attachment file has been selected.
@@ -821,7 +801,6 @@
return form.find(".js-attachment-filename").text(filename);
};
-
/*
Called when the tab visibility changes
*/
diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6
index 6dbaae25f2a..5aec9c813fe 100644
--- a/app/assets/javascripts/profile/profile.js.es6
+++ b/app/assets/javascripts/profile/profile.js.es6
@@ -36,6 +36,7 @@
}
onSubmitForm(e) {
+ e.preventDefault();
return this.saveForm();
}
diff --git a/app/assets/javascripts/search_autocomplete.js.es6 b/app/assets/javascripts/search_autocomplete.js.es6
index 480755899fb..6250e75d407 100644
--- a/app/assets/javascripts/search_autocomplete.js.es6
+++ b/app/assets/javascripts/search_autocomplete.js.es6
@@ -69,12 +69,17 @@
search: {
fields: ['text']
},
+ id: this.getSearchText,
data: this.getData.bind(this),
selectable: true,
clicked: this.onClick.bind(this)
});
}
+ getSearchText(selectedObject, el) {
+ return selectedObject.id ? selectedObject.text : '';
+ }
+
getData(term, callback) {
var _this, contents, jqXHR;
_this = this;
@@ -364,7 +369,7 @@
onClick(item, $el, e) {
if (location.pathname.indexOf(item.url) !== -1) {
- e.preventDefault();
+ if (!e.metaKey) e.preventDefault();
if (!this.badgePresent) {
if (item.category === 'Projects') {
this.projectInputEl.val(item.id);
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 6f919de3da7..4ef516af8c8 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -39,29 +39,39 @@
}
ShortcutsIssuable.prototype.replyWithSelectedText = function() {
- var quote, replyField, selected, separator;
- if (window.getSelection) {
- selected = window.getSelection().toString();
- replyField = $('.js-main-target-form #note_note');
- if (selected.trim() === "") {
- return;
- }
- // Put a '>' character before each non-empty line in the selection
- quote = _.map(selected.split("\n"), function(val) {
- if (val.trim() !== '') {
- return "> " + val + "\n";
- }
- });
- // If replyField already has some content, add a newline before our quote
- separator = replyField.val().trim() !== "" && "\n" || '';
- replyField.val(function(_, current) {
- return current + separator + quote.join('') + "\n";
- });
- // Trigger autosave for the added text
- replyField.trigger('input');
- // Focus the input field
- return replyField.focus();
+ var quote, replyField, documentFragment, selected, separator;
+
+ documentFragment = window.gl.utils.getSelectedFragment();
+ if (!documentFragment) return;
+
+ // If the documentFragment contains more than just Markdown, don't copy as GFM.
+ if (documentFragment.querySelector('.md, .wiki')) return;
+
+ selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment);
+
+ replyField = $('.js-main-target-form #note_note');
+ if (selected.trim() === "") {
+ return;
}
+ quote = _.map(selected.split("\n"), function(val) {
+ return ("> " + val).trim() + "\n";
+ });
+ // If replyField already has some content, add a newline before our quote
+ separator = replyField.val().trim() !== "" && "\n\n" || '';
+ replyField.val(function(_, current) {
+ return current + separator + quote.join('') + "\n";
+ });
+
+ // Trigger autosave
+ replyField.trigger('input');
+
+ // Trigger autosize
+ var event = document.createEvent('Event');
+ event.initEvent('autosize:update', true, false);
+ replyField.get(0).dispatchEvent(event);
+
+ // Focus the input field
+ return replyField.focus();
};
ShortcutsIssuable.prototype.editIssue = function() {
diff --git a/app/assets/javascripts/version_check_image.js.es6 b/app/assets/javascripts/version_check_image.js.es6
new file mode 100644
index 00000000000..1fa2b5ac399
--- /dev/null
+++ b/app/assets/javascripts/version_check_image.js.es6
@@ -0,0 +1,10 @@
+(() => {
+ class VersionCheckImage {
+ static bindErrorEvent(imageElement) {
+ imageElement.off('error').on('error', () => imageElement.hide());
+ }
+ }
+
+ window.gl = window.gl || {};
+ gl.VersionCheckImage = VersionCheckImage;
+})();
diff --git a/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6
index ad5cb30cc42..b195b0ef3ba 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/pipeline_actions.js.es6
@@ -22,47 +22,51 @@
<div class="controls pull-right">
<div class="btn-group inline">
<div class="btn-group">
- <a
+ <button
v-if='actions'
- class="dropdown-toggle btn btn-default js-pipeline-dropdown-manual-actions"
+ class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
data-toggle="dropdown"
title="Manual build"
- alt="Manual Build"
+ data-placement="top"
+ data-toggle="dropdown"
+ aria-label="Manual build"
>
- <span v-html='svgs.iconPlay'></span>
- <i class="fa fa-caret-down"></i>
- </a>
+ <span v-html='svgs.iconPlay' aria-hidden="true"></span>
+ <i class="fa fa-caret-down" aria-hidden="true"></i>
+ </button>
<ul class="dropdown-menu dropdown-menu-align-right">
<li v-for='action in pipeline.details.manual_actions'>
<a
rel="nofollow"
data-method="post"
:href='action.path'
- title="Manual build"
>
- <span v-html='svgs.iconPlay'></span>
- <span title="Manual build">{{action.name}}</span>
+ <span v-html='svgs.iconPlay' aria-hidden="true"></span>
+ <span>{{action.name}}</span>
</a>
</li>
</ul>
</div>
<div class="btn-group">
- <a
+ <button
v-if='artifacts'
- class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download"
+ class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
+ data-toggle="dropdown"
+ title="Artifacts"
+ data-placement="top"
data-toggle="dropdown"
- type="button"
+ aria-label="Artifacts"
>
- <i class="fa fa-download"></i>
- <i class="fa fa-caret-down"></i>
- </a>
+ <i class="fa fa-download" aria-hidden="true"></i>
+ <i class="fa fa-caret-down" aria-hidden="true"></i>
+ </button>
<ul class="dropdown-menu dropdown-menu-align-right">
<li v-for='artifact in pipeline.details.artifacts'>
<a
rel="nofollow"
:href='artifact.path'
>
- <i class="fa fa-download"></i>
+ <i class="fa fa-download" aria-hidden="true"></i>
<span>{{download(artifact.name)}}</span>
</a>
</li>
@@ -76,9 +80,12 @@
title="Retry"
rel="nofollow"
data-method="post"
+ data-placement="top"
+ data-toggle="dropdown"
:href='pipeline.retry_path'
+ aria-label="Retry"
>
- <i class="fa fa-repeat"></i>
+ <i class="fa fa-repeat" aria-hidden="true"></i>
</a>
<a
v-if='pipeline.flags.cancelable'
@@ -86,10 +93,12 @@
title="Cancel"
rel="nofollow"
data-method="post"
+ data-placement="top"
+ data-toggle="dropdown"
:href='pipeline.cancel_path'
- data-original-title="Cancel"
+ aria-label="Cancel"
>
- <i class="fa fa-remove"></i>
+ <i class="fa fa-remove" aria-hidden="true"></i>
</a>
</div>
</div>
diff --git a/app/assets/javascripts/vue_pipelines_index/stage.js.es6 b/app/assets/javascripts/vue_pipelines_index/stage.js.es6
index 4e85f16ebc5..496df9aaced 100644
--- a/app/assets/javascripts/vue_pipelines_index/stage.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/stage.js.es6
@@ -82,12 +82,13 @@
data-placement="top"
data-toggle="dropdown"
type="button"
+ :aria-label='stage.title'
>
- <span v-html="svg"></span>
- <i class="fa fa-caret-down "></i>
+ <span v-html="svg" aria-hidden="true"></span>
+ <i class="fa fa-caret-down" aria-hidden="true"></i>
</button>
<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
- <div class="arrow-up"></div>
+ <div class="arrow-up" aria-hidden="true"></div>
<div
@click='keepGraph($event)'
:class="dropdownClass"
diff --git a/app/assets/javascripts/vue_pipelines_index/store.js.es6 b/app/assets/javascripts/vue_pipelines_index/store.js.es6
index 1982142853a..9e19b1564dc 100644
--- a/app/assets/javascripts/vue_pipelines_index/store.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/store.js.es6
@@ -4,19 +4,15 @@
((gl) => {
const pageValues = (headers) => {
- const normalizedHeaders = {};
-
- Object.keys(headers).forEach((e) => {
- normalizedHeaders[e.toUpperCase()] = headers[e];
- });
+ const normalized = gl.utils.normalizeHeaders(headers);
const paginationInfo = {
- perPage: +normalizedHeaders['X-PER-PAGE'],
- page: +normalizedHeaders['X-PAGE'],
- total: +normalizedHeaders['X-TOTAL'],
- totalPages: +normalizedHeaders['X-TOTAL-PAGES'],
- nextPage: +normalizedHeaders['X-NEXT-PAGE'],
- previousPage: +normalizedHeaders['X-PREV-PAGE'],
+ perPage: +normalized['X-PER-PAGE'],
+ page: +normalized['X-PAGE'],
+ total: +normalized['X-TOTAL'],
+ totalPages: +normalized['X-TOTAL-PAGES'],
+ nextPage: +normalized['X-NEXT-PAGE'],
+ previousPage: +normalized['X-PREV-PAGE'],
};
return paginationInfo;
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 8392b98f0a7..1d59700543c 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -37,6 +37,8 @@
display: inline-block;
margin-left: 4px;
margin-bottom: 2px;
+ flex-shrink: 0;
+ -webkit-flex-shrink: 0;
&.s16 { margin-right: 4px; }
&.s24 { margin-right: 4px; }
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 407c800feb7..592ef0d647f 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -82,7 +82,12 @@
}
.block-controls {
- float: right;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-justify-content: flex-end;
+ justify-content: flex-end;
+ -webkit-flex: 1;
+ flex: 1;
.control {
float: left;
@@ -282,3 +287,8 @@
}
}
}
+
+.flex-container-block {
+ display: -webkit-flex;
+ display: flex;
+}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 755eddefa42..6bfb9a6d1cb 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -125,7 +125,8 @@
top: 100%;
left: 0;
z-index: 9;
- width: 240px;
+ max-width: 280px;
+ min-width: 240px;
margin-top: 2px;
margin-bottom: 0;
font-size: 14px;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index d957ec64654..e3da467a27c 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -79,6 +79,16 @@
overflow: auto;
}
+%filter-dropdown-item-btn-hover {
+ background-color: $dropdown-hover-color;
+ color: $white-light;
+ text-decoration: none;
+
+ .avatar {
+ border-color: $white-light;
+ }
+}
+
.filter-dropdown-item {
.btn {
border: none;
@@ -103,13 +113,7 @@
&:hover,
&:focus {
- background-color: $dropdown-hover-color;
- color: $white-light;
- text-decoration: none;
-
- .avatar {
- border-color: $white-light;
- }
+ @extend %filter-dropdown-item-btn-hover;
}
}
@@ -128,11 +132,18 @@
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
+
+ &> span {
+ white-space: normal;
+ word-break: break-all;
+ }
}
}
-.hint-dropdown {
- width: 250px;
+.filter-dropdown-item.dropdown-active {
+ .btn {
+ @extend %filter-dropdown-item-btn-hover;
+ }
}
.filter-dropdown-loading {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 1c6698ad0c6..426596027de 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -155,7 +155,8 @@ ul.content-list {
}
> .btn,
- > .btn-group {
+ > .btn-group,
+ > .dropdown.inline {
margin-right: $gl-padding-top;
display: inline-block;
margin-top: 3px;
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index cb923166b25..6f2e746d4b0 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -13,6 +13,8 @@ $dark-main-bg: #1d1f21;
$dark-main-color: #1d1f21;
$dark-line-color: #c5c8c6;
$dark-line-num-color: rgba(255, 255, 255, 0.3);
+$dark-line-num-color-new: #627165;
+$dark-line-num-color-old: #806565;
$dark-diff-not-empty-bg: #557;
$dark-highlight-bg: #ffe792;
$dark-highlight-color: $black;
@@ -89,7 +91,6 @@ $dark-il: #de935f;
.diff-line-num,
.diff-line-num a {
- color: $dark-main-color;
color: $dark-line-num-color;
}
@@ -121,11 +122,21 @@ $dark-il: #de935f;
.diff-line-num.new,
.line_content.new {
@include diff_background($dark-new-bg, $dark-new-idiff, $dark-border);
+
+ &::before,
+ a {
+ color: $dark-line-num-color-new;
+ }
}
.diff-line-num.old,
.line_content.old {
@include diff_background($dark-old-bg, $dark-old-idiff, $dark-border);
+
+ &::before,
+ a {
+ color: $dark-line-num-color-old;
+ }
}
.line_content.match {
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index d8510baad8a..2144a5f7466 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -7,6 +7,8 @@ $monokai-bg: #272822;
$monokai-border: #555;
$monokai-text-color: #f8f8f2;
$monokai-line-num-color: rgba(255, 255, 255, 0.3);
+$monokai-line-num-color-new: #707565;
+$monokai-line-num-color-old: #7e736f;
$monokai-line-empty-bg: #49483e;
$monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
$monokai-diff-border: #808080;
@@ -120,11 +122,21 @@ $monokai-gi: #a6e22e;
.diff-line-num.new,
.line_content.new {
@include diff_background($monokai-new-bg, $monokai-new-idiff, $monokai-diff-border);
+
+ &::before,
+ a {
+ color: $monokai-line-num-color-new;
+ }
}
.diff-line-num.old,
.line_content.old {
@include diff_background($monokai-old-bg, $monokai-old-idiff, $monokai-diff-border);
+
+ &::before,
+ a {
+ color: $monokai-line-num-color-old;
+ }
}
.line_content.match {
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index 874aecb5e16..2cb1d18f12f 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -13,6 +13,8 @@ $solarized-dark-pre-color: #93a1a1;
$solarized-dark-pre-border: #113b46;
$solarized-dark-line-bg: #002b36;
$solarized-dark-line-color: rgba(255, 255, 255, 0.3);
+$solarized-dark-line-color-new: #5a766c;
+$solarized-dark-line-color-old: #7a6c71;
$solarized-dark-highlight: #094554;
$solarized-dark-hll-bg: #174652;
$solarized-dark-c: #586e75;
@@ -124,11 +126,21 @@ $solarized-dark-il: #2aa198;
.diff-line-num.new,
.line_content.new {
@include diff_background($solarized-dark-new-bg, $solarized-dark-new-idiff, $solarized-dark-border);
+
+ &::before,
+ a {
+ color: $solarized-dark-line-color-new;
+ }
}
.diff-line-num.old,
.line_content.old {
@include diff_background($solarized-dark-old-bg, $solarized-dark-old-idiff, $solarized-dark-border);
+
+ &::before,
+ a {
+ color: $solarized-dark-line-color-old;
+ }
}
.line_content.match {
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 499a1c108b8..b72c4326730 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -13,6 +13,9 @@ $solarized-light-pre-bg: #002b36;
$solarized-light-pre-bg: #fdf6e3;
$solarized-light-pre-color: #586e75;
$solarized-light-line-bg: #fdf6e3;
+$solarized-light-line-color: rgba(0, 0, 0, 0.3);
+$solarized-light-line-color-new: #a1a080;
+$solarized-light-line-color-old: #ad9186;
$solarized-light-highlight: #eee8d5;
$solarized-light-hll-bg: #ddd8c5;
$solarized-light-c: #93a1a1;
@@ -98,7 +101,7 @@ $solarized-light-il: #2aa198;
.diff-line-num,
.diff-line-num a {
- color: $black-transparent;
+ color: $solarized-light-line-color;
}
// Code itself
@@ -130,11 +133,21 @@ $solarized-light-il: #2aa198;
.line_content.new {
@include diff_background($solarized-light-new-bg,
$solarized-light-new-idiff, $solarized-light-border);
+
+ &::before,
+ a {
+ color: $solarized-light-line-color-new;
+ }
}
.diff-line-num.old,
.line_content.old {
@include diff_background($solarized-light-old-bg, $solarized-light-old-idiff, $solarized-light-border);
+
+ &::before,
+ a {
+ color: $solarized-light-line-color-old;
+ }
}
.line_content.match {
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index b425c78e0d5..398fbfd3b18 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -108,11 +108,19 @@ $white-gc-bg: #eaf2f5;
&.old {
background-color: $line-number-old;
border-color: $line-removed-dark;
+
+ a {
+ color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%);
+ }
}
&.new {
background-color: $line-number-new;
border-color: $line-added-dark;
+
+ a {
+ color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%);
+ }
}
&.hll:not(.empty-cell) {
@@ -125,6 +133,10 @@ $white-gc-bg: #eaf2f5;
&.old {
background-color: $line-removed;
+ &::before {
+ color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%);
+ }
+
span.idiff {
background-color: $line-removed-dark;
}
@@ -133,6 +145,10 @@ $white-gc-bg: #eaf2f5;
&.new {
background-color: $line-added;
+ &::before {
+ color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%);
+ }
+
span.idiff {
background-color: $line-added-dark;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 93cc5a8cf0a..4ef95d27f4f 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -26,10 +26,6 @@
border: 0;
}
}
-
- .container-fluid {
- @extend .fixed-width-container;
- }
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 45ff9f7ff5f..ab68b360f93 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -420,10 +420,6 @@
.merge-request-tabs-holder {
background-color: $white-light;
- .container-limited {
- max-width: $limited-layout-width;
- }
-
&.affix {
top: 100px;
left: 0;
@@ -433,10 +429,26 @@
@media (max-width: $screen-xs-max) {
right: 0;
}
+
+ .merge-request-tabs-container {
+ padding-left: $gl-padding;
+ padding-right: $gl-padding;
+ }
}
+}
- &:not(.affix) .container-fluid {
- padding-left: 0;
- padding-right: 0;
+.limit-container-width {
+ .merge-request-tabs-container {
+ max-width: $limited-layout-width;
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+.limit-container-width:not(.container-limited) {
+ .merge-request-tabs-holder:not(.affix) {
+ .merge-request-tabs-container {
+ max-width: $limited-layout-width - ($gl-padding * 2);
+ }
}
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 8dff22e32bd..47dfc22d533 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -203,6 +203,10 @@
position: relative;
margin-right: 6px;
+ .tooltip {
+ white-space: nowrap;
+ }
+
.tooltip-inner {
padding: 3px 4px;
}
@@ -210,9 +214,9 @@
&:not(:last-child) {
&::after {
content: '';
- width: 8px;
+ width: 7px;
position: absolute;
- right: -8px;
+ right: -7px;
top: 10px;
border-bottom: 2px solid $border-color;
}
@@ -288,6 +292,10 @@
}
}
}
+
+ .tooltip {
+ white-space: nowrap;
+ }
}
.build-link {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 259e2557993..8b59c20cb65 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -929,8 +929,32 @@ pre.light-well {
.variables-table {
table-layout: fixed;
+ &.table-responsive {
+ border: none;
+ }
+
.variable-key {
- width: 30%;
+ width: 300px;
+ max-width: 300px;
+ overflow: hidden;
+ word-wrap: break-word;
+
+ // override bootstrap
+ white-space: normal!important;
+
+ @media (max-width: $screen-sm-max) {
+ width: 150px;
+ max-width: 150px;
+ }
+ }
+
+ .variable-value {
+ @media(max-width: $screen-xs-max) {
+ width: 150px;
+ max-width: 150px;
+ overflow: hidden;
+ word-wrap: break-word;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 4cce1c363eb..948921efc0b 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -32,6 +32,10 @@
.last-commit {
@include str-truncated(506px);
+ .fa-angle-right {
+ margin-left: 5px;
+ }
+
@media (min-width: $screen-md-min) and (max-width: $screen-md-max) {
@include str-truncated(450px);
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 1b4987dd738..543d5eac504 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -5,7 +5,11 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
def update
- if @application_setting.update_attributes(application_setting_params)
+ successful = ApplicationSettings::UpdateService
+ .new(@application_setting, current_user, application_setting_params)
+ .execute
+
+ if successful
redirect_to admin_application_settings_path,
notice: 'Application settings saved successfully'
else
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 6db4e1dc1bc..d7a45bacd35 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -18,15 +18,14 @@ class AutocompleteController < ApplicationController
if params[:search].blank?
# Include current user if available to filter by "Me"
if params[:current_user].present? && current_user
+ @users = @users.where.not(id: current_user.id)
@users = [current_user, *@users]
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
- @users = [author, *@users] if author
+ @users = [author, *@users].uniq if author
end
-
- @users.uniq!
end
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index a62c6211372..26e17a7553e 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -22,6 +22,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def trending
@projects = filter_projects(Project.trending)
+ @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page])
respond_to do |format|
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 9b45ed6b6af..886934a3f67 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -94,7 +94,7 @@ class Projects::BuildsController < Projects::ApplicationController
private
def build
- @build ||= project.builds.find_by!(id: params[:id]).present(user: current_user)
+ @build ||= project.builds.find_by!(id: params[:id]).present(current_user: current_user)
end
def build_path(build)
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 8714349e27f..70845617d3c 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -109,12 +109,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
def repository
+ wiki? ? project.wiki.repository : project.repository
+ end
+
+ def wiki?
+ return @wiki if defined?(@wiki)
+
_, suffix = project_id_with_suffix
- if suffix == '.wiki.git'
- project.wiki.repository
- else
- project.repository
- end
+ @wiki = suffix == '.wiki.git'
end
def render_not_found
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 9184dcccac5..278098fcc58 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -84,7 +84,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def access
- @access ||= Gitlab::GitAccess.new(user, project, 'http', authentication_abilities: authentication_abilities)
+ @access ||= access_klass.new(user, project, 'http', authentication_abilities: authentication_abilities)
end
def access_check
@@ -102,4 +102,8 @@ class Projects::GitHttpController < Projects::GitHttpClientController
access_check.allowed?
end
+
+ def access_klass
+ @access_klass ||= wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
+ end
end
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 3602b3d5e58..667f4870c7a 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -32,12 +32,6 @@ class Projects::RefsController < Projects::ApplicationController
redirect_to new_path
end
- format.js do
- @ref = params[:ref]
- define_tree_vars
- tree
- render "tree"
- end
end
end
diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb
index aa54ee07bdc..2aa0449c46e 100644
--- a/app/helpers/compare_helper.rb
+++ b/app/helpers/compare_helper.rb
@@ -3,7 +3,7 @@ module CompareHelper
from.present? &&
to.present? &&
from != to &&
- project.feature_available?(:merge_requests, current_user) &&
+ can?(current_user, :create_merge_request, project) &&
project.repository.branch_names.include?(from) &&
project.repository.branch_names.include?(to)
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 77dc9e7d538..926c9703628 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -14,7 +14,7 @@ module GroupsHelper
def group_title(group, name = nil, url = nil)
full_title = ''
- group.parents.each do |parent|
+ group.ancestors.each do |parent|
full_title += link_to(simple_sanitize(parent.name), group_path(parent))
full_title += ' / '.html_safe
end
diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb
index a674564c4ec..456598b4c28 100644
--- a/app/helpers/version_check_helper.rb
+++ b/app/helpers/version_check_helper.rb
@@ -1,7 +1,8 @@
module VersionCheckHelper
def version_status_badge
if Rails.env.production? && current_application_settings.version_check_enabled
- image_tag VersionCheck.new.url
+ image_url = VersionCheck.new.url
+ image_tag image_url, class: 'js-version-status-badge'
end
end
end
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index 0d20c9092c4..46fa6fd9f6d 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -38,6 +38,14 @@ module Emails
mail_answer_thread(@snippet, note_thread_options(recipient_id))
end
+ def note_personal_snippet_email(recipient_id, note_id)
+ setup_note_mail(note_id, recipient_id)
+
+ @snippet = @note.noteable
+ @target_url = snippet_url(@note.noteable)
+ mail_answer_thread(@snippet, note_thread_options(recipient_id))
+ end
+
private
def note_target_url_options
diff --git a/app/models/ability.rb b/app/models/ability.rb
index fa8f8bc3a5f..ad6c588202e 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -22,6 +22,17 @@ class Ability
end
end
+ # Given a list of users and a snippet this method returns the users that can
+ # read the given snippet.
+ def users_that_can_read_personal_snippet(users, snippet)
+ case snippet.visibility_level
+ when Snippet::INTERNAL, Snippet::PUBLIC
+ users
+ when Snippet::PRIVATE
+ users.include?(snippet.author) ? [snippet.author] : []
+ end
+ end
+
# Returns an Array of Issues that can be read by the given user.
#
# issues - The issues to reduce down to those readable by the user.
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 8fab77cda0a..e33a58d3771 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -13,6 +13,49 @@ class ApplicationSetting < ActiveRecord::Base
[\r\n] # any number of newline characters
}x
+ DEFAULTS_CE = {
+ after_sign_up_text: nil,
+ akismet_enabled: false,
+ container_registry_token_expire_delay: 5,
+ default_branch_protection: Settings.gitlab['default_branch_protection'],
+ default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+ default_projects_limit: Settings.gitlab['default_projects_limit'],
+ default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+ disabled_oauth_sign_in_sources: [],
+ domain_whitelist: Settings.gitlab['domain_whitelist'],
+ gravatar_enabled: Settings.gravatar['enabled'],
+ help_page_text: nil,
+ housekeeping_bitmaps_enabled: true,
+ housekeeping_enabled: true,
+ housekeeping_full_repack_period: 50,
+ housekeeping_gc_period: 200,
+ housekeeping_incremental_repack_period: 10,
+ import_sources: Gitlab::ImportSources.values,
+ koding_enabled: false,
+ koding_url: nil,
+ max_artifacts_size: Settings.artifacts['max_size'],
+ max_attachment_size: Settings.gitlab['max_attachment_size'],
+ plantuml_enabled: false,
+ plantuml_url: nil,
+ recaptcha_enabled: false,
+ repository_checks_enabled: true,
+ repository_storages: ['default'],
+ require_two_factor_authentication: false,
+ restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
+ session_expire_delay: Settings.gitlab['session_expire_delay'],
+ send_user_confirmation_email: false,
+ shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
+ shared_runners_text: nil,
+ sidekiq_throttling_enabled: false,
+ sign_in_text: nil,
+ signin_enabled: Settings.gitlab['signin_enabled'],
+ signup_enabled: Settings.gitlab['signup_enabled'],
+ two_factor_grace_period: 48,
+ user_default_external: false
+ }
+
+ DEFAULTS = DEFAULTS_CE
+
serialize :restricted_visibility_levels
serialize :import_sources
serialize :disabled_oauth_sign_in_sources, Array
@@ -163,46 +206,7 @@ class ApplicationSetting < ActiveRecord::Base
end
def self.create_from_defaults
- create(
- default_projects_limit: Settings.gitlab['default_projects_limit'],
- default_branch_protection: Settings.gitlab['default_branch_protection'],
- signup_enabled: Settings.gitlab['signup_enabled'],
- signin_enabled: Settings.gitlab['signin_enabled'],
- gravatar_enabled: Settings.gravatar['enabled'],
- sign_in_text: nil,
- after_sign_up_text: nil,
- help_page_text: nil,
- shared_runners_text: nil,
- restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
- max_attachment_size: Settings.gitlab['max_attachment_size'],
- session_expire_delay: Settings.gitlab['session_expire_delay'],
- default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- domain_whitelist: Settings.gitlab['domain_whitelist'],
- import_sources: Gitlab::ImportSources.values,
- shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
- max_artifacts_size: Settings.artifacts['max_size'],
- require_two_factor_authentication: false,
- two_factor_grace_period: 48,
- recaptcha_enabled: false,
- akismet_enabled: false,
- koding_enabled: false,
- koding_url: nil,
- plantuml_enabled: false,
- plantuml_url: nil,
- repository_checks_enabled: true,
- disabled_oauth_sign_in_sources: [],
- send_user_confirmation_email: false,
- container_registry_token_expire_delay: 5,
- repository_storages: ['default'],
- user_default_external: false,
- sidekiq_throttling_enabled: false,
- housekeeping_enabled: true,
- housekeeping_bitmaps_enabled: true,
- housekeeping_incremental_repack_period: 10,
- housekeeping_full_repack_period: 50,
- housekeeping_gc_period: 200,
- )
+ create(DEFAULTS)
end
def home_page_url_column_exist
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 90bd6490a02..a600f9c14c5 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -51,6 +51,10 @@ module CacheMarkdownField
CACHING_CLASSES.map(&:constantize)
end
+ def skip_project_check?
+ false
+ end
+
extend ActiveSupport::Concern
included do
@@ -112,7 +116,8 @@ module CacheMarkdownField
invalidation_method = "#{html_field}_invalidated?".to_sym
define_method(cache_method) do
- html = Banzai::Renderer.cacheless_render_field(self, markdown_field)
+ options = { skip_project_check: skip_project_check? }
+ html = Banzai::Renderer.cacheless_render_field(self, markdown_field, options)
__send__("#{html_field}=", html)
true
end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 8ab0401d288..ef2c1e5d414 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -49,7 +49,11 @@ module Mentionable
self.class.mentionable_attrs.each do |attr, options|
text = __send__(attr)
- options = options.merge(cache_key: [self, attr], author: author)
+ options = options.merge(
+ cache_key: [self, attr],
+ author: author,
+ skip_project_check: skip_project_check?
+ )
extractor.analyze(text, options)
end
@@ -121,4 +125,8 @@ module Mentionable
def cross_reference_exists?(target)
SystemNoteService.cross_reference_exists?(target, local_reference)
end
+
+ def skip_project_check?
+ false
+ end
end
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 70740c76e43..4865c0a14b1 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -96,6 +96,11 @@ module Participable
participants.merge(ext.users)
- Ability.users_that_can_read_project(participants.to_a, project)
+ case self
+ when PersonalSnippet
+ Ability.users_that_can_read_personal_snippet(participants.to_a, self)
+ else
+ Ability.users_that_can_read_project(participants.to_a, project)
+ end
end
end
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 1108a64c59e..2b93aa30c0f 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -60,6 +60,21 @@ module Routable
joins(:route).where(wheres.join(' OR '))
end
end
+
+ # Builds a relation to find multiple objects that are nested under user membership
+ #
+ # Usage:
+ #
+ # Klass.member_descendants(1)
+ #
+ # Returns an ActiveRecord::Relation.
+ def member_descendants(user_id)
+ joins(:route).
+ joins("INNER JOIN routes r2 ON routes.path LIKE CONCAT(r2.path, '/%')
+ INNER JOIN members ON members.source_id = r2.source_id
+ AND members.source_type = r2.source_type").
+ where('members.user_id = ?', user_id)
+ end
end
private
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index 68385dc47eb..25e2d8ea24e 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -11,10 +11,10 @@ module Taskable
INCOMPLETE = 'incomplete'.freeze
ITEM_PATTERN = /
^
- \s*(?:[-+*]|(?:\d+\.))? # optional list prefix
- \s* # optional whitespace prefix
- (\[\s\]|\[[xX]\]) # checkbox
- (\s.+) # followed by whitespace and some text.
+ \s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list
+ \s+ # whitespace prefix has to be always presented for a list item
+ (\[\s\]|\[[xX]\]) # checkbox
+ (\s.+) # followed by whitespace and some text.
/x
def self.get_tasks(content)
diff --git a/app/models/group.rb b/app/models/group.rb
index 99675ddb366..4cdfd022094 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -201,7 +201,7 @@ class Group < Namespace
end
def members_with_parents
- GroupMember.where(requested_at: nil, source_id: parents.map(&:id).push(id))
+ GroupMember.where(requested_at: nil, source_id: ancestors.map(&:id).push(id))
end
def users_with_parents
diff --git a/app/models/member.rb b/app/models/member.rb
index c585e0b450e..26a6054e00d 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -68,9 +68,9 @@ class Member < ActiveRecord::Base
after_create :send_request, if: :request?, unless: :importing?
after_create :create_notification_setting, unless: [:pending?, :importing?]
after_create :post_create_hook, unless: [:pending?, :importing?]
- after_create :refresh_member_authorized_projects, if: :importing?
after_update :post_update_hook, unless: [:pending?, :importing?]
after_destroy :post_destroy_hook, unless: :pending?
+ after_commit :refresh_member_authorized_projects
delegate :name, :username, :email, to: :user, prefix: true
@@ -147,8 +147,6 @@ class Member < ActiveRecord::Base
member.save
end
- UserProjectAccessChangedService.new(user.id).execute if user.is_a?(User)
-
member
end
@@ -275,23 +273,27 @@ class Member < ActiveRecord::Base
end
def post_create_hook
- UserProjectAccessChangedService.new(user.id).execute
system_hook_service.execute_hooks_for(self, :create)
end
def post_update_hook
- UserProjectAccessChangedService.new(user.id).execute if access_level_changed?
+ # override in sub class
end
def post_destroy_hook
- refresh_member_authorized_projects
system_hook_service.execute_hooks_for(self, :destroy)
end
+ # Refreshes authorizations of the current member.
+ #
+ # This method schedules a job using Sidekiq and as such **must not** be called
+ # in a transaction. Doing so can lead to the job running before the
+ # transaction has been committed, resulting in the job either throwing an
+ # error or not doing any meaningful work.
def refresh_member_authorized_projects
- # If user/source is being destroyed, project access are gonna be destroyed eventually
- # because of DB foreign keys, so we shouldn't bother with refreshing after each
- # member is destroyed through association
+ # If user/source is being destroyed, project access are going to be
+ # destroyed eventually because of DB foreign keys, so we shouldn't bother
+ # with refreshing after each member is destroyed through association
return if destroyed_by_association.present?
UserProjectAccessChangedService.new(user_id).execute
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index cd5b345bae5..6753504acff 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -865,9 +865,11 @@ class MergeRequest < ActiveRecord::Base
paths: paths
)
- active_diff_notes.each do |note|
- service.execute(note)
- Gitlab::Timeless.timeless(note, &:save)
+ transaction do
+ active_diff_notes.each do |note|
+ service.execute(note)
+ Gitlab::Timeless.timeless(note, &:save)
+ end
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index dd33975731f..67d8c1c2e4c 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -4,6 +4,7 @@ class Namespace < ActiveRecord::Base
include CacheMarkdownField
include Sortable
include Gitlab::ShellAdapter
+ include Gitlab::CurrentSettings
include Routable
cache_markdown_field :description, pipeline: :description
@@ -176,6 +177,10 @@ class Namespace < ActiveRecord::Base
end
end
+ def shared_runners_enabled?
+ projects.with_shared_runners.any?
+ end
+
def full_name
@full_name ||=
if parent
@@ -185,8 +190,26 @@ class Namespace < ActiveRecord::Base
end
end
- def parents
- @parents ||= parent ? parent.parents + [parent] : []
+ # Scopes the model on ancestors of the record
+ def ancestors
+ if parent_id
+ path = route.path
+ paths = []
+
+ until path.blank?
+ path = path.rpartition('/').first
+ paths << path
+ end
+
+ self.class.joins(:route).where('routes.path IN (?)', paths).reorder('routes.path ASC')
+ else
+ self.class.none
+ end
+ end
+
+ # Scopes the model on direct and indirect children of the record
+ def descendants
+ self.class.joins(:route).where('routes.path LIKE ?', "#{route.path}/%").reorder('routes.path ASC')
end
private
diff --git a/app/models/note.rb b/app/models/note.rb
index 0c1b05dabf2..bf090a0438c 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -43,7 +43,8 @@ class Note < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true
delegate :title, to: :noteable, allow_nil: true
- validates :note, :project, presence: true
+ validates :note, presence: true
+ validates :project, presence: true, unless: :for_personal_snippet?
# Attachments are deprecated and are handled by Markdown uploader
validates :attachment, file_size: { maximum: :max_attachment_size }
@@ -53,7 +54,7 @@ class Note < ActiveRecord::Base
validates :commit_id, presence: true, if: :for_commit?
validates :author, presence: true
- validate unless: [:for_commit?, :importing?] do |note|
+ validate unless: [:for_commit?, :importing?, :for_personal_snippet?] do |note|
unless note.noteable.try(:project) == note.project
errors.add(:invalid_project, 'Note and noteable project mismatch')
end
@@ -83,7 +84,7 @@ class Note < ActiveRecord::Base
after_initialize :ensure_discussion_id
before_validation :nullify_blank_type, :nullify_blank_line_code
before_validation :set_discussion_id
- after_save :keep_around_commit
+ after_save :keep_around_commit, unless: :for_personal_snippet?
class << self
def model_name
@@ -165,6 +166,14 @@ class Note < ActiveRecord::Base
noteable_type == "Snippet"
end
+ def for_personal_snippet?
+ noteable.is_a?(PersonalSnippet)
+ end
+
+ def skip_project_check?
+ for_personal_snippet?
+ end
+
# override to return commits, which are not active record
def noteable
if for_commit?
@@ -220,6 +229,10 @@ class Note < ActiveRecord::Base
note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
end
+ def to_ability_name
+ for_personal_snippet? ? 'personal_snippet' : noteable_type.underscore
+ end
+
private
def keep_around_commit
diff --git a/app/models/project.rb b/app/models/project.rb
index cd35601d76b..59faf35e051 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -224,6 +224,7 @@ class Project < ActiveRecord::Base
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
scope :with_statistics, -> { includes(:statistics) }
+ scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
# "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) {
@@ -1096,12 +1097,20 @@ class Project < ActiveRecord::Base
project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
end
+ def shared_runners_available?
+ shared_runners_enabled?
+ end
+
+ def shared_runners
+ shared_runners_available? ? Ci::Runner.shared : Ci::Runner.none
+ end
+
def any_runners?(&block)
if runners.active.any?(&block)
return true
end
- shared_runners_enabled? && Ci::Runner.shared.active.any?(&block)
+ shared_runners.active.any?(&block)
end
def valid_runners_token?(token)
diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb
index 6149c35cc61..5cb6b0c527d 100644
--- a/app/models/project_group_link.rb
+++ b/app/models/project_group_link.rb
@@ -16,8 +16,7 @@ class ProjectGroupLink < ActiveRecord::Base
validates :group_access, inclusion: { in: Gitlab::Access.values }, presence: true
validate :different_group
- after_create :refresh_group_members_authorized_projects
- after_destroy :refresh_group_members_authorized_projects
+ after_commit :refresh_group_members_authorized_projects
def self.access_options
Gitlab::Access.options
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 43dba86e5ed..d77b7692d75 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1188,7 +1188,18 @@ class Repository
end
def tags_sorted_by_committed_date
- tags.sort_by { |tag| tag.dereferenced_target.committed_date }
+ tags.sort_by do |tag|
+ # Annotated tags can point to any object (e.g. a blob), but generally
+ # tags point to a commit. If we don't have a commit, then just default
+ # to putting the tag at the end of the list.
+ target = tag.dereferenced_target
+
+ if target
+ target.committed_date
+ else
+ Time.now
+ end
+ end
end
def keep_around_ref_name(sha)
diff --git a/app/models/route.rb b/app/models/route.rb
index caf596efa79..dd171fdb069 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -8,15 +8,16 @@ class Route < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
- after_update :rename_children, if: :path_changed?
+ after_update :rename_descendants, if: :path_changed?
- def rename_children
+ def rename_descendants
# We update each row separately because MySQL does not have regexp_replace.
# rubocop:disable Rails/FindEach
Route.where('path LIKE ?', "#{path_was}/%").each do |route|
# Note that update column skips validation and callbacks.
- # We need this to avoid recursive call of rename_children method
+ # We need this to avoid recursive call of rename_descendants method
route.update_column(:path, route.path.sub(path_was, path))
end
+ # rubocop:enable Rails/FindEach
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 06dd98a3188..54f5388eb2c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -179,8 +179,8 @@ class User < ActiveRecord::Base
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members WHERE user_id IS NOT NULL AND requested_at IS NULL)') }
scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) }
- scope :order_recent_sign_in, -> { reorder(last_sign_in_at: :desc) }
- scope :order_oldest_sign_in, -> { reorder(last_sign_in_at: :asc) }
+ scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('last_sign_in_at', 'DESC')) }
+ scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('last_sign_in_at', 'ASC')) }
def self.with_two_factor
joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
@@ -439,6 +439,15 @@ class User < ActiveRecord::Base
Group.where("namespaces.id IN (#{union.to_sql})")
end
+ def nested_groups
+ Group.member_descendants(id)
+ end
+
+ def nested_projects
+ Project.joins(:namespace).where('namespaces.parent_id IS NOT NULL').
+ member_descendants(id)
+ end
+
def refresh_authorized_projects
Users::RefreshAuthorizedProjectsService.new(self).execute
end
diff --git a/app/presenters/README.md b/app/presenters/README.md
index 3edd63451e7..a4d592b54d6 100644
--- a/app/presenters/README.md
+++ b/app/presenters/README.md
@@ -113,7 +113,7 @@ detects the presenter based on the presented subject's class.
class Projects::LabelsController < Projects::ApplicationController
def edit
@label = Gitlab::View::Presenter::Factory
- .new(@label, user: current_user)
+ .new(@label, current_user: current_user)
.fabricate!
end
end
@@ -132,7 +132,7 @@ and then in the controller:
```ruby
class Projects::LabelsController < Projects::ApplicationController
def edit
- @label = @label.present(user: current_user)
+ @label = @label.present(current_user: current_user)
end
end
```
@@ -147,7 +147,7 @@ end
You can also present the model in the view:
```ruby
-- label = @label.present(current_user)
+- label = @label.present(current_user: current_user)
%div{ class: label.text_color }
= render partial: label, label: label
diff --git a/app/services/application_settings/base_service.rb b/app/services/application_settings/base_service.rb
new file mode 100644
index 00000000000..2bcc7d7c08b
--- /dev/null
+++ b/app/services/application_settings/base_service.rb
@@ -0,0 +1,7 @@
+module ApplicationSettings
+ class BaseService < ::BaseService
+ def initialize(application_setting, user, params = {})
+ @application_setting, @current_user, @params = application_setting, user, params.dup
+ end
+ end
+end
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
new file mode 100644
index 00000000000..61589a07250
--- /dev/null
+++ b/app/services/application_settings/update_service.rb
@@ -0,0 +1,7 @@
+module ApplicationSettings
+ class UpdateService < ApplicationSettings::BaseService
+ def execute
+ @application_setting.update(@params)
+ end
+ end
+end
diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb
index 74b5ebf372b..6f03bf2be13 100644
--- a/app/services/ci/register_build_service.rb
+++ b/app/services/ci/register_build_service.rb
@@ -2,48 +2,72 @@ module Ci
# This class responsible for assigning
# proper pending build to runner on runner API request
class RegisterBuildService
- def execute(current_runner)
- builds = Ci::Build.pending.unstarted
+ include Gitlab::CurrentSettings
+ attr_reader :runner
+
+ Result = Struct.new(:build, :valid?)
+
+ def initialize(runner)
+ @runner = runner
+ end
+
+ def execute
builds =
- if current_runner.shared?
- builds.
- # don't run projects which have not enabled shared runners and builds
- joins(:project).where(projects: { shared_runners_enabled: true }).
- joins('LEFT JOIN project_features ON ci_builds.gl_project_id = project_features.project_id').
-
- # this returns builds that are ordered by number of running builds
- # we prefer projects that don't use shared runners at all
- joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.gl_project_id=project_builds.gl_project_id").
- where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
- order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
+ if runner.shared?
+ builds_for_shared_runner
else
- # do run projects which are only assigned to this runner (FIFO)
- builds.where(project: current_runner.projects.with_builds_enabled).order('created_at ASC')
+ builds_for_specific_runner
end
build = builds.find do |build|
- current_runner.can_pick?(build)
+ runner.can_pick?(build)
end
if build
# In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
- build.runner_id = current_runner.id
+ build.runner_id = runner.id
build.run!
end
- build
+ Result.new(build, true)
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
- nil
+ Result.new(build, false)
end
private
+ def builds_for_shared_runner
+ new_builds.
+ # don't run projects which have not enabled shared runners and builds
+ joins(:project).where(projects: { shared_runners_enabled: true }).
+ joins('LEFT JOIN project_features ON ci_builds.gl_project_id = project_features.project_id').
+ where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
+
+ # Implement fair scheduling
+ # this returns builds that are ordered by number of running builds
+ # we prefer projects that don't use shared runners at all
+ joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.gl_project_id=project_builds.gl_project_id").
+ order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
+ end
+
+ def builds_for_specific_runner
+ new_builds.where(project: runner.projects.with_builds_enabled).order('created_at ASC')
+ end
+
def running_builds_for_shared_runners
Ci::Build.running.where(runner: Ci::Runner.shared).
group(:gl_project_id).select(:gl_project_id, 'count(*) AS running_builds')
end
+
+ def new_builds
+ Ci::Build.pending.unstarted
+ end
+
+ def shared_runner_build_limits_feature_enabled?
+ ENV['DISABLE_SHARED_RUNNER_BUILD_MINUTES_LIMIT'].to_s != 'true'
+ end
end
end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index cdd765c85eb..b4f8b33d564 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -3,9 +3,10 @@ module Notes
def execute
merge_request_diff_head_sha = params.delete(:merge_request_diff_head_sha)
- note = project.notes.new(params)
- note.author = current_user
- note.system = false
+ note = Note.new(params)
+ note.project = project
+ note.author = current_user
+ note.system = false
if note.award_emoji?
noteable = note.noteable
diff --git a/app/services/notes/post_process_service.rb b/app/services/notes/post_process_service.rb
index e4cd3fc7833..6a10e172483 100644
--- a/app/services/notes/post_process_service.rb
+++ b/app/services/notes/post_process_service.rb
@@ -10,6 +10,9 @@ module Notes
# Skip system notes, like status changes and cross-references and awards
unless @note.system?
EventCreateService.new.leave_note(@note, @note.author)
+
+ return if @note.for_personal_snippet?
+
@note.create_cross_references!
execute_note_hooks
end
diff --git a/app/services/notes/slash_commands_service.rb b/app/services/notes/slash_commands_service.rb
index aaea9717fc4..56913568cae 100644
--- a/app/services/notes/slash_commands_service.rb
+++ b/app/services/notes/slash_commands_service.rb
@@ -12,7 +12,7 @@ module Notes
def self.supported?(note, current_user)
noteable_update_service(note) &&
current_user &&
- current_user.can?(:"update_#{note.noteable_type.underscore}", note.noteable)
+ current_user.can?(:"update_#{note.to_ability_name}", note.noteable)
end
def supported?(note)
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index c3b61e68eab..f74e6cac174 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -178,8 +178,15 @@ class NotificationService
recipients = []
mentioned_users = note.mentioned_users
+
+ ability, subject = if note.for_personal_snippet?
+ [:read_personal_snippet, note.noteable]
+ else
+ [:read_project, note.project]
+ end
+
mentioned_users.select! do |user|
- user.can?(:read_project, note.project)
+ user.can?(ability, subject)
end
# Add all users participating in the thread (author, assignee, comment authors)
@@ -192,11 +199,13 @@ class NotificationService
recipients = recipients.concat(participants)
- # Merge project watchers
- recipients = add_project_watchers(recipients, note.project)
+ unless note.for_personal_snippet?
+ # Merge project watchers
+ recipients = add_project_watchers(recipients, note.project)
- # Merge project with custom notification
- recipients = add_custom_notifications(recipients, note.project, :new_note)
+ # Merge project with custom notification
+ recipients = add_custom_notifications(recipients, note.project, :new_note)
+ end
# Reject users with Mention notification level, except those mentioned in _this_ note.
recipients = reject_mention_users(recipients - mentioned_users, note.project)
@@ -211,8 +220,7 @@ class NotificationService
recipients.delete(note.author)
recipients = recipients.uniq
- # build notify method like 'note_commit_email'
- notify_method = "note_#{note.noteable_type.underscore}_email".to_sym
+ notify_method = "note_#{note.to_ability_name}_email".to_sym
recipients.each do |recipient|
mailer.send(notify_method, recipient.id, note.id).deliver_later
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 159f46cd465..c7cce0c55b9 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -22,17 +22,7 @@ module Projects
return @project
end
- # Set project name from path
- if @project.name.present? && @project.path.present?
- # if both name and path set - everything is ok
- elsif @project.path.present?
- # Set project name from path
- @project.name = @project.path.dup
- elsif @project.name.present?
- # For compatibility - set path from name
- # TODO: remove this in 8.0
- @project.path = @project.name.dup.parameterize
- end
+ set_project_name_from_path
# get namespace id
namespace_id = params[:namespace_id]
@@ -144,5 +134,19 @@ module Projects
service.save!
end
end
+
+ def set_project_name_from_path
+ # Set project name from path
+ if @project.name.present? && @project.path.present?
+ # if both name and path set - everything is ok
+ elsif @project.path.present?
+ # Set project name from path
+ @project.name = @project.path.dup
+ elsif @project.name.present?
+ # For compatibility - set path from name
+ # TODO: remove this in 8.0
+ @project.path = @project.name.dup.parameterize
+ end
+ end
end
end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 842e23eb6b6..55d9cb13ae4 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -22,6 +22,8 @@ module Projects
if project.update_attributes(params.except(:default_branch))
if project.previous_changes.include?('path')
project.rename_repo
+ else
+ system_hook_service.execute_hooks_for(project, :update)
end
success
diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb
index 2469b4f0d7c..d7a6804ee88 100644
--- a/app/services/user_project_access_changed_service.rb
+++ b/app/services/user_project_access_changed_service.rb
@@ -4,6 +4,6 @@ class UserProjectAccessChangedService
end
def execute
- AuthorizedProjectsWorker.bulk_perform_async(@user_ids.map { |id| [id] })
+ AuthorizedProjectsWorker.bulk_perform_and_wait(@user_ids.map { |id| [id] })
end
end
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 2d211d5ebbe..fad741531ea 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -118,7 +118,8 @@ module Users
user.personal_projects.select("#{user.id} AS user_id, projects.id AS project_id, #{Gitlab::Access::MASTER} AS access_level"),
user.groups_projects.select_for_project_authorization,
user.projects.select_for_project_authorization,
- user.groups.joins(:shared_projects).select_for_project_authorization
+ user.groups.joins(:shared_projects).select_for_project_authorization,
+ user.nested_projects.select_for_project_authorization
]
Gitlab::SQL::Union.new(relations)
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 3132d157f29..2269fb1fd8c 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -4,7 +4,7 @@
- if @broadcast_message.message.present?
= render_broadcast_message(@broadcast_message)
- else
- = "Your message here"
+ Your message here
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f|
= form_errors(@broadcast_message)
diff --git a/app/views/admin/identities/_identity.html.haml b/app/views/admin/identities/_identity.html.haml
index 7362d904b94..8c658905bd6 100644
--- a/app/views/admin/identities/_identity.html.haml
+++ b/app/views/admin/identities/_identity.html.haml
@@ -1,6 +1,6 @@
%tr
%td
- = "#{Gitlab::OAuth::Provider.label_for(identity.provider)} (#{identity.provider})"
+ #{Gitlab::OAuth::Provider.label_for(identity.provider)} (#{identity.provider})
%td
= identity.extern_uid
%td
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 37bb6a3b0e0..124f970524e 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -11,7 +11,7 @@
that for future communication.
%br
Registration token is
- %code{ id: 'runners-token' } #{current_application_settings.runners_registration_token}
+ %code#runners-token= current_application_settings.runners_registration_token
.bs-callout.clearfix
.pull-left
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index ca503e35623..39e103e3062 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -100,7 +100,7 @@
%td.build-link
- if project
= link_to ci_status_path(build.pipeline) do
- %strong #{build.pipeline.short_sha}
+ %strong= build.pipeline.short_sha
%td.timestamp
- if build.finished_at
diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml
index bfc6142067a..2e5f120c4e4 100644
--- a/app/views/admin/system_info/show.html.haml
+++ b/app/views/admin/system_info/show.html.haml
@@ -10,7 +10,7 @@
%h4 CPU
.data
- if @cpus
- %h1= "#{@cpus.length} cores"
+ %h1 #{@cpus.length} cores
- else
= icon('warning', class: 'text-warning')
Unable to collect CPU info
@@ -19,7 +19,7 @@
%h4 Memory
.data
- if @memory
- %h1= "#{number_to_human_size(@memory.active_bytes)} / #{number_to_human_size(@memory.total_bytes)}"
+ %h1 #{number_to_human_size(@memory.active_bytes)} / #{number_to_human_size(@memory.total_bytes)}
- else
= icon('warning', class: 'text-warning')
Unable to collect memory info
@@ -28,6 +28,6 @@
%h4 Disks
.data
- @disks.each do |disk|
- %h1= "#{number_to_human_size(disk[:bytes_used])} / #{number_to_human_size(disk[:bytes_total])}"
- %p= "#{disk[:disk_name]}"
- %p= "#{disk[:mount_path]}"
+ %h1 #{number_to_human_size(disk[:bytes_used])} / #{number_to_human_size(disk[:bytes_total])}
+ %p= disk[:disk_name]
+ %p= disk[:mount_path]
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index a71240986c9..76b1291fe10 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -186,6 +186,6 @@
- if @user.solo_owned_groups.present?
%p
This user is currently an owner in these groups:
- %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
+ %strong= @user.solo_owned_groups.map(&:name).join(', ')
%p
You must transfer ownership or delete these groups before you can delete this user.
diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml
index 95eb9a57152..b0bee1c6204 100644
--- a/app/views/ci/lints/show.html.haml
+++ b/app/views/ci/lints/show.html.haml
@@ -13,7 +13,7 @@
.file-holder
.file-title.clearfix
Content of .gitlab-ci.yml
- #ci-editor.ci-editor #{@content}
+ #ci-editor.ci-editor= @content
= text_area_tag(:content, @content, class: 'hidden form-control span1', rows: 7, require: true)
.col-sm-12
.pull-left.prepend-top-10
diff --git a/app/views/ci/status/_badge.html.haml b/app/views/ci/status/_badge.html.haml
index 601fb7f0f3f..c00c7f7407e 100644
--- a/app/views/ci/status/_badge.html.haml
+++ b/app/views/ci/status/_badge.html.haml
@@ -1,7 +1,8 @@
- status = local_assigns.fetch(:status)
+- link = local_assigns.fetch(:link, true)
- css_classes = "ci-status ci-#{status.group}"
-- if status.has_details?
+- if link && status.has_details?
= link_to status.details_path, class: css_classes do
= custom_icon(status.icon)
= status.text
diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml
index 2bce2780484..6f5d4bf2a2f 100644
--- a/app/views/discussions/_discussion.html.haml
+++ b/app/views/discussions/_discussion.html.haml
@@ -1,6 +1,9 @@
- expanded = discussion.expanded?
%li.note.note-discussion.timeline-entry
.timeline-entry-inner
+ .timeline-icon
+ = link_to user_path(discussion.author) do
+ = image_tag avatar_icon(discussion.author), class: "avatar s40"
.timeline-content
.discussion.js-toggle-container{ class: discussion.id, data: { discussion_id: discussion.id } }
.discussion-header
diff --git a/app/views/discussions/_parallel_diff_discussion.html.haml b/app/views/discussions/_parallel_diff_discussion.html.haml
index ef16b516e2c..3a19e021643 100644
--- a/app/views/discussions/_parallel_diff_discussion.html.haml
+++ b/app/views/discussions/_parallel_diff_discussion.html.haml
@@ -6,7 +6,7 @@
.content{ class: ('hide' unless discussion_left.expanded?) }
= render "discussions/notes", discussion: discussion_left, line_type: 'old'
- else
- %td.notes_line.old= ""
+ %td.notes_line.old= ("")
%td.notes_content.parallel.old
.content
@@ -16,6 +16,6 @@
.content{ class: ('hide' unless discussion_right.expanded?) }
= render "discussions/notes", discussion: discussion_right, line_type: 'new'
- else
- %td.notes_line.new= ""
+ %td.notes_line.new= ("")
%td.notes_content.parallel.new
.content
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index 2a0e301c8dd..a196561f381 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -10,7 +10,7 @@
%p
= icon("exclamation-triangle fw")
You are an admin, which means granting access to
- %strong #{@pre_auth.client.name}
+ %strong= @pre_auth.client.name
will allow them to interact with GitLab as an admin as well. Proceed with caution.
- if @pre_auth.scopes
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index bc5d3c797ac..f4c432a095a 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -25,7 +25,7 @@
.panel.panel-default
.panel-heading
Users with access to
- %strong #{@group.name}
+ %strong= @group.name
%span.badge= @members.total_count
%ul.content-list
= render partial: 'shared/members/member', collection: @members, as: :member
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index b4aa4f24d9e..6ad03a60b3a 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -18,7 +18,7 @@
.row-content-block.second-block
Only issues from the
- %strong #{@group.name}
+ %strong= @group.name
group are listed here.
- if current_user
To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index dbbdb583a24..af73554086b 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -10,7 +10,7 @@
.row-content-block.second-block
Only merge requests from
- %strong #{@group.name}
+ %strong= @group.name
group are listed here.
- if current_user
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index a8fdbd8c426..cd5388fe402 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -10,7 +10,7 @@
.row-content-block
Only milestones from
- %strong #{@group.name}
+ %strong= @group.name
group are listed here.
.milestones
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index 864c5c0ff95..0e7f0b5ed4f 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -16,7 +16,7 @@
%colgroup.import-jobs-status-col
%thead
%tr
- %th= "From #{provider_title}"
+ %th From #{provider_title}
%th To GitLab
%th Status
%tbody
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index 07338736bac..9999a4362c6 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -37,7 +37,7 @@
%tbody
- @user_map.each do |id, user|
%tr
- %td= id
+ %td= (id)
%td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control'
%td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control'
%td
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index 97e5e51abe0..5de5da5e6a2 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -50,7 +50,7 @@
%td
= repo.name
%td.import-target
- = "#{current_user.username}/#{repo.name}"
+ #{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
Import
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index 9f1507cade6..5e01af008be 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -55,7 +55,7 @@
%td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank"
%td.import-target
- = "#{current_user.username}/#{repo.name}"
+ #{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
Import
diff --git a/app/views/notify/_reassigned_issuable_email.html.haml b/app/views/notify/_reassigned_issuable_email.html.haml
index 56d81b2ed2e..fd35713f79c 100644
--- a/app/views/notify/_reassigned_issuable_email.html.haml
+++ b/app/views/notify/_reassigned_issuable_email.html.haml
@@ -2,9 +2,9 @@
Assignee changed
- if @previous_assignee
from
- %strong #{@previous_assignee.name}
+ %strong= @previous_assignee.name
to
- if issuable.assignee_id
- %strong #{issuable.assignee_name}
+ %strong= issuable.assignee_name
- else
%strong Unassigned
diff --git a/app/views/notify/closed_issue_email.html.haml b/app/views/notify/closed_issue_email.html.haml
index 56c18cd83cd..b7284dd819b 100644
--- a/app/views/notify/closed_issue_email.html.haml
+++ b/app/views/notify/closed_issue_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Issue was closed by #{@updated_by.name}"
+ Issue was closed by #{@updated_by.name}
diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml
index ac703b31edd..bc12e38675f 100644
--- a/app/views/notify/closed_issue_email.text.haml
+++ b/app/views/notify/closed_issue_email.text.haml
@@ -1,3 +1,3 @@
-= "Issue was closed by #{@updated_by.name}"
+Issue was closed by #{@updated_by.name}
Issue ##{@issue.iid}: #{namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)}
diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml
index 81c7c88fc96..44e018304e1 100644
--- a/app/views/notify/closed_merge_request_email.html.haml
+++ b/app/views/notify/closed_merge_request_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name}"
+ Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name}
diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml
index b435067d5a6..d0c96b83976 100644
--- a/app/views/notify/closed_merge_request_email.text.haml
+++ b/app/views/notify/closed_merge_request_email.text.haml
@@ -1,4 +1,4 @@
-= "Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name}"
+Merge Request #{@merge_request.to_reference} was closed by #{@updated_by.name}
Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
diff --git a/app/views/notify/issue_status_changed_email.html.haml b/app/views/notify/issue_status_changed_email.html.haml
index 482c884a9db..b6051b11cea 100644
--- a/app/views/notify/issue_status_changed_email.html.haml
+++ b/app/views/notify/issue_status_changed_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Issue was #{@issue_status} by #{@updated_by.name}"
+ Issue was #{@issue_status} by #{@updated_by.name}
diff --git a/app/views/notify/merge_request_status_email.html.haml b/app/views/notify/merge_request_status_email.html.haml
index 41a320d6bd8..b487e26b122 100644
--- a/app/views/notify/merge_request_status_email.html.haml
+++ b/app/views/notify/merge_request_status_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name}"
+ Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name}
diff --git a/app/views/notify/merge_request_status_email.text.haml b/app/views/notify/merge_request_status_email.text.haml
index 7a5074a1dc3..4c9719ba732 100644
--- a/app/views/notify/merge_request_status_email.text.haml
+++ b/app/views/notify/merge_request_status_email.text.haml
@@ -1,4 +1,4 @@
-= "Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name}"
+Merge Request #{@merge_request.to_reference} was #{@mr_status} by #{@updated_by.name}
Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml
index fbe506d4f4d..0fe54e73313 100644
--- a/app/views/notify/merged_merge_request_email.html.haml
+++ b/app/views/notify/merged_merge_request_email.html.haml
@@ -1,2 +1,2 @@
%p
- = "Merge Request #{@merge_request.to_reference} was merged"
+ Merge Request #{@merge_request.to_reference} was merged
diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml
index bfbae01094f..46c1c9dee0b 100644
--- a/app/views/notify/merged_merge_request_email.text.haml
+++ b/app/views/notify/merged_merge_request_email.text.haml
@@ -1,4 +1,4 @@
-= "Merge Request #{@merge_request.to_reference} was merged"
+Merge Request #{@merge_request.to_reference} was merged
Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
diff --git a/app/views/notify/note_personal_snippet_email.html.haml b/app/views/notify/note_personal_snippet_email.html.haml
new file mode 100644
index 00000000000..2fa2f784661
--- /dev/null
+++ b/app/views/notify/note_personal_snippet_email.html.haml
@@ -0,0 +1 @@
+= render 'note_message'
diff --git a/app/views/notify/note_personal_snippet_email.text.erb b/app/views/notify/note_personal_snippet_email.text.erb
new file mode 100644
index 00000000000..b2a8809a23b
--- /dev/null
+++ b/app/views/notify/note_personal_snippet_email.text.erb
@@ -0,0 +1,8 @@
+New comment for Snippet <%= @snippet.id %>
+
+<%= url_for(snippet_url(@snippet, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index 82c7fe229b8..d9ebbaa2704 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -139,7 +139,7 @@
had
= failed.size
failed
- = "#{'build'.pluralize(failed.size)}."
+ #{'build'.pluralize(failed.size)}.
%tr.warning
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" }
Logs may contain sensitive data. Please consider before forwarding this email.
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index 6dddb3b6373..8add2e18206 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -138,9 +138,9 @@
%a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
= "\##{@pipeline.id}"
successfully completed
- = "#{build_count} #{'build'.pluralize(build_count)}"
+ #{build_count} #{'build'.pluralize(build_count)}
in
- = "#{stage_count} #{'stage'.pluralize(stage_count)}."
+ #{stage_count} #{'stage'.pluralize(stage_count)}.
%tr.footer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
diff --git a/app/views/notify/project_was_not_exported_email.text.haml b/app/views/notify/project_was_not_exported_email.text.haml
index 27785165c2d..6c6902994a1 100644
--- a/app/views/notify/project_was_not_exported_email.text.haml
+++ b/app/views/notify/project_was_not_exported_email.text.haml
@@ -1,6 +1,6 @@
-= "Project #{@project.name} couldn't be exported."
+Project #{@project.name} couldn't be exported.
-= "The errors we encountered were:"
+The errors we encountered were:
- @errors.each do |error|
#{error}
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index 36858fa6f34..c6b1db17f91 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -17,7 +17,7 @@
%ul
- @message.commits.each do |commit|
%li
- %strong #{link_to(commit.short_id, namespace_project_commit_url(@message.project_namespace, @message.project, commit))}
+ %strong= link_to(commit.short_id, namespace_project_commit_url(@message.project_namespace, @message.project, commit))
%div
%span by #{commit.author_name}
%i at #{commit.committed_date.to_s(:iso8601)}
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 72f658d1b68..14b330d16ad 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -102,7 +102,7 @@
= f.text_field :username, required: true, class: 'form-control'
.help-block
Current path:
- = "#{root_url}#{current_user.username}"
+ #{root_url}#{current_user.username}
.prepend-top-default
= f.button class: "btn btn-warning", type: "submit" do
= icon "spinner spin", class: "hidden loading-username"
@@ -128,7 +128,7 @@
- if @user.solo_owned_groups.present?
%p
Your account is currently an owner in these groups:
- %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
+ %strong= @user.solo_owned_groups.map(&:name).join(', ')
%p
You must transfer ownership or delete these groups before you can delete your account.
.append-bottom-default
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index c0c82cde2f6..d551754a2e5 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -62,7 +62,7 @@
%span.help-block
Please click the link in the confirmation email before continuing. It was sent to
= succeed "." do
- %strong #{@user.unconfirmed_email}
+ %strong= @user.unconfirmed_email
%p
= link_to "Resend confirmation e-mail", user_confirmation_path(user: { email: @user.unconfirmed_email }), method: :post
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 085f79de785..23e27c1105c 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -23,7 +23,7 @@
= markdown_toolbar_button({ icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
= markdown_toolbar_button({ icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
.toolbar-group
- %button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
+ %button.toolbar-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
= icon("arrows-alt fw")
.md-write-holder
diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml
index afe2fd7fd7b..1a1327fb53c 100644
--- a/app/views/projects/_merge_request_merge_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_settings.html.haml
@@ -8,7 +8,7 @@
%br
%span.descr
Builds need to be configured to enable this feature.
- = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_build_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
+ = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')
.checkbox
= form.label :only_allow_merge_if_all_discussions_are_resolved do
= form.check_box :only_allow_merge_if_all_discussions_are_resolved
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 1d058daa094..228ac61fc8c 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -34,7 +34,7 @@
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
.file-editor.code
- %pre.js-edit-mode-pane#editor #{params[:content] || local_assigns[:blob_data]}
+ %pre.js-edit-mode-pane#editor= params[:content] || local_assigns[:blob_data]
- if local_assigns[:path]
.js-edit-mode-pane#preview.hide
.center
diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml
index a486b2fe491..f864702d862 100644
--- a/app/views/projects/blob/_image.html.haml
+++ b/app/views/projects/blob/_image.html.haml
@@ -1,8 +1,8 @@
.file-content.image_file
- if blob.svg?
- if blob.size_within_svg_limits?
- - # We need to scrub SVG but we cannot do so in the RawController: it would
- - # be wrong/strange if RawController modified the data.
+ -# We need to scrub SVG but we cannot do so in the RawController: it would
+ -# be wrong/strange if RawController modified the data.
- blob.load_all_data!(@repository)
- blob = sanitize_svg(blob)
%img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}" }
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 61a7ffdd0ab..4924c73cf8e 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -3,7 +3,7 @@
.modal-content
.modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } ×
- %h3.page-title #{title}
+ %h3.page-title= title
.modal-body
= form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal' do
.dropzone
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 04efc2e996c..19ffe73a08d 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -18,7 +18,7 @@
- if @project.protected_branch? branch.name
%span.label.label-success
protected
- .controls.hidden-xs
+ .controls.hidden-xs<
- if merge_project && create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do
Merge Request
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 5f8f56150f9..bd1f2d96f56 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -12,7 +12,7 @@
= form_tag(filter_branches_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
- .dropdown.inline
+ .dropdown.inline>
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
= projects_sort_options_hash[@sort]
diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml
index b15be0d861d..736b485bf06 100644
--- a/app/views/projects/builds/_header.html.haml
+++ b/app/views/projects/builds/_header.html.haml
@@ -1,8 +1,8 @@
.content-block.build-header
.header-content
- = render 'ci/status/badge', status: @build.detailed_status(current_user)
+ = render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
Build
- %strong ##{@build.id}
+ %strong.js-build-id ##{@build.id}
in pipeline
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 324a7f8cd3f..762ff34a9ec 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -1,5 +1,5 @@
- if !project.empty_repo? && can?(current_user, :download_code, project)
- .project-action-button.dropdown.inline
+ .project-action-button.dropdown.inline>
%button.btn{ 'data-toggle' => 'dropdown' }
= icon('download')
= icon("caret-down")
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 520113639b7..c1e496455d1 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -85,7 +85,7 @@
- if build.finished_at
%p.finished-at
= icon("calendar")
- %span #{time_ago_with_tooltip(build.finished_at)}
+ %span= time_ago_with_tooltip(build.finished_at)
%td.coverage
- if coverage && build.try(:coverage)
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index 990bfbcf951..818a70f38f1 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -78,7 +78,7 @@
.btn-group.inline
- if actions.any?
.btn-group
- %button.dropdown-toggle.btn.btn-default.js-pipeline-dropdown-manual-actions{ type: 'button', 'data-toggle' => 'dropdown' }
+ %button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual build', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual build' }
= custom_icon('icon_play')
= icon('caret-down', 'aria-hidden' => 'true')
%ul.dropdown-menu.dropdown-menu-align-right
@@ -89,7 +89,7 @@
%span= build.name
- if artifacts.present?
.btn-group
- %button.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{ type: 'button', 'data-toggle' => 'dropdown' }
+ %button.dropdown-toggle.btn.btn-default.build-artifacts.has-tooltip.js-pipeline-dropdown-download{ type: 'button', title: 'Artifacts', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Artifacts' }
= icon("download")
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
@@ -102,8 +102,8 @@
- if can?(current_user, :update_pipeline, pipeline.project)
.cancel-retry-btns.inline
- if pipeline.retryable?
- = link_to retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
+ = link_to retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn has-tooltip', title: 'Retry', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Retry' , method: :post do
= icon("repeat")
- if pipeline.cancelable?
- = link_to cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
+ = link_to cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-remove has-tooltip', title: 'Cancel', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Cancel' , method: :post do
= icon("remove")
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 08eb0c57f66..6dba42d5226 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,8 +1,7 @@
.page-content-header
.header-main-content
- %strong
+ %strong Commit #{@commit.short_id}
= clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard")
- = @commit.short_id
%span.hidden-xs authored
#{time_ago_with_tooltip(@commit.authored_date)}
%span by
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index fcc367951ad..904cdb5767f 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -2,7 +2,7 @@
- commits, hidden = limited_commits(@commits)
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
- %li.commit-header= "#{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}"
+ %li.commit-header #{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}
%li.commits-row
%ul.content-list.commit-list.table-list.table-wide
= render commits, project: project, ref: ref
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index e77f23c7fd8..d94f23f5a38 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -9,10 +9,13 @@
= render "head"
%div{ class: container_class }
- .row-content-block.second-block.content-component-block
+ .row-content-block.second-block.content-component-block.flex-container-block
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'commits'
+ %ul.breadcrumb.repo-breadcrumb
+ = commits_breadcrumbs
+
.block-controls.hidden-xs.hidden-sm
- if @merge_request.present?
.control
@@ -30,8 +33,6 @@
.control
= link_to namespace_project_commits_path(@project.namespace, @project, @ref, { format: :atom, private_token: current_user.private_token }), title: "Commits Feed", class: 'btn' do
= icon("rss")
- %ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
%div{ id: dom_id(@project) }
%ol#commits-list.list-unstyled.content_list
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 819e9bc15ae..9c8f58d4aea 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -16,9 +16,9 @@
There isn't anything to compare.
%p.slead
- if params[:to] == params[:from]
- %span.label-branch #{params[:from]}
+ %span.label-branch= params[:from]
and
- %span.label-branch #{params[:to]}
+ %span.label-branch= params[:to]
are the same.
- else
You'll need to use different branch names to get a valid comparison.
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
index 9238f232c7e..c468202569f 100644
--- a/app/views/projects/deployments/_deployment.html.haml
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -1,6 +1,6 @@
%tr.deployment
%td
- %strong= "##{deployment.iid}"
+ %strong ##{deployment.iid}
%td
= render 'projects/deployments/commit', deployment: deployment
@@ -8,7 +8,7 @@
%td.build-column
- if deployment.deployable
= link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link' do
- = "#{deployment.deployable.name} (##{deployment.deployable.id})"
+ #{deployment.deployable.name} (##{deployment.deployable.id})
- if deployment.user
by
= user_avatar(user: deployment.user, size: 20)
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
index 52a1ece7d60..b87b79b170e 100644
--- a/app/views/projects/diffs/_content.html.haml
+++ b/app/views/projects/diffs/_content.html.haml
@@ -1,5 +1,5 @@
.diff-content.diff-wrap-lines
- - # Skip all non non-supported blobs
+ -# Skip all non non-supported blobs
- return unless blob.respond_to?(:text?)
- if diff_file.too_large?
.nothing-here-block This diff could not be displayed because it is too large.
diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml
index 90c9a0c6c2b..ddec775b789 100644
--- a/app/views/projects/diffs/_file_header.html.haml
+++ b/app/views/projects/diffs/_file_header.html.haml
@@ -25,4 +25,4 @@
- if diff_file.mode_changed?
%small
- = "#{diff_file.a_mode} → #{diff_file.b_mode}"
+ #{diff_file.a_mode} → #{diff_file.b_mode}
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 1bccaaf5273..ca10921c5e2 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -9,7 +9,7 @@
%span.wrap
.frame{ class: image_diff_class(diff) }
%img{ src: diff.deleted_file ? old_file_raw_path : file_raw_path, alt: diff.new_path }
- %p.image-info= "#{number_to_human_size file.size}"
+ %p.image-info= number_to_human_size(file.size)
- else
.image
.two-up.view
@@ -18,7 +18,7 @@
%a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path)) }
%img{ src: old_file_raw_path, alt: diff.old_path }
%p.image-info.hide
- %span.meta-filesize= "#{number_to_human_size old_file.size}"
+ %span.meta-filesize= number_to_human_size(old_file.size)
|
%b W:
%span.meta-width
@@ -30,7 +30,7 @@
%a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.new_ref, diff.new_path)) }
%img{ src: file_raw_path, alt: diff.new_path }
%p.image-info.hide
- %span.meta-filesize= "#{number_to_human_size file.size}"
+ %span.meta-filesize= number_to_human_size(file.size)
|
%b W:
%span.meta-width
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index b087485aa17..f361204ecac 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -14,7 +14,7 @@
- left_line_code = diff_file.line_code(left)
- left_position = diff_file.position(left)
%td.old_line.diff-line-num{ id: left_line_code, class: left.type, data: { linenumber: left.old_pos } }
- %a{ href: "##{left_line_code}" }= raw(left.old_pos)
+ %a{ href: "##{left_line_code}", data: { linenumber: left.old_pos } }
%td.line_content.parallel.noteable_line{ class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old') }= diff_line_content(left.text)
- else
%td.old_line.diff-line-num.empty-cell
@@ -27,7 +27,7 @@
- right_line_code = diff_file.line_code(right)
- right_position = diff_file.position(right)
%td.new_line.diff-line-num{ id: right_line_code, class: right.type, data: { linenumber: right.new_pos } }
- %a{ href: "##{right_line_code}" }= raw(right.new_pos)
+ %a{ href: "##{right_line_code}", data: { linenumber: right.new_pos } }
%td.line_content.parallel.noteable_line{ class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new') }= diff_line_content(right.text)
- else
%td.old_line.diff-line-num.empty-cell
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index 290f696d582..8e24e28765f 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -2,7 +2,7 @@
.commit-stat-summary
Showing
= link_to '#', class: 'js-toggle-button' do
- %strong #{pluralize(diff_files.size, "changed file")}
+ %strong= pluralize(diff_files.size, "changed file")
with
%strong.cgreen #{diff_files.sum(&:added_lines)} additions
and
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 1d7fdf68cb3..84787155168 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -7,10 +7,17 @@
.project-edit-errors
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f|
%fieldset.append-bottom-0
- .form-group
- = f.label :name, class: 'label-light' do
- Project name
- = f.text_field :name, class: "form-control", id: "project_name_edit"
+ .row
+ .form-group.col-md-9
+ = f.label :name, class: 'label-light', for: 'project_name_edit' do
+ Project name
+ = f.text_field :name, class: "form-control", id: "project_name_edit"
+
+ .form-group.col-md-3
+ = f.label :id, class: 'label-light' do
+ Project ID
+ = f.text_field :id, class: 'form-control', readonly: true
+
.form-group
= f.label :description, class: 'label-light' do
Project description
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index 6c8a6f051a9..f4aa523b32d 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -1,7 +1,7 @@
.top-area
.nav-text
- full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private"
- = "#{pluralize(@total_forks_count, 'fork')}: #{full_count_title}"
+ #{pluralize(@total_forks_count, 'fork')}: #{full_count_title}
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
index 6d7af1685fd..07fb80750d6 100644
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
+++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
@@ -77,7 +77,7 @@
- if generic_commit_status.finished_at
%p.finished-at
= icon("calendar")
- %span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
+ %span= time_ago_with_tooltip(generic_commit_status.finished_at)
%td.coverage
- if coverage && generic_commit_status.try(:coverage)
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index 7e34a89f9ae..c8a82f7bca3 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -11,7 +11,7 @@
%p.lead
Commit statistics for
- %strong #{@ref}
+ %strong= @ref
#{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
.row
@@ -19,19 +19,19 @@
%ul
%li
%p.lead
- %strong #{@commits_graph.commits.size}
+ %strong= @commits_graph.commits.size
commits during
- %strong #{@commits_graph.duration}
+ %strong= @commits_graph.duration
days
%li
%p.lead
Average
- %strong #{@commits_graph.commit_per_day}
+ %strong= @commits_graph.commit_per_day
commits per day
%li
%p.lead
Contributed by
- %strong #{@commits_graph.authors}
+ %strong= @commits_graph.authors
authors
.col-md-6
%div
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 36c6e7a8dad..d3c013b3f21 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -3,9 +3,9 @@
%p.slead
- source_title, target_title = format_mr_branch_names(@merge_request)
From
- %strong.label-branch #{source_title}
+ %strong.label-branch= source_title
%span into
- %strong.label-branch #{target_title}
+ %strong.label-branch= target_title
%span.pull-right
= link_to 'Change branches', mr_change_branches_path(@merge_request)
@@ -43,7 +43,7 @@
#commits.commits.tab-pane.active
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
- if @pipelines.any?
#pipelines.pipelines.tab-pane
= render "projects/merge_requests/show/pipelines"
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 2a7cd3a19d0..9585a9a3ad4 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -52,7 +52,7 @@
= render 'award_emoji/awards_block', awardable: @merge_request, inline: true
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
- %div{ class: container_class }
+ .merge-request-tabs-container
%ul.merge-request-tabs.nav-links.no-top.no-bottom
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
@@ -92,11 +92,11 @@
= render "projects/merge_requests/discussion"
#commits.commits.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
#diffs.diffs.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
.mr-loading-status
= spinner
@@ -115,4 +115,3 @@
});
var mrRefreshWidgetUrl = "#{mr_widget_refresh_url(@merge_request)}";
-
diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml
index b0f3c86fd21..74a7b1dc498 100644
--- a/app/views/projects/merge_requests/show/_versions.html.haml
+++ b/app/views/projects/merge_requests/show/_versions.html.haml
@@ -25,7 +25,7 @@
latest version
- else
version #{version_index(merge_request_diff)}
- .monospace #{short_sha(merge_request_diff.head_commit_sha)}
+ .monospace= short_sha(merge_request_diff.head_commit_sha)
%small
#{number_with_delimiter(merge_request_diff.commits_count)} #{'commit'.pluralize(merge_request_diff.commits_count)},
= time_ago_with_tooltip(merge_request_diff.created_at)
@@ -55,14 +55,14 @@
latest version
- else
version #{version_index(merge_request_diff)}
- .monospace #{short_sha(merge_request_diff.head_commit_sha)}
+ .monospace= short_sha(merge_request_diff.head_commit_sha)
%small
= time_ago_with_tooltip(merge_request_diff.created_at)
%li
= link_to merge_request_version_path(@project, @merge_request, @merge_request_diff), class: ('is-active' unless @start_sha) do
%strong
#{@merge_request.target_branch} (base)
- .monospace #{short_sha(@merge_request_diff.base_commit_sha)}
+ .monospace= short_sha(@merge_request_diff.base_commit_sha)
- if different_base?(@start_version, @merge_request_diff)
.content-block
@@ -72,7 +72,7 @@
= link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do
new commits
from
- %code #{@merge_request.target_branch}
+ %code= @merge_request.target_branch
- unless @merge_request_diff.latest? && !@start_sha
.comments-disabled-notif.content-block
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index c80dc33058d..5faa6c43f9f 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -13,8 +13,8 @@
%span.ci-coverage
- elsif @merge_request.has_ci?
- - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
- - # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API
+ -# Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
+ -# TODO, remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading
- %w[success skipped canceled failed running pending].each do |status|
.ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" }
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 36c388c3318..09339e520dd 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -69,7 +69,7 @@
- if note_editable
.original-note-content.hidden{ data: { post_url: namespace_project_note_path(@project.namespace, @project, note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
#{note.note}
- %textarea.hidden.js-task-list-field.original-task-list #{note.note}
+ %textarea.hidden.js-task-list-field.original-task-list= note.note
.note-awards
= render 'award_emoji/awards_block', awardable: note, inline: false
- if note.system
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index 9738f369a35..c7996077bc7 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -1,7 +1,7 @@
.panel.panel-default
.panel-heading
Group members with access to
- %strong #{@group.name}
+ %strong= @group.name
%span.badge= members.size
- if can?(current_user, :admin_group_member, @group)
.controls
diff --git a/app/views/projects/project_members/_groups.html.haml b/app/views/projects/project_members/_groups.html.haml
index d7f5fa96527..fdeb5f21fbe 100644
--- a/app/views/projects/project_members/_groups.html.haml
+++ b/app/views/projects/project_members/_groups.html.haml
@@ -1,7 +1,7 @@
.panel.panel-default.project-members-groups
.panel-heading
Groups with access to
- %strong #{@project.name}
+ %strong= @project.name
%span.badge= group_links.size
%ul.content-list
= render partial: 'shared/members/group', collection: group_links, as: :group_link
diff --git a/app/views/projects/project_members/_shared_group_members.html.haml b/app/views/projects/project_members/_shared_group_members.html.haml
index 77370c14def..7902ddb1ae9 100644
--- a/app/views/projects/project_members/_shared_group_members.html.haml
+++ b/app/views/projects/project_members/_shared_group_members.html.haml
@@ -5,9 +5,9 @@
.panel.panel-default
.panel-heading
Shared with
- %strong #{shared_group.name}
+ %strong= shared_group.name
group, members with
- %strong #{group_links.human_access}
+ %strong= group_links.human_access
role (#{shared_group_users_count})
- if can?(current_user, :admin_group, shared_group)
.panel-head-actions
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 5292e73be7a..81d57c77edf 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -1,7 +1,7 @@
.panel.panel-default
.panel-heading
Members with access to
- %strong #{@project.name}
+ %strong= @project.name
%span.badge= @project_members.total_count
= form_tag namespace_project_settings_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index 33d5cbff420..79d8d721aa9 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -7,7 +7,7 @@
.oneline
.title
Release notes for tag
- %strong #{@tag.name}
+ %strong= @tag.name
= form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f|
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 51b0939564e..dcff675eafc 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -9,10 +9,10 @@
(checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} for information on how to install it).
%li
Specify the following URL during the Runner setup:
- %code #{ci_root_url(only_path: false)}
+ %code= ci_root_url(only_path: false)
%li
Use the following registration token during setup:
- %code #{@project.runners_token}
+ %code= @project.runners_token
%li
Start the Runner!
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index fc338dcf887..f1a80f1d5e1 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -17,4 +17,4 @@
- disabled_title = @service.disabled_title
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service), class: "btn #{disabled_class}", title: disabled_title
- = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_settings_integrations_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml b/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml
index c929eee3bb9..fcc91be11cd 100644
--- a/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_installation_info.html.haml
@@ -4,4 +4,4 @@
.col-sm-9.col-sm-offset-3
= link_to new_namespace_project_mattermost_path(@project.namespace, @project), class: 'btn btn-lg' do
= custom_icon('mattermost_logo', size: 15)
- = 'Add to Mattermost'
+ Add to Mattermost
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 2c08221565b..6855c463c6d 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -7,10 +7,12 @@
%th.hidden-xs
.pull-left Last commit
.last-commit.hidden-sm.pull-left
+ %i.fa.fa-angle-right
%small.light
- = clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard")
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
+ = clipboard_button(clipboard_text: @commit.id, title: "Copy commit SHA to clipboard")
= time_ago_with_tooltip(@commit.committed_date)
+ \-
= @commit.full_title
%small.commit-history-link-spacer &#124;
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'commit-history-link'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index e25d6a48573..fb0efd85dcd 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -17,6 +17,13 @@
%pre.dark
:preserve
gem install gollum
+ %p
+ It is recommended to install
+ %code github-markdown
+ so that GFM features render locally:
+ %pre.dark
+ :preserve
+ gem install github-markdown
%h3 Clone your wiki
%pre.dark
diff --git a/app/views/search/results/_empty.html.haml b/app/views/search/results/_empty.html.haml
index 05a63016c09..821a39d61f5 100644
--- a/app/views/search/results/_empty.html.haml
+++ b/app/views/search/results/_empty.html.haml
@@ -3,4 +3,4 @@
%h4
= icon('search')
We couldn't find any results matching
- %code #{@search_term}
+ %code= @search_term
diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml
index 07b17bc69c0..2e6adf3027c 100644
--- a/app/views/search/results/_merge_request.html.haml
+++ b/app/views/search/results/_merge_request.html.haml
@@ -2,7 +2,7 @@
%h4
= link_to [merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request] do
%span.term.str-truncated= merge_request.title
- .pull-right #{merge_request.to_reference}
+ .pull-right= merge_request.to_reference
- if merge_request.description.present?
.description.term
= preserve do
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index c9b7bd154af..23ca6479414 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -9,7 +9,7 @@
= link_to user_snippets_path(snippet.author) do
= image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
= snippet.author_name
- %span.light #{time_ago_with_tooltip(snippet.created_at)}
+ %span.light= time_ago_with_tooltip(snippet.created_at)
%h4.snippet-title
- snippet_path = reliable_snippet_path(snippet)
= link_to snippet_path do
diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml
index 027d42396b4..704d1d01a81 100644
--- a/app/views/search/results/_snippet_title.html.haml
+++ b/app/views/search/results/_snippet_title.html.haml
@@ -20,4 +20,4 @@
= link_to user_snippets_path(snippet_title.author) do
= image_tag avatar_icon(snippet_title.author_email), class: "avatar avatar-inline s16", alt: ''
= snippet_title.author_name
- %span.light #{time_ago_with_tooltip(snippet_title.created_at)}
+ %span.light= time_ago_with_tooltip(snippet_title.created_at)
diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml
index e7cb93b17a7..f94f8ffc604 100644
--- a/app/views/shared/_confirm_modal.html.haml
+++ b/app/views/shared/_confirm_modal.html.haml
@@ -14,7 +14,7 @@
To prevent accidental actions we ask you to confirm your intention.
%br
Please type
- %code.js-confirm-danger-match #{phrase}
+ %code.js-confirm-danger-match= phrase
to proceed or close this modal to cancel.
.form-group
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index 39294fe1a09..704893b4d5b 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -6,14 +6,14 @@
= link_to milestones_filter_path(state: 'opened') do
Open
- if @project
- %span.badge #{counts[:opened]}
+ %span.badge= counts[:opened]
%li{ class: milestone_class_for_state(params[:state], 'closed') }>
= link_to milestones_filter_path(state: 'closed') do
Closed
- if @project
- %span.badge #{counts[:closed]}
+ %span.badge= counts[:closed]
%li{ class: milestone_class_for_state(params[:state], 'all') }>
= link_to milestones_filter_path(state: 'all') do
All
- if @project
- %span.badge #{counts[:all]}
+ %span.badge= counts[:all]
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index f9a7aa4e29b..dd9e433491b 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -32,7 +32,7 @@
- if group_member
as
- %span #{group_member.human_access}
+ %span= group_member.human_access
- if group.description.present?
.description
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index c0e8a498316..0a4de709fcd 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -68,7 +68,7 @@
- if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project))
.inline.prepend-left-10
Please review the
- %strong #{link_to 'contribution guidelines', guide_url}
+ %strong= link_to('contribution guidelines', guide_url)
for this project.
- if issuable.new_record?
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index e9644ca0f12..55360dadbc4 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -47,10 +47,6 @@
%li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link
No Assignee
- - if current_user
- %li.filter-dropdown-item{ 'data-value' => current_user.to_reference }
- %button.btn.btn-link
- Assigned to me
%li.divider
%ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
%li.filter-dropdown-item
diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml
index 5074afb63a1..8bbaf431536 100644
--- a/app/views/shared/tokens/_scopes_form.html.haml
+++ b/app/views/shared/tokens/_scopes_form.html.haml
@@ -5,5 +5,5 @@
- scopes.each do |scope|
%fieldset
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}"
- = label_tag "#{prefix}_scopes_#{scope}", scope
- %span= "(#{t(scope, scope: [:doorkeeper, :scopes])})"
+ = label_tag ("#{prefix}_scopes_#{scope}"), scope
+ %span= t(scope, scope: [:doorkeeper, :scopes])
diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml
index f51599212db..b09782749f5 100644
--- a/app/views/users/calendar_activities.html.haml
+++ b/app/views/users/calendar_activities.html.haml
@@ -1,6 +1,6 @@
%h4.prepend-top-20
Contributions for
- %strong #{@calendar_date.to_s(:short)}
+ %strong= @calendar_date.to_s(:short)
- if @events.any?
%ul.bordered-list
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index fb25eed4f37..c3d33d49c1e 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -110,16 +110,16 @@
= spinner
#groups.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
#contributed.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
#projects.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
#snippets.tab-pane
- - # This tab is always loaded via AJAX
+ -# This tab is always loaded via AJAX
.loading-status
= spinner
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index 2badd0680fb..6abbb5a5250 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -2,6 +2,13 @@ class AuthorizedProjectsWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ # Schedules multiple jobs and waits for them to be completed.
+ def self.bulk_perform_and_wait(args_list)
+ job_ids = bulk_perform_async(args_list)
+
+ Gitlab::JobWaiter.new(job_ids).wait
+ end
+
def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'args' => args_list)
end
diff --git a/changelogs/unreleased/25312-search-input-cmd-click-issue.yml b/changelogs/unreleased/25312-search-input-cmd-click-issue.yml
new file mode 100644
index 00000000000..56e03a48692
--- /dev/null
+++ b/changelogs/unreleased/25312-search-input-cmd-click-issue.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent removal of input fields if it is the parent dropdown element
+merge_request: 8397
+author:
diff --git a/changelogs/unreleased/26068_tasklist_issue.yml b/changelogs/unreleased/26068_tasklist_issue.yml
new file mode 100644
index 00000000000..c938351b8a7
--- /dev/null
+++ b/changelogs/unreleased/26068_tasklist_issue.yml
@@ -0,0 +1,4 @@
+---
+title: Don’t count tasks that are not defined as list items correctly
+merge_request: 8526
+author:
diff --git a/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml b/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml
new file mode 100644
index 00000000000..565672917b2
--- /dev/null
+++ b/changelogs/unreleased/26186-diff-view-plus-and-minus-signs-as-part-of-line-number.yml
@@ -0,0 +1,4 @@
+---
+title: Color + and - signs in diffs to increase code legibility
+merge_request:
+author:
diff --git a/changelogs/unreleased/26445-accessible-piplelines-buttons.yml b/changelogs/unreleased/26445-accessible-piplelines-buttons.yml
new file mode 100644
index 00000000000..fb5274e5253
--- /dev/null
+++ b/changelogs/unreleased/26445-accessible-piplelines-buttons.yml
@@ -0,0 +1,4 @@
+---
+title: Improve button accessibility on pipelines page
+merge_request: 8561
+author:
diff --git a/changelogs/unreleased/26447-fix-tab-list-order.yml b/changelogs/unreleased/26447-fix-tab-list-order.yml
new file mode 100644
index 00000000000..351c53bd076
--- /dev/null
+++ b/changelogs/unreleased/26447-fix-tab-list-order.yml
@@ -0,0 +1,4 @@
+---
+title: Fix tab index order on branch commits list page
+merge_request:
+author: Ryan Harris
diff --git a/changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml b/changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml
new file mode 100644
index 00000000000..87ae8233c4a
--- /dev/null
+++ b/changelogs/unreleased/26468-fix-users-sort-in-admin-area.yml
@@ -0,0 +1,4 @@
+---
+title: Fix Sort by Recent Sign-in in Admin Area
+merge_request: 8637
+author: Poornima M
diff --git a/changelogs/unreleased/26775-fix-auto-complete-initial-loading.yml b/changelogs/unreleased/26775-fix-auto-complete-initial-loading.yml
new file mode 100644
index 00000000000..2d4ec482ee0
--- /dev/null
+++ b/changelogs/unreleased/26775-fix-auto-complete-initial-loading.yml
@@ -0,0 +1,4 @@
+---
+title: Fix autocomplete initial undefined state
+merge_request:
+author:
diff --git a/changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml b/changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml
deleted file mode 100644
index 76e9b19b828..00000000000
--- a/changelogs/unreleased/26785-fix-droplab-in-ie-11-v1.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add some basic fixes for IE11/Edge
-merge_request:
-author:
diff --git a/changelogs/unreleased/26844-new-search-bar-performs-a-new-request-for-each-tag.yml b/changelogs/unreleased/26844-new-search-bar-performs-a-new-request-for-each-tag.yml
new file mode 100644
index 00000000000..4678297cfd4
--- /dev/null
+++ b/changelogs/unreleased/26844-new-search-bar-performs-a-new-request-for-each-tag.yml
@@ -0,0 +1,4 @@
+---
+title: Add caching of droplab ajax requests
+merge_request: 8725
+author:
diff --git a/changelogs/unreleased/26947-build-status-self-link.yml b/changelogs/unreleased/26947-build-status-self-link.yml
new file mode 100644
index 00000000000..15c5821874e
--- /dev/null
+++ b/changelogs/unreleased/26947-build-status-self-link.yml
@@ -0,0 +1,4 @@
+---
+title: Add link verification to badge partial in order to render a badge without a link
+merge_request: 8740
+author:
diff --git a/changelogs/unreleased/27013-regression-in-commit-title-bar.yml b/changelogs/unreleased/27013-regression-in-commit-title-bar.yml
new file mode 100644
index 00000000000..7cb5e4b273d
--- /dev/null
+++ b/changelogs/unreleased/27013-regression-in-commit-title-bar.yml
@@ -0,0 +1,4 @@
+---
+title: Fix commit title bar and repository view copy clipboard button order on last commit in repository view
+merge_request:
+author:
diff --git a/changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml b/changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml
new file mode 100644
index 00000000000..f0301c849b6
--- /dev/null
+++ b/changelogs/unreleased/27014-fix-pipeline-tooltip-wrapping.yml
@@ -0,0 +1,4 @@
+---
+title: Fix mini-pipeline stage tooltip text wrapping
+merge_request:
+author:
diff --git a/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml b/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml
new file mode 100644
index 00000000000..b5584749098
--- /dev/null
+++ b/changelogs/unreleased/27021-line-numbers-now-in-copy-pasta-data.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent copying of line numbers in parallel diff view
+merge_request: 8706
+author:
diff --git a/changelogs/unreleased/27044-fix-explore-sorting-on-trending.yml b/changelogs/unreleased/27044-fix-explore-sorting-on-trending.yml
new file mode 100644
index 00000000000..0f0a8940f72
--- /dev/null
+++ b/changelogs/unreleased/27044-fix-explore-sorting-on-trending.yml
@@ -0,0 +1,4 @@
+---
+title: Fix /explore sorting
+merge_request:
+author:
diff --git a/changelogs/unreleased/27066-textarea-border.yml b/changelogs/unreleased/27066-textarea-border.yml
deleted file mode 100644
index e45cb3aced5..00000000000
--- a/changelogs/unreleased/27066-textarea-border.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove blue border from comment box hover
-merge_request:
-author:
diff --git a/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml b/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml
new file mode 100644
index 00000000000..52406bba464
--- /dev/null
+++ b/changelogs/unreleased/27178-update-builds-link-in-project-settings.yml
@@ -0,0 +1,4 @@
+---
+title: Updated builds info link on the project settings page
+merge_request:
+author: Ryan Harris
diff --git a/changelogs/unreleased/27259-label-for-references-the-wrong-associated-text-input.yml b/changelogs/unreleased/27259-label-for-references-the-wrong-associated-text-input.yml
new file mode 100644
index 00000000000..2591f161bc5
--- /dev/null
+++ b/changelogs/unreleased/27259-label-for-references-the-wrong-associated-text-input.yml
@@ -0,0 +1,4 @@
+---
+title: Fix project name label's for reference in project settings
+merge_request: 8795
+author:
diff --git a/changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml b/changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml
new file mode 100644
index 00000000000..9456251025b
--- /dev/null
+++ b/changelogs/unreleased/27277-small-mini-pipeline-graph-glitch-upon-hover.yml
@@ -0,0 +1,4 @@
+---
+title: fixed small mini pipeline graph line glitch
+merge_request: 8804
+author:
diff --git a/changelogs/unreleased/add_project_update_hook.yml b/changelogs/unreleased/add_project_update_hook.yml
new file mode 100644
index 00000000000..915c9538843
--- /dev/null
+++ b/changelogs/unreleased/add_project_update_hook.yml
@@ -0,0 +1,4 @@
+---
+title: Add system hook for when a project is updated (other than rename/transfer)
+merge_request: 5711
+author: Tommy Beadle
diff --git a/changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml b/changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml
new file mode 100644
index 00000000000..77750b55e7e
--- /dev/null
+++ b/changelogs/unreleased/broken_iamge_when_doing_offline_update_check-help_page-.yml
@@ -0,0 +1,4 @@
+---
+title: Hide version check image if there is no internet connection
+merge_request: 8355
+author: Ken Ding
diff --git a/changelogs/unreleased/copy-as-md.yml b/changelogs/unreleased/copy-as-md.yml
new file mode 100644
index 00000000000..637e9dc36e2
--- /dev/null
+++ b/changelogs/unreleased/copy-as-md.yml
@@ -0,0 +1,4 @@
+---
+title: Copying a rendered issue/comment will paste into GFM textareas as actual GFM
+merge_request:
+author:
diff --git a/changelogs/unreleased/display-project-id.yml b/changelogs/unreleased/display-project-id.yml
new file mode 100644
index 00000000000..8705ed28400
--- /dev/null
+++ b/changelogs/unreleased/display-project-id.yml
@@ -0,0 +1,4 @@
+---
+title: Display project ID in project settings
+merge_request: 8572
+author: winniehell
diff --git a/changelogs/unreleased/fix-26518.yml b/changelogs/unreleased/fix-26518.yml
new file mode 100644
index 00000000000..961ac2642fb
--- /dev/null
+++ b/changelogs/unreleased/fix-26518.yml
@@ -0,0 +1,4 @@
+---
+title: Fix access to the wiki code via HTTP when repository feature disabled
+merge_request: 8758
+author:
diff --git a/changelogs/unreleased/fix-cancel-integration-settings.yml b/changelogs/unreleased/fix-cancel-integration-settings.yml
new file mode 100644
index 00000000000..294b0aa5db9
--- /dev/null
+++ b/changelogs/unreleased/fix-cancel-integration-settings.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed services form cancel not redirecting back the integrations settings view
+merge_request: 8843
+author:
diff --git a/changelogs/unreleased/fix_broken_diff_discussions.yml b/changelogs/unreleased/fix_broken_diff_discussions.yml
new file mode 100644
index 00000000000..4551212759f
--- /dev/null
+++ b/changelogs/unreleased/fix_broken_diff_discussions.yml
@@ -0,0 +1,4 @@
+---
+title: Make MR-review-discussions more reliable
+merge_request:
+author:
diff --git a/changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml b/changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml
new file mode 100644
index 00000000000..f60417d185e
--- /dev/null
+++ b/changelogs/unreleased/get-rid-of-water-from-notification_service_spec-to-make-it-DRY.yml
@@ -0,0 +1,4 @@
+---
+title: Make notification_service spec DRYer by making test reusable
+merge_request:
+author: YarNayar
diff --git a/changelogs/unreleased/issue-filter-click-to-search.yml b/changelogs/unreleased/issue-filter-click-to-search.yml
deleted file mode 100644
index c024ea48dc7..00000000000
--- a/changelogs/unreleased/issue-filter-click-to-search.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: allow issue filter bar to be operated with mouse only
-merge_request: 8681
-author:
diff --git a/changelogs/unreleased/issue_27211.yml b/changelogs/unreleased/issue_27211.yml
new file mode 100644
index 00000000000..ad48fec5d85
--- /dev/null
+++ b/changelogs/unreleased/issue_27211.yml
@@ -0,0 +1,4 @@
+---
+title: Remove unused js response from refs controller
+merge_request:
+author:
diff --git a/changelogs/unreleased/label-select-toggle.yml b/changelogs/unreleased/label-select-toggle.yml
new file mode 100644
index 00000000000..af5b4246521
--- /dev/null
+++ b/changelogs/unreleased/label-select-toggle.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed label dropdown toggle text not correctly updating
+merge_request:
+author:
diff --git a/changelogs/unreleased/merge-dropdown-this-context.yml b/changelogs/unreleased/merge-dropdown-this-context.yml
deleted file mode 100644
index 5c4890fcaa2..00000000000
--- a/changelogs/unreleased/merge-dropdown-this-context.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed bug where links in merge dropdown wouldn't work
-merge_request:
-author:
diff --git a/changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml b/changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml
new file mode 100644
index 00000000000..f32b3aea3c8
--- /dev/null
+++ b/changelogs/unreleased/misalinged-discussion-with-no-avatar-26854.yml
@@ -0,0 +1,4 @@
+---
+title: adds avatar for discussion note
+merge_request: 8734
+author:
diff --git a/changelogs/unreleased/mr-tabs-container-offset.yml b/changelogs/unreleased/mr-tabs-container-offset.yml
new file mode 100644
index 00000000000..c5df8abfcf2
--- /dev/null
+++ b/changelogs/unreleased/mr-tabs-container-offset.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed merge requests tab extra margin when fixed to window
+merge_request:
+author:
diff --git a/changelogs/unreleased/newline-eslint-rule.yml b/changelogs/unreleased/newline-eslint-rule.yml
new file mode 100644
index 00000000000..5ce080b6912
--- /dev/null
+++ b/changelogs/unreleased/newline-eslint-rule.yml
@@ -0,0 +1,4 @@
+---
+title: Flag multiple empty lines in eslint, fix offenses.
+merge_request: 8137
+author:
diff --git a/changelogs/unreleased/no_project_notes.yml b/changelogs/unreleased/no_project_notes.yml
new file mode 100644
index 00000000000..6106c027360
--- /dev/null
+++ b/changelogs/unreleased/no_project_notes.yml
@@ -0,0 +1,4 @@
+---
+title: Support notes when a project is not specified (personal snippet notes)
+merge_request: 8468
+author:
diff --git a/changelogs/unreleased/refresh-authorizations-fork-join.yml b/changelogs/unreleased/refresh-authorizations-fork-join.yml
new file mode 100644
index 00000000000..b1349b9447d
--- /dev/null
+++ b/changelogs/unreleased/refresh-authorizations-fork-join.yml
@@ -0,0 +1,4 @@
+---
+title: Fix race conditions for AuthorizedProjectsWorker
+merge_request:
+author:
diff --git a/changelogs/unreleased/revert-filter-assigned-to-me.yml b/changelogs/unreleased/revert-filter-assigned-to-me.yml
new file mode 100644
index 00000000000..37f9d2f5fc4
--- /dev/null
+++ b/changelogs/unreleased/revert-filter-assigned-to-me.yml
@@ -0,0 +1,4 @@
+---
+title: Revert 3f17f29a
+merge_request: 8785
+author:
diff --git a/changelogs/unreleased/sh-fix-annotated-tags-pointing-to-blob.yml b/changelogs/unreleased/sh-fix-annotated-tags-pointing-to-blob.yml
new file mode 100644
index 00000000000..ff2b38f21f2
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-annotated-tags-pointing-to-blob.yml
@@ -0,0 +1,4 @@
+---
+title: Fix Error 500 when repositories contain annotated tags pointing to blobs
+merge_request:
+author:
diff --git a/changelogs/unreleased/small-screen-fullscreen-button.yml b/changelogs/unreleased/small-screen-fullscreen-button.yml
new file mode 100644
index 00000000000..f4c269bc473
--- /dev/null
+++ b/changelogs/unreleased/small-screen-fullscreen-button.yml
@@ -0,0 +1,4 @@
+---
+title: Display fullscreen button on small screens
+merge_request: 5302
+author: winniehell
diff --git a/changelogs/unreleased/tc-only-mr-button-if-allowed.yml b/changelogs/unreleased/tc-only-mr-button-if-allowed.yml
new file mode 100644
index 00000000000..a7f5dcb560c
--- /dev/null
+++ b/changelogs/unreleased/tc-only-mr-button-if-allowed.yml
@@ -0,0 +1,4 @@
+---
+title: Only show Merge Request button when user can create a MR
+merge_request: 8639
+author:
diff --git a/config/database.yml.mysql b/config/database.yml.mysql
index d9702870249..a33e40e8eb3 100644
--- a/config/database.yml.mysql
+++ b/config/database.yml.mysql
@@ -3,8 +3,8 @@
#
production:
adapter: mysql2
- encoding: utf8mb4
- collation: utf8mb4_general_ci
+ encoding: utf8
+ collation: utf8_general_ci
reconnect: false
database: gitlabhq_production
pool: 10
@@ -18,8 +18,8 @@ production:
#
development:
adapter: mysql2
- encoding: utf8mb4
- collation: utf8mb4_general_ci
+ encoding: utf8
+ collation: utf8_general_ci
reconnect: false
database: gitlabhq_development
pool: 5
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 3b8771543e4..e0702e06cc9 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -1,3 +1,117 @@
+# Autoload all classes that we want to instrument, and instrument the methods we
+# need. This takes the Gitlab::Metrics::Instrumentation module as an argument so
+# that we can stub it for testing, as it is only called when metrics are
+# enabled.
+#
+# rubocop:disable Metrics/AbcSize
+def instrument_classes(instrumentation)
+ instrumentation.instrument_instance_methods(Gitlab::Shell)
+
+ instrumentation.instrument_methods(Gitlab::Git)
+
+ Gitlab::Git.constants.each do |name|
+ const = Gitlab::Git.const_get(name)
+
+ next unless const.is_a?(Module)
+
+ instrumentation.instrument_methods(const)
+ instrumentation.instrument_instance_methods(const)
+ end
+
+ # Path to search => prefix to strip from constant
+ paths_to_instrument = {
+ ['app', 'finders'] => ['app', 'finders'],
+ ['app', 'mailers', 'emails'] => ['app', 'mailers'],
+ ['app', 'services', '**'] => ['app', 'services'],
+ ['lib', 'gitlab', 'conflicts'] => ['lib'],
+ ['lib', 'gitlab', 'diff'] => ['lib'],
+ ['lib', 'gitlab', 'email', 'message'] => ['lib'],
+ ['lib', 'gitlab', 'checks'] => ['lib']
+ }
+
+ paths_to_instrument.each do |(path, prefix)|
+ prefix = Rails.root.join(*prefix)
+
+ Dir[Rails.root.join(*path + ['*.rb'])].each do |file_path|
+ path = Pathname.new(file_path).relative_path_from(prefix)
+ const = path.to_s.sub('.rb', '').camelize.constantize
+
+ instrumentation.instrument_methods(const)
+ instrumentation.instrument_instance_methods(const)
+ end
+ end
+
+ instrumentation.instrument_methods(Premailer::Adapter::Nokogiri)
+ instrumentation.instrument_instance_methods(Premailer::Adapter::Nokogiri)
+
+ [
+ :Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository,
+ :Tag, :TagCollection, :Tree
+ ].each do |name|
+ const = Rugged.const_get(name)
+
+ instrumentation.instrument_methods(const)
+ instrumentation.instrument_instance_methods(const)
+ end
+
+ # Instruments all Banzai filters and reference parsers
+ {
+ Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
+ ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
+ }.each do |const_name, path|
+ Dir[path].each do |file|
+ klass = File.basename(file, File.extname(file)).camelize
+ const = Banzai.const_get(const_name).const_get(klass)
+
+ instrumentation.instrument_methods(const)
+ instrumentation.instrument_instance_methods(const)
+ end
+ end
+
+ instrumentation.instrument_methods(Banzai::Renderer)
+ instrumentation.instrument_methods(Banzai::Querying)
+
+ instrumentation.instrument_instance_methods(Banzai::ObjectRenderer)
+ instrumentation.instrument_instance_methods(Banzai::Redactor)
+ instrumentation.instrument_methods(Banzai::NoteRenderer)
+
+ [Issuable, Mentionable, Participable].each do |klass|
+ instrumentation.instrument_instance_methods(klass)
+ instrumentation.instrument_instance_methods(klass::ClassMethods)
+ end
+
+ instrumentation.instrument_methods(Gitlab::ReferenceExtractor)
+ instrumentation.instrument_instance_methods(Gitlab::ReferenceExtractor)
+
+ # Instrument the classes used for checking if somebody has push access.
+ instrumentation.instrument_instance_methods(Gitlab::GitAccess)
+ instrumentation.instrument_instance_methods(Gitlab::GitAccessWiki)
+
+ instrumentation.instrument_instance_methods(API::Helpers)
+
+ instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
+
+ instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet)
+ instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
+
+ [:XML, :HTML].each do |namespace|
+ namespace_mod = Nokogiri.const_get(namespace)
+
+ instrumentation.instrument_methods(namespace_mod)
+ instrumentation.instrument_methods(namespace_mod::Document)
+ end
+
+ instrumentation.instrument_methods(Rinku)
+ instrumentation.instrument_instance_methods(Repository)
+
+ instrumentation.instrument_methods(Gitlab::Highlight)
+ instrumentation.instrument_instance_methods(Gitlab::Highlight)
+
+ # This is a Rails scope so we have to instrument it manually.
+ instrumentation.instrument_method(Project, :visible_to_user)
+end
+# rubocop:enable Metrics/AbcSize
+
if Gitlab::Metrics.enabled?
require 'pathname'
require 'influxdb'
@@ -49,110 +163,7 @@ if Gitlab::Metrics.enabled?
end
Gitlab::Metrics::Instrumentation.configure do |config|
- config.instrument_instance_methods(Gitlab::Shell)
-
- config.instrument_methods(Gitlab::Git)
-
- Gitlab::Git.constants.each do |name|
- const = Gitlab::Git.const_get(name)
-
- next unless const.is_a?(Module)
-
- config.instrument_methods(const)
- config.instrument_instance_methods(const)
- end
-
- # Path to search => prefix to strip from constant
- paths_to_instrument = {
- ['app', 'finders'] => ['app', 'finders'],
- ['app', 'mailers', 'emails'] => ['app', 'mailers'],
- ['app', 'services', '**'] => ['app', 'services'],
- ['lib', 'gitlab', 'conflicts'] => ['lib'],
- ['lib', 'gitlab', 'diff'] => ['lib'],
- ['lib', 'gitlab', 'email', 'message'] => ['lib'],
- ['lib', 'gitlab', 'checks'] => ['lib']
- }
-
- paths_to_instrument.each do |(path, prefix)|
- prefix = Rails.root.join(*prefix)
-
- Dir[Rails.root.join(*path + ['*.rb'])].each do |file_path|
- path = Pathname.new(file_path).relative_path_from(prefix)
- const = path.to_s.sub('.rb', '').camelize.constantize
-
- config.instrument_methods(const)
- config.instrument_instance_methods(const)
- end
- end
-
- config.instrument_methods(Premailer::Adapter::Nokogiri)
- config.instrument_instance_methods(Premailer::Adapter::Nokogiri)
-
- [
- :Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository,
- :Tag, :TagCollection, :Tree
- ].each do |name|
- const = Rugged.const_get(name)
-
- config.instrument_methods(const)
- config.instrument_instance_methods(const)
- end
-
- # Instruments all Banzai filters and reference parsers
- {
- Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
- ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
- }.each do |const_name, path|
- Dir[path].each do |file|
- klass = File.basename(file, File.extname(file)).camelize
- const = Banzai.const_get(const_name).const_get(klass)
-
- config.instrument_methods(const)
- config.instrument_instance_methods(const)
- end
- end
-
- config.instrument_methods(Banzai::Renderer)
- config.instrument_methods(Banzai::Querying)
-
- config.instrument_instance_methods(Banzai::ObjectRenderer)
- config.instrument_instance_methods(Banzai::Redactor)
- config.instrument_methods(Banzai::NoteRenderer)
-
- [Issuable, Mentionable, Participable].each do |klass|
- config.instrument_instance_methods(klass)
- config.instrument_instance_methods(klass::ClassMethods)
- end
-
- config.instrument_methods(Gitlab::ReferenceExtractor)
- config.instrument_instance_methods(Gitlab::ReferenceExtractor)
-
- # Instrument the classes used for checking if somebody has push access.
- config.instrument_instance_methods(Gitlab::GitAccess)
- config.instrument_instance_methods(Gitlab::GitAccessWiki)
-
- config.instrument_instance_methods(API::Helpers)
-
- config.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
-
- config.instrument_instance_methods(Rouge::Plugins::Redcarpet)
- config.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
-
- [:XML, :HTML].each do |namespace|
- namespace_mod = Nokogiri.const_get(namespace)
-
- config.instrument_methods(namespace_mod)
- config.instrument_methods(namespace_mod::Document)
- end
-
- config.instrument_methods(Rinku)
- config.instrument_instance_methods(Repository)
-
- config.instrument_methods(Gitlab::Highlight)
- config.instrument_instance_methods(Gitlab::Highlight)
-
- # This is a Rails scope so we have to instrument it manually.
- config.instrument_method(Project, :visible_to_user)
+ instrument_classes(config)
end
GC::Profiler.enable
diff --git a/config/initializers/rspec_profiling.rb b/config/initializers/rspec_profiling.rb
new file mode 100644
index 00000000000..f462e654b2c
--- /dev/null
+++ b/config/initializers/rspec_profiling.rb
@@ -0,0 +1,14 @@
+module RspecProfilingConnection
+ def establish_connection
+ ::RspecProfiling::Collectors::PSQL::Result.establish_connection(ENV['RSPEC_PROFILING_POSTGRES_URL'])
+ end
+end
+
+if Rails.env.test?
+ RspecProfiling.configure do |config|
+ if ENV['RSPEC_PROFILING_POSTGRES_URL']
+ RspecProfiling::Collectors::PSQL.prepend(RspecProfilingConnection)
+ config.collector = RspecProfiling::Collectors::PSQL
+ end
+ end
+end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 5a7365bb0f6..fa318384405 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -12,6 +12,11 @@ Sidekiq.configure_server do |config|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS']
chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware unless ENV['SIDEKIQ_REQUEST_STORE'] == '0'
+ chain.add Gitlab::SidekiqStatus::ServerMiddleware
+ end
+
+ config.client_middleware do |chain|
+ chain.add Gitlab::SidekiqStatus::ClientMiddleware
end
# Sidekiq-cron: load recurring jobs from gitlab.yml
@@ -46,6 +51,10 @@ end
Sidekiq.configure_client do |config|
config.redis = redis_config_hash
+
+ config.client_middleware do |chain|
+ chain.add Gitlab::SidekiqStatus::ClientMiddleware
+ end
end
# The Sidekiq client API always adds the queue to the Sidekiq queue
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 776c31c9dac..60a1175fe80 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -19,13 +19,11 @@ end
scope(path: 'groups/*id',
controller: :groups,
- constraints: { id: Gitlab::Regex.namespace_route_regex }) do
+ constraints: { id: Gitlab::Regex.namespace_route_regex, format: /(html|json|atom)/ }) do
get :edit, as: :edit_group
get :issues, as: :issues_group
get :merge_requests, as: :merge_requests_group
get :projects, as: :projects_group
get :activity, as: :activity_group
+ get '/', action: :show, as: :group_canonical
end
-
-# Must be last route in this file
-get 'groups/*id' => 'groups#show', as: :group_canonical, constraints: { id: Gitlab::Regex.namespace_route_regex }
diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb
index bba2fc4b186..6f241f6fa4a 100644
--- a/db/fixtures/development/01_admin.rb
+++ b/db/fixtures/development/01_admin.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
User.seed do |s|
s.id = 1
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
index a984eda5ab5..c2b8f7ba819 100644
--- a/db/fixtures/development/04_project.rb
+++ b/db/fixtures/development/04_project.rb
@@ -1,4 +1,4 @@
-require 'sidekiq/testing'
+require './spec/support/sidekiq'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb
index 03da29c4c68..101ff3a1209 100644
--- a/db/fixtures/development/05_users.rb
+++ b/db/fixtures/development/05_users.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
20.times do |i|
begin
diff --git a/db/fixtures/development/06_teams.rb b/db/fixtures/development/06_teams.rb
index 5c2a03fec3f..86e0a38aae1 100644
--- a/db/fixtures/development/06_teams.rb
+++ b/db/fixtures/development/06_teams.rb
@@ -1,4 +1,4 @@
-require 'sidekiq/testing'
+require './spec/support/sidekiq'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb
index 540e4e68259..271bfbc97e0 100644
--- a/db/fixtures/development/07_milestones.rb
+++ b/db/fixtures/development/07_milestones.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
Project.all.each do |project|
5.times do |i|
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 4fa572fca9b..d93d133d157 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
Project.all.each do |project|
10.times do
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index 87fb8e3300d..c04afe97277 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
# Limit the number of merge requests per project to avoid long seeds
MAX_NUM_MERGE_REQUESTS = 10
diff --git a/db/fixtures/development/11_keys.rb b/db/fixtures/development/11_keys.rb
index 8b4bee384e1..51e22137d6f 100644
--- a/db/fixtures/development/11_keys.rb
+++ b/db/fixtures/development/11_keys.rb
@@ -1,12 +1,18 @@
-Gitlab::Seeder.quiet do
- User.first(10).each do |user|
- key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{user.id + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
+require './spec/support/sidekiq'
- user.keys.create(
- title: "Sample key #{user.id}",
- key: key
- )
+# Creating keys runs a gitlab-shell worker. Since we may not have the right
+# gitlab-shell path set (yet) we need to disable this for these fixtures.
+Sidekiq::Testing.disable! do
+ Gitlab::Seeder.quiet do
+ User.first(10).each do |user|
+ key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{user.id + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
- print '.'
+ user.keys.create(
+ title: "Sample key #{user.id}",
+ key: key
+ )
+
+ print '.'
+ end
end
end
diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb
index 74898544a69..4f3bdba043d 100644
--- a/db/fixtures/development/12_snippets.rb
+++ b/db/fixtures/development/12_snippets.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
content =<<eos
class Member < ActiveRecord::Base
diff --git a/db/fixtures/development/13_comments.rb b/db/fixtures/development/13_comments.rb
index 566c0705638..29b8081055d 100644
--- a/db/fixtures/development/13_comments.rb
+++ b/db/fixtures/development/13_comments.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
Issue.all.each do |issue|
project = issue.project
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index be95d788850..534847a7107 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
class Gitlab::Seeder::Pipelines
STAGES = %w[build test deploy notify]
BUILDS = [
diff --git a/db/fixtures/development/15_award_emoji.rb b/db/fixtures/development/15_award_emoji.rb
index baac32f2d10..ea343c26b69 100644
--- a/db/fixtures/development/15_award_emoji.rb
+++ b/db/fixtures/development/15_award_emoji.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
emoji = Gitlab::AwardEmoji.emojis.keys
diff --git a/db/fixtures/development/16_protected_branches.rb b/db/fixtures/development/16_protected_branches.rb
index 103c7f9445c..39d466fb43f 100644
--- a/db/fixtures/development/16_protected_branches.rb
+++ b/db/fixtures/development/16_protected_branches.rb
@@ -1,3 +1,5 @@
+require './spec/support/sidekiq'
+
Gitlab::Seeder.quiet do
admin_user = User.find(1)
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 916ee8dbac8..747901dd634 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -1,4 +1,4 @@
-require 'sidekiq/testing'
+require './spec/support/sidekiq'
require './spec/support/test_env'
class Gitlab::Seeder::CycleAnalytics
diff --git a/db/migrate/20161223034433_add_estimate_to_issuables_ce.rb b/db/migrate/20161223034433_add_estimate_to_issuables_ce.rb
index 2cbe626d752..d5116dfab49 100644
--- a/db/migrate/20161223034433_add_estimate_to_issuables_ce.rb
+++ b/db/migrate/20161223034433_add_estimate_to_issuables_ce.rb
@@ -3,7 +3,7 @@ class AddEstimateToIssuablesCe < ActiveRecord::Migration
DOWNTIME = false
- def change
+ def up
unless column_exists?(:issues, :time_estimate)
add_column :issues, :time_estimate, :integer
end
@@ -12,4 +12,14 @@ class AddEstimateToIssuablesCe < ActiveRecord::Migration
add_column :merge_requests, :time_estimate, :integer
end
end
+
+ def down
+ if column_exists?(:issues, :time_estimate)
+ remove_column :issues, :time_estimate
+ end
+
+ if column_exists?(:merge_requests, :time_estimate)
+ remove_column :merge_requests, :time_estimate
+ end
+ end
end
diff --git a/db/migrate/20161223034646_create_timelogs_ce.rb b/db/migrate/20161223034646_create_timelogs_ce.rb
index e8a4b406012..66d9cd823fb 100644
--- a/db/migrate/20161223034646_create_timelogs_ce.rb
+++ b/db/migrate/20161223034646_create_timelogs_ce.rb
@@ -3,7 +3,7 @@ class CreateTimelogsCe < ActiveRecord::Migration
DOWNTIME = false
- def change
+ def up
unless table_exists?(:timelogs)
create_table :timelogs do |t|
t.integer :time_spent, null: false
@@ -17,4 +17,8 @@ class CreateTimelogsCe < ActiveRecord::Migration
add_index :timelogs, :user_id
end
end
+
+ def down
+ drop_table :timelogs if table_exists?(:timelogs)
+ end
end
diff --git a/doc/README.md b/doc/README.md
index 993b30ccdb5..909740211a6 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -44,7 +44,7 @@
- [Operations](administration/operations.md) Keeping GitLab up and running.
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
- [Repository checks](administration/repository_checks.md) Periodic Git repository checks.
-- [Repository storages](administration/repository_storages.md) Manage the paths used to store repositories.
+- [Repository storage paths](administration/repository_storage_paths.md) Manage the paths used to store repositories.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Update](update/README.md) Update guides to upgrade your installation.
@@ -61,7 +61,6 @@
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability.
- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
-- [Multiple mountpoints for the repositories storage](administration/repository_storages.md) Define multiple repository storage paths to distribute the storage load.
## Contributor documentation
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 04723365277..f6027b2f99e 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -65,11 +65,15 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
#
# Example: 'Paris' or 'Acme, Ltd.'
label: 'LDAP'
-
+
+ # Example: 'ldap.mydomain.com'
host: '_your_ldap_server'
+ # This port is an example, it is sometimes different but it is always an integer and not a string
port: 389
uid: 'sAMAccountName'
method: 'plain' # "tls" or "ssl" or "plain"
+
+ # Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com'
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
@@ -101,7 +105,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
# Base where we can search for users
#
- # Ex. ou=People,dc=gitlab,dc=example
+ # Ex. 'ou=People,dc=gitlab,dc=example' or 'DC=mydomain,DC=com'
#
base: ''
@@ -112,6 +116,9 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
#
# Note: GitLab does not support omniauth-ldap's custom filter syntax.
#
+ # Below an example for get only specific users
+ # Example: '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))'
+ #
user_filter: ''
# LDAP attributes that GitLab will use to create an account for the LDAP user.
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index f3c2e72341f..33b9b28433a 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -27,6 +27,7 @@ Ruby Version: 2.1.5p273
Gem Version: 2.4.3
Bundler Version: 1.7.6
Rake Version: 10.3.2
+Redis Version: 3.2.5
Sidekiq Version: 2.17.8
GitLab information
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index bc2b1f20ed3..ee37ea49874 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -13,12 +13,12 @@ checks failed you can see their output on the admin log page under
## Periodic checks
-GitLab periodically runs a repository check on all project repositories and
-wiki repositories in order to detect data corruption problems. A
-project will be checked no more than once per week. If any projects
+When enabled, GitLab periodically runs a repository check on all project
+repositories and wiki repositories in order to detect data corruption problems.
+A project will be checked no more than once per month. If any projects
fail their repository checks all GitLab administrators will receive an email
-notification of the situation. This notification is sent out no more
-than once a day.
+notification of the situation. This notification is sent out once a week on
+Sunday, by default.
## Disabling periodic checks
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
new file mode 100644
index 00000000000..d6aa6101026
--- /dev/null
+++ b/doc/administration/repository_storage_paths.md
@@ -0,0 +1,102 @@
+# Repository storage paths
+
+> [Introduced][ce-4578] in GitLab 8.10.
+
+GitLab allows you to define multiple repository storage paths to distribute the
+storage load between several mount points.
+
+>**Notes:**
+>
+- You must have at least one storage path called `default`.
+- The paths are defined in key-value pairs. The key is an arbitrary name you
+ can pick to name the file path.
+- The target directories and any of its subpaths must not be a symlink.
+
+## Configure GitLab
+
+>**Warning:**
+In order for [backups] to work correctly, the storage path must **not** be a
+mount point and the GitLab user should have correct permissions for the parent
+directory of the path. In Omnibus GitLab this is taken care of automatically,
+but for source installations you should be extra careful.
+>
+The thing is that for compatibility reasons `gitlab.yml` has a different
+structure than Omnibus. In `gitlab.yml` you indicate the path for the
+repositories, for example `/home/git/repositories`, while in Omnibus you
+indicate `git_data_dirs`, which for the example above would be `/home/git`.
+Then, Omnibus will create a `repositories` directory under that path to use with
+`gitlab.yml`.
+>
+This little detail matters because while restoring a backup, the current
+contents of `/home/git/repositories` [are moved to][raketask] `/home/git/repositories.old`,
+so if `/home/git/repositories` is the mount point, then `mv` would be moving
+things between mount points, and bad things could happen. Ideally,
+`/home/git` would be the mount point, so then things would be moving within the
+same mount point. This is guaranteed with Omnibus installations (because they
+don't specify the full repository path but the parent path), but not for source
+installations.
+
+---
+
+Now that you've read that big fat warning above, let's edit the configuration
+files and add the full paths of the alternative repository storage paths. In
+the example below, we add two more mountpoints that are named `nfs` and `cephfs`
+respectively.
+
+**For installations from source**
+
+1. Edit `gitlab.yml` and add the storage paths:
+
+ ```yaml
+ repositories:
+ # Paths where repositories can be stored. Give the canonicalized absolute pathname.
+ # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!!
+ storages: # You must have at least a 'default' storage path.
+ default: /home/git/repositories
+ nfs: /mnt/nfs/repositories
+ cephfs: /mnt/cephfs/repositories
+ ```
+
+1. [Restart GitLab] for the changes to take effect.
+
+>**Note:**
+The [`gitlab_shell: repos_path` entry][repospath] in `gitlab.yml` will be
+deprecated and replaced by `repositories: storages` in the future, so if you
+are upgrading from a version prior to 8.10, make sure to add the configuration
+as described in the step above. After you make the changes and confirm they are
+working, you can remove the `repos_path` line.
+
+---
+
+**For Omnibus installations**
+
+1. Edit `/etc/gitlab/gitlab.rb` by appending the rest of the paths to the
+ default one:
+
+ ```ruby
+ git_data_dirs({
+ "default" => "/var/opt/gitlab/git-data",
+ "nfs" => "/mnt/nfs/git-data",
+ "cephfs" => "/mnt/cephfs/git-data"
+ })
+ ```
+
+ Note that Omnibus stores the repositories in a `repositories` subdirectory
+ of the `git-data` directory.
+
+## Choose where new project repositories will be stored
+
+Once you set the multiple storage paths, you can choose where new projects will
+be stored via the **Application Settings** in the Admin area.
+
+![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png)
+
+Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be
+randomly placed on one of the selected paths.
+
+[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
+[restart gitlab]: restart_gitlab.md#installations-from-source
+[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
+[backups]: ../raketasks/backup_restore.md
+[raketask]: https://gitlab.com/gitlab-org/gitlab-ce/blob/033e5423a2594e08a7ebcd2379bd2331f4c39032/lib/backup/repository.rb#L54-56
+[repospath]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/config/gitlab.yml.example#L457
diff --git a/doc/administration/repository_storages.md b/doc/administration/repository_storages.md
index ab70557b69a..9d41ba77f34 100644
--- a/doc/administration/repository_storages.md
+++ b/doc/administration/repository_storages.md
@@ -1,102 +1,3 @@
# Repository storages
-> [Introduced][ce-4578] in GitLab 8.10.
-
-GitLab allows you to define multiple repository storage paths to distribute the
-storage load between several mount points.
-
->**Notes:**
->
-- You must have at least one storage path called `default`.
-- The paths are defined in key-value pairs. The key is an arbitrary name you
- can pick to name the file path.
-- The target directories and any of its subpaths must not be a symlink.
-
-## Configure GitLab
-
->**Warning:**
-In order for [backups] to work correctly, the storage path must **not** be a
-mount point and the GitLab user should have correct permissions for the parent
-directory of the path. In Omnibus GitLab this is taken care of automatically,
-but for source installations you should be extra careful.
->
-The thing is that for compatibility reasons `gitlab.yml` has a different
-structure than Omnibus. In `gitlab.yml` you indicate the path for the
-repositories, for example `/home/git/repositories`, while in Omnibus you
-indicate `git_data_dirs`, which for the example above would be `/home/git`.
-Then, Omnibus will create a `repositories` directory under that path to use with
-`gitlab.yml`.
->
-This little detail matters because while restoring a backup, the current
-contents of `/home/git/repositories` [are moved to][raketask] `/home/git/repositories.old`,
-so if `/home/git/repositories` is the mount point, then `mv` would be moving
-things between mount points, and bad things could happen. Ideally,
-`/home/git` would be the mount point, so then things would be moving within the
-same mount point. This is guaranteed with Omnibus installations (because they
-don't specify the full repository path but the parent path), but not for source
-installations.
-
----
-
-Now that you've read that big fat warning above, let's edit the configuration
-files and add the full paths of the alternative repository storage paths. In
-the example below, we add two more mountpoints that are named `nfs` and `cephfs`
-respectively.
-
-**For installations from source**
-
-1. Edit `gitlab.yml` and add the storage paths:
-
- ```yaml
- repositories:
- # Paths where repositories can be stored. Give the canonicalized absolute pathname.
- # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!!
- storages: # You must have at least a 'default' storage path.
- default: /home/git/repositories
- nfs: /mnt/nfs/repositories
- cephfs: /mnt/cephfs/repositories
- ```
-
-1. [Restart GitLab] for the changes to take effect.
-
->**Note:**
-The [`gitlab_shell: repos_path` entry][repospath] in `gitlab.yml` will be
-deprecated and replaced by `repositories: storages` in the future, so if you
-are upgrading from a version prior to 8.10, make sure to add the configuration
-as described in the step above. After you make the changes and confirm they are
-working, you can remove the `repos_path` line.
-
----
-
-**For Omnibus installations**
-
-1. Edit `/etc/gitlab/gitlab.rb` by appending the rest of the paths to the
- default one:
-
- ```ruby
- git_data_dirs({
- "default" => "/var/opt/gitlab/git-data",
- "nfs" => "/mnt/nfs/git-data",
- "cephfs" => "/mnt/cephfs/git-data"
- })
- ```
-
- Note that Omnibus stores the repositories in a `repositories` subdirectory
- of the `git-data` directory.
-
-## Choose where new project repositories will be stored
-
-Once you set the multiple storage paths, you can choose where new projects will
-be stored via the **Application Settings** in the Admin area.
-
-![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png)
-
-Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be
-randomly placed on one of the selected paths.
-
-[ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
-[restart gitlab]: restart_gitlab.md#installations-from-source
-[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
-[backups]: ../raketasks/backup_restore.md
-[raketask]: https://gitlab.com/gitlab-org/gitlab-ce/blob/033e5423a2594e08a7ebcd2379bd2331f4c39032/lib/backup/repository.rb#L54-56
-[repospath]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/config/gitlab.yml.example#L457
+This document was moved to a [new location](repository_storage_paths.md).
diff --git a/doc/api/enviroments.md b/doc/api/enviroments.md
index 1299aca8c45..e0ee20d9610 100644
--- a/doc/api/enviroments.md
+++ b/doc/api/enviroments.md
@@ -78,7 +78,7 @@ PUT /projects/:id/environments/:environments_id
| `external_url` | string | no | The new external_url |
```bash
-curl --request PUT --data "name=staging&external_url=https://staging.example.gitlab.com" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environment/1"
+curl --request PUT --data "name=staging&external_url=https://staging.example.gitlab.com" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environments/1"
```
Example response:
@@ -106,7 +106,7 @@ DELETE /projects/:id/environments/:environment_id
| `environment_id` | integer | yes | The ID of the environment |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environment/1"
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/environments/1"
```
Example response:
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index ffc310ec8c7..5377bf9ee80 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -14,6 +14,12 @@ Apart from those, here is an collection of tutorials and guides on setting up yo
- [Test a Phoenix application](test-phoenix-application.md)
- [Using `dpl` as deployment tool](deployment/README.md)
- [Example project that shows how to use Review Apps](https://gitlab.com/gitlab-examples/review-apps-nginx/)
+- [Run PHP Composer & NPM scripts then deploy them to a staging server](deployment/composer-npm-deploy.md)
+- Help your favorite programming language and GitLab by sending a merge request
+ with a guide for that language.
+
+## Outside the documentation
+
- [Blog post about using GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
- [Repositories with examples for various languages](https://gitlab.com/groups/gitlab-examples)
- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
new file mode 100644
index 00000000000..5334a73e1f5
--- /dev/null
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -0,0 +1,156 @@
+## Running Composer and NPM scripts with deployment via SCP
+
+This guide covers the building dependencies of a PHP project while compiling assets via an NPM script.
+
+While is possible to create your own image with custom PHP and Node JS versions, for brevity, we will use an existing [Docker image](https://hub.docker.com/r/tetraweb/php/) that contains both PHP and NodeJS installed.
+
+
+```yaml
+image: tetraweb/php
+```
+
+The next step is to install zip/unzip packages and make composer available. We will place these in the `before_script` section:
+
+```yaml
+before_script:
+ - apt-get update
+ - apt-get install zip unzip
+ - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+ - php composer-setup.php
+ - php -r "unlink('composer-setup.php');"
+```
+
+This will make sure we have all requirements ready. Next, we want to run `composer update` to fetch all PHP dependencies and `npm install` to load node packages, then run the `npm` script. We need to append them into `before_script` section:
+
+```yaml
+before_script:
+ # ...
+ - php composer.phar update
+ - npm install
+ - npm run deploy
+```
+
+In this particular case, the `npm deploy` script is a Gulp script that does the following:
+
+1. Compile CSS & JS
+2. Create sprites
+3. Copy various assets (images, fonts) around
+4. Replace some strings
+
+All these operations will put all files into a `build` folder, which is ready to be deployed to a live server.
+
+### How to transfer files to a live server?
+
+You have multiple options: rsync, scp, sftp and so on. For now, we will use scp.
+
+To make this work, you need to add a GitLab Secret Variable (accessible on _gitlab.example/your-project-name/variables_). That variable will be called `STAGING_PRIVATE_KEY` and it's the **private** ssh key of your server.
+
+#### Security tip
+
+Create a user that has access **only** to the folder that needs to be updated!
+
+After you create that variable, you need to make sure that key will be added to the docker container on run:
+
+```yaml
+before_script:
+ # - ....
+ - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
+ - mkdir -p ~/.ssh
+ - eval $(ssh-agent -s)
+ - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
+```
+
+In order, this means that:
+
+1. We check if the `ssh-agent` is available and we install it if it's not;
+2. We create the `~/.ssh` folder;
+3. We make sure we're running bash;
+4. We disable host checking (we don't ask for user accept when we first connect to a server; and since every build will equal a first connect, we kind of need this)
+
+And this is basically all you need in the `before_script` section.
+
+## How to deploy things?
+
+As we stated above, we need to deploy the `build` folder from the docker image to our server. To do so, we create a new job:
+
+```yaml
+stage_deploy:
+ artifacts:
+ paths:
+ - build/
+ only:
+ - dev
+ script:
+ - ssh-add <(echo "$STAGING_PRIVATE_KEY")
+ - ssh -p22 server_user@server_host "mkdir htdocs/wp-content/themes/_tmp"
+ - scp -P22 -r build/* server_user@server_host:htdocs/wp-content/themes/_tmp
+ - ssh -p22 server_user@server_host "mv htdocs/wp-content/themes/live htdocs/wp-content/themes/_old && mv htdocs/wp-content/themes/_tmp htdocs/wp-content/themes/live"
+ - ssh -p22 server_user@server_host "rm -rf htdocs/wp-content/themes/_old"
+```
+
+### What's going on here?
+
+1. `only:dev` means that this build will run only when something is pushed to the `dev` branch. You can remove this block completely and have everything be ran on every push (but probably this is something you don't want)
+2. `ssh-add ...` we will add that private key you added on the web UI to the docker container
+3. We will connect via `ssh` and create a new `_tmp` folder
+4. We will connect via `scp` and upload the `build` folder (which was generated by a `npm` script) to our previously created `_tmp` folder
+5. We will connect again to `ssh` and move the `live` folder to an `_old` folder, then move `_tmp` to `live`.
+6. We connect to ssh and remove the `_old` folder
+
+What's the deal with the artifacts? We just tell GitLab CI to keep the `build` directory (later on, you can download that as needed).
+
+#### Why we do it this way?
+
+If you're using this only for stage server, you could do this in two steps:
+
+```yaml
+- ssh -p22 server_user@server_host "rm -rf htdocs/wp-content/themes/live/*"
+- scp -P22 -r build/* server_user@server_host:htdocs/wp-content/themes/live
+```
+
+The problem is that there will be a small period of time when you won't have the app on your server.
+
+So we use so many steps because we want to make sure that at any given time we have a functional app in place.
+
+## Where to go next?
+
+Since this was a WordPress project, I gave real life code snippets. Some ideas you can pursuit:
+
+- Having a slightly different script for `master` branch will allow you to deploy to a production server from that branch and to a stage server from any other branches;
+- Instead of pushing it live, you can push it to WordPress official repo (with creating a SVN commit & stuff);
+- You could generate i18n text domains on the fly.
+
+---
+
+Our final `.gitlab-ci.yml` will look like this:
+
+```yaml
+image: tetraweb/php
+
+before_script:
+ - apt-get update
+ - apt-get install zip unzip
+ - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+ - php composer-setup.php
+ - php -r "unlink('composer-setup.php');"
+ - php composer.phar update
+ - npm install
+ - npm run deploy
+ - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
+ - mkdir -p ~/.ssh
+ - eval $(ssh-agent -s)
+ - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
+
+stage_deploy:
+ artifacts:
+ paths:
+ - build/
+ only:
+ - dev
+ script:
+ - ssh-add <(echo "$STAGING_PRIVATE_KEY")
+ - ssh -p22 server_user@server_host "mkdir htdocs/wp-content/themes/_tmp"
+ - scp -P22 -r build/* server_user@server_host:htdocs/wp-content/themes/_tmp
+ - ssh -p22 server_user@server_host "mv htdocs/wp-content/themes/live htdocs/wp-content/themes/_old && mv htdocs/wp-content/themes/_tmp htdocs/wp-content/themes/live"
+ - ssh -p22 server_user@server_host "rm -rf htdocs/wp-content/themes/_old"
+``` \ No newline at end of file
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 75a0897eb15..f11257be5c3 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -86,7 +86,7 @@ used for time of the build. The configuration of this feature is covered in
### before_script
`before_script` is used to define the command that should be run before all
-builds, including deploy builds. This can be an array or a multi-line string.
+builds, including deploy builds, but after the restoration of artifacts. This can be an array or a multi-line string.
### after_script
diff --git a/doc/development/README.md b/doc/development/README.md
index 6f2ca7b8590..265df98fb87 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -18,6 +18,7 @@
- [Frontend guidelines](frontend.md)
- [SQL guidelines](sql.md) for working with SQL queries
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
+- [`Gemfile` guidelines](gemfile.md)
## Process
diff --git a/doc/development/gemfile.md b/doc/development/gemfile.md
new file mode 100644
index 00000000000..ec9718cea71
--- /dev/null
+++ b/doc/development/gemfile.md
@@ -0,0 +1,14 @@
+# `Gemfile` guidelines
+
+When adding a new entry to `Gemfile` or upgrading an existing dependency pay
+attention to the following rules.
+
+## No gems fetched from git repositories
+
+We do not allow gems that are fetched from git repositories. All gems have
+to be available in the RubyGems index. We want to minimize external build
+dependencies and build times.
+
+## License compliance
+
+Refer to [licensing guidelines](licensing.md) for ensuring license compliance.
diff --git a/doc/development/performance.md b/doc/development/performance.md
index f936a49a2aa..c1f129e576c 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -211,6 +211,41 @@ suite first. See the
[StackProf documentation](https://github.com/tmm1/stackprof/blob/master/README.md)
for details.
+## RSpec profiling
+
+GitLab's development environment also includes the
+[rspec_profiling](https://github.com/foraker/rspec_profiling) gem, which is used
+to collect data on spec execution times. This is useful for analyzing the
+performance of the test suite itself, or seeing how the performance of a spec
+may have changed over time.
+
+To activate profiling in your local environment, run the following:
+
+```
+$ export RSPEC_PROFILING=yes
+$ rake rspec_profiling:install
+```
+
+This creates an SQLite3 database in `tmp/rspec_profiling`, into which statistics
+are saved every time you run specs with the `RSPEC_PROFILING` environment
+variable set.
+
+Ad-hoc investigation of the collected results can be performed in an interactive
+shell:
+
+```
+$ rake rspec_profiling:console
+irb(main):001:0> results.count
+=> 231
+irb(main):002:0> results.last.attributes.keys
+=> ["id", "commit", "date", "file", "line_number", "description", "time", "status", "exception", "query_count", "query_time", "request_count", "request_time", "created_at", "updated_at"]
+irb(main):003:0> results.where(status: "passed").average(:time).to_s
+=> "0.211340155844156"
+```
+These results can also be placed into a PostgreSQL database by setting the
+`RSPEC_PROFILING_POSTGRES_URL` variable. This is used to profile the test suite
+when running in the CI environment.
+
## Importance of Changes
When working on performance improvements, it's important to always ask yourself
diff --git a/doc/gitlab-basics/add-image.md b/doc/gitlab-basics/add-image.md
index 476b48a217c..1a44123aa81 100644
--- a/doc/gitlab-basics/add-image.md
+++ b/doc/gitlab-basics/add-image.md
@@ -1,62 +1,56 @@
# How to add an image
-The following are the steps to add images to your repository in
-GitLab:
+Using your standard tool for copying files (e.g. Finder in Mac OS, or Explorer
+in Windows, or...), put the image file into the GitLab project. You can find the
+project as a regular folder in your files.
-Find the image that you’d like to add.
+Go to your [shell](command-line-commands.md), and move into the folder of your
+Gitlab project. This usually means running the following command until you get
+to the desired destination:
-In your computer files, find the GitLab project to which you'd like to add the image
-(you'll find it as a regular file). Click on every file until you find exactly where you'd
-like to add the image. There, paste the image.
-
-Go to your [shell](command-line-commands.md), and add the following commands:
-
-Add this command for every directory that you'd like to open:
```
-cd NAME-OF-FILE-YOU'D-LIKE-TO-OPEN
+cd NAME-OF-FOLDER-YOU'D-LIKE-TO-OPEN
```
-Create a new branch:
-```
-git checkout -b NAME-OF-BRANCH
-```
+Check if your image is actually present in the directory (if you are in Windows,
+use `dir` instead):
-Check if your image was correctly added to the directory:
```
ls
```
You should see the name of the image in the list shown.
-Move up the hierarchy through directories:
-```
-cd ../
-```
+Check the status:
-Check the status and you should see your image’s name in red:
```
git status
```
-Add your changes:
+Your image's name should appear in red, so `git` took notice of it! Now add it
+to the repository:
+
```
git add NAME-OF-YOUR-IMAGE
```
-Check the status and you should see your image’s name in green:
+Check the status again, your image's name should have turned green:
+
```
git status
```
-Add the commit:
+Commit:
+
```
-git commit -m “DESCRIBE COMMIT IN A FEW WORDS”
+git commit -m "DESCRIBE COMMIT IN A FEW WORDS"
```
-Now you can push (send) your changes (in the branch NAME-OF-BRANCH) to GitLab (the git remote named 'origin'):
+Now you can push (send) your changes (in the branch NAME-OF-BRANCH) to GitLab
+(the git remote named 'origin'):
+
```
git push origin NAME-OF-BRANCH
```
-Your image will be added to your branch in your repository in GitLab. Create a [Merge Request](add-merge-request.md)
-to integrate your changes to your project.
+Your image will be added to your branch in your repository in GitLab.
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index 322680f0cf4..65bfb0f7d6e 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -4,7 +4,7 @@
We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164).
-## MySQL
+## Initial database setup
# Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
@@ -32,8 +32,11 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
mysql> SET storage_engine=INNODB;
+ # If you have MySQL < 5.7.7 and want to enable utf8mb4 character set support with your GitLab install, you must set the following NOW:
+ mysql> SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda, innodb_large_prefix=1;
+
# Create the GitLab production database
- mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
+ mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_general_ci`;
# Grant the GitLab user necessary permissions on the database
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES, REFERENCES ON `gitlabhq_production`.* TO 'git'@'localhost';
@@ -51,7 +54,203 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Quit the database session
mysql> \q
- # You are done installing the database and can go back to the rest of the installation.
+ # You are done installing the database for now and can go back to the rest of the installation.
+
+Please proceed to the rest of the installation before running through the utf8mb4 support section.
+
+
+### MySQL utf8mb4 support
+
+After installation or upgrade, remember to [convert any new tables](#convert) to `utf8mb4`/`utf8mb4_general_ci`.
+
+---
+
+GitLab 8.14 has introduced [a feature](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7420) requiring `utf8mb4` encoding to be supported in your GitLab MySQL Database, which is not the case if you have setup your database before GitLab 8.16.
+
+Follow the below instructions to ensure you use the most up to date requirements for your GitLab MySQL Database.
+
+**We are about to do the following:**
+- Ensure you can enable `utf8mb4` encoding and `utf8mb4_general_ci` collation for your GitLab DB, tables and data.
+- Convert your GitLab tables and data from `utf8`/`utf8_general_ci` to `utf8mb4`/`utf8mb4_general_ci`
+
+### Check for utf8mb4 support
+
+#### Check for InnoDB File-Per-Table Tablespaces
+
+We need to check, enable and maybe convert your existing GitLab DB tables to the [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) as a prerequise for supporting **utfb8mb4 with long indexes** required by recent GitLab databases.
+
+ # Login to MySQL
+ mysql -u root -p
+
+ # Type the MySQL root password
+ mysql > use gitlabhq_production;
+
+ # Check your MySQL version is >= 5.5.3 (GitLab requires 5.5.14+)
+ mysql > SHOW VARIABLES LIKE 'version';
+ +---------------+-----------------+
+ | Variable_name | Value |
+ +---------------+-----------------+
+ | version | 5.5.53-0+deb8u1 |
+ +---------------+-----------------+
+
+ # Note where is your MySQL data dir for later:
+ mysql > select @@datadir;
+ +----------------+
+ | @@datadir |
+ +----------------+
+ | /var/lib/mysql |
+ +----------------+
+
+ # Note whether your MySQL server runs with innodb_file_per_table ON or OFF:
+ mysql> SELECT @@innodb_file_per_table;
+ +-------------------------+
+ | @@innodb_file_per_table |
+ +-------------------------+
+ | 1 |
+ +-------------------------+
+
+ # You can now quit the database session
+ mysql> \q
+
+> You need **MySQL 5.5.3 or later** to perform this update.
+
+Whatever the results of your checks above, we now need to check if your GitLab database has been created using [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) (i.e. `innodb_file_per_table` was set to **1** at initial setup time).
+
+> Note: This setting is [enabled by default](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_file_per_table) since MySQL 5.6.6.
+
+ # Run this command with root privileges, replace the data dir if different:
+ sudo ls -lh /var/lib/mysql/gitlabhq_production/*.ibd | wc -l
+
+ # Run this command with root privileges, replace the data dir if different:
+ sudo ls -lh /var/lib/mysql/gitlabhq_production/*.frm | wc -l
+
+
+- **Case 1: a result > 0 for both commands**
+
+Congrats, your GitLab database uses the right InnoDB tablespace format.
+
+However, you must still ensure that any **future tables** created by GitLab will still use the right format:
+
+- If `SELECT @@innodb_file_per_table` returned **1** previously, your server is running correctly.
+> It's however a requirement to check *now* that this setting is indeed persisted in your [my.cnf](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) file!
+
+- If `SELECT @@innodb_file_per_table` returned **0** previously, your server is not running correctly.
+> [Enable innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) by running in a MySQL session as root the command `SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;` and persist the two settings in your [my.cnf](https://dev.mysql.com/doc/refman/5.7/en/tablespace-enabling.html) file
+
+Now, if you have a **different result** returned by the 2 commands above, it means you have a **mix of tables format** uses in your GitLab database. This can happen if your MySQL server had different values for `innodb_file_per_table` in its life and you updated GitLab at different moments with those inconsistent values. So keep reading.
+
+- **Case 2: a result equals to "0" OR not the same result for both commands**
+
+Unfortunately, none or only some of your GitLab database tables use the GitLab requirement of [InnoDB File-Per-Table Tablespaces](http://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html).
+
+Let's enable what we need on the running server:
+
+ # Login to MySQL
+ mysql -u root -p
+
+ # Type the MySQL root password
+
+ # Enable innodb_file_per_table and set innodb_file_format on the running server:
+ mysql > SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;
+
+ # You can now quit the database session
+ mysql> \q
+
+> Now, **persist** [innodb_file_per_table](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/innodb-file-format-enabling.html) in your `my.cnf` file.
+
+Ensure at this stage that your GitLab instance is indeed **stopped**.
+
+Now, let's convert all the GitLab database tables to the new tablespace format:
+
+ # Login to MySQL
+ mysql -u root -p
+
+ # Type the MySQL root password
+ mysql > use gitlabhq_production;
+
+ # Safety check: you should still have those values set as follow:
+ mysql> SELECT @@innodb_file_per_table, @@innodb_file_format;
+ +-------------------------+----------------------+
+ | @@innodb_file_per_table | @@innodb_file_format |
+ +-------------------------+----------------------+
+ | 1 | Barracuda |
+ +-------------------------+----------------------+
+
+ mysql > SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` ENGINE=InnoDB;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_TYPE="BASE TABLE";
+
+ # If previous query returned results, copy & run all shown SQL statements
+
+ # You can now quit the database session
+ mysql> \q
+
+---
+
+#### Check for proper InnoDB File Format, Row Format, Large Prefix and tables conversion
+
+We need to check, enable and probably convert your existing GitLab DB tables to use the [Barracuda InnoDB file format](https://dev.mysql.com/doc/refman/5.6/en/innodb-file-format.html), the [DYNAMIC row format](https://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_dynamic_row_format) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) as a second prerequisite for supporting **utfb8mb4 with long indexes** used by recent GitLab databases.
+
+ # Login to MySQL
+ mysql -u root -p
+
+ # Type the MySQL root password
+ mysql > use gitlabhq_production;
+
+ # Set innodb_file_format and innodb_large_prefix on the running server:
+ # Note: These are the default settings only for MySQL 5.7.7 and later.
+
+ mysql > SET GLOBAL innodb_file_format=Barracuda, innodb_large_prefix=1;
+
+ # Your DB must be (still) using utf8/utf8_general_ci as default encoding and collation.
+ # We will NOT change the default encoding and collation on the DB in order to support future GitLab migrations creating tables
+ # that require "long indexes support" on installations using MySQL <= 5.7.9.
+ # However, when such migrations occur, you will have to follow this guide again to convert the newly created tables to the proper encoding/collation.
+
+ # This should return the following:
+ mysql> SELECT @@character_set_database, @@collation_database;
+ +--------------------------+----------------------+
+ | @@character_set_database | @@collation_database |
+ +--------------------------+----------------------+
+ | utf8 | utf8_general_ci |
+ +--------------------------+----------------------+
+
+> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.6/en/tablespace-enabling.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file.
+
+#### Tables and data conversion to utf8mb4
+<a name="convert"></a>
+
+Now that you have a persistent MySQL setup, you can safely upgrade tables after setup or upgrade time:
+
+ # Convert tables not using ROW_FORMAT DYNAMIC:
+
+ mysql> SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` ROW_FORMAT=DYNAMIC;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_TYPE="BASE TABLE" AND ROW_FORMAT!="Dynamic";
+
+ # !! If previous query returned results, copy & run all shown SQL statements
+
+ # Convert tables/columns not using utf8mb4/utf8mb4_general_ci as encoding/collation:
+
+ mysql > SET foreign_key_checks = 0;
+
+ mysql > SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_COLLATION != "utf8mb4_general_ci" AND TABLE_TYPE="BASE TABLE";
+
+ # !! If previous query returned results, copy & run all shown SQL statements
+
+ # Turn foreign key checks back on
+ mysql > SET foreign_key_checks = 1;
+
+ # You can now quit the database session
+ mysql> \q
+
+Ensure your GitLab database configuration file uses a proper connection encoding and collation:
+
+```sudo -u git -H editor config/database.yml```
+
+ production:
+ adapter: mysql2
+ encoding: utf8mb4
+ collation: utf8mb4_general_ci
+
+[Restart your GitLab instance](../administration/restart_gitlab.md).
+
## MySQL strings limits
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index c44930a4ceb..ec13c2446ef 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -1,6 +1,6 @@
# System hooks
-Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`.
+Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `project_update`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`.
System hooks can be used, e.g. for logging or changing information in a LDAP server.
@@ -88,6 +88,23 @@ X-Gitlab-Event: System Hook
}
```
+**Project updated:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:54Z",
+ "updated_at": "2012-07-21T07:38:22Z",
+ "event_name": "project_update",
+ "name": "StoreCloud",
+ "owner_email": "johnsmith@gmail.com",
+ "owner_name": "John Smith",
+ "path": "storecloud",
+ "path_with_namespace": "jsmith/storecloud",
+ "project_id": 74,
+ "project_visibility": "private",
+}
+```
+
**New Team Member:**
```json
diff --git a/doc/update/8.15-to-8.16.md b/doc/update/8.15-to-8.16.md
index 63f3c3fdda9..2695a16ac0b 100644
--- a/doc/update/8.15-to-8.16.md
+++ b/doc/update/8.15-to-8.16.md
@@ -97,6 +97,8 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
```
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
### 6. Update gitlab-workhorse
Install and compile gitlab-workhorse. This requires
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 0f959b956a5..cc688a7f99c 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -140,75 +140,77 @@ into the password field.
## Recovery options
-If you lose your code generation device (such as your mobile phone) and you need
-to disable two-factor authentication on your account, you have several options.
+To disable two-factor authentication on your account (for example, if you
+have lost your code generation device) you can:
+* [Use a saved recovery code](#use-a-saved-recovery-code)
+* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-SSH)
+* [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account)
### Use a saved recovery code
-When you enabled two-factor authentication for your account, a series of
-recovery codes were generated. If you saved those codes somewhere safe, you
-may use one to sign in.
+Enabling two-factor authentication for your account generated several recovery
+codes. If you saved these codes, you can use one of them to sign in.
-First, enter your username/email and password on the GitLab sign in page. When
-prompted for a two-factor code, enter one of the recovery codes you saved
-previously.
+To use a recovery code, enter your username/email and password on the GitLab
+sign-in page. When prompted for a two-factor code, enter the recovery code.
-> **Note:** Once a particular recovery code has been used, it cannot be used again.
- You may still use the other saved recovery codes at a later time.
+> **Note:** Once you use a recovery code, you cannot re-use it. You can still
+ use the other recovery codes you saved.
### Generate new recovery codes using SSH
-It's not uncommon for users to forget to save the recovery codes when enabling
-two-factor authentication. If you have an SSH key added to your GitLab account,
-you can generate a new set of recovery codes using SSH.
-
-Run `ssh git@gitlab.example.com 2fa_recovery_codes`. You will be prompted to
-confirm that you wish to generate new codes. If you choose to continue, any
-previously saved codes will be invalidated.
-
-```bash
-$ ssh git@gitlab.example.com 2fa_recovery_codes
-Are you sure you want to generate new two-factor recovery codes?
-Any existing recovery codes you saved will be invalidated. (yes/no)
-yes
-
-Your two-factor authentication recovery codes are:
-
-119135e5a3ebce8e
-11f6v2a498810dcd
-3924c7ab2089c902
-e79a3398bfe4f224
-34bd7b74adbc8861
-f061691d5107df1a
-169bf32a18e63e7f
-b510e7422e81c947
-20dbed24c5e74663
-df9d3b9403b9c9f0
-
-During sign in, use one of the codes above when prompted for
-your two-factor code. Then, visit your Profile Settings and add
-a new device so you do not lose access to your account again.
-```
-
-Next, go to the GitLab sign in page and enter your username/email and password.
-When prompted for a two-factor code, enter one of the recovery codes obtained
-from the command line output.
-
-> **Note:** After signing in, you should immediately visit your **Profile Settings
- -> Account** to set up two-factor authentication with a new device.
-
-### Ask a GitLab administrator to disable two-factor on your account
-
-If the above two methods are not possible, you may ask a GitLab global
-administrator to disable two-factor authentication for your account. Please
-be aware that this will temporarily leave your account in a less secure state.
-You should sign in and re-enable two-factor authentication as soon as possible
-after the administrator disables it.
+Users often forget to save their recovery codes when enabling two-factor
+authentication. If an SSH key is added to your GitLab account, you can generate
+a new set of recovery codes with SSH.
+
+1. Run `ssh git@gitlab.example.com 2fa_recovery_codes`.
+2. You are prompted to confirm that you want to generate new codes. Continuing this process invalidates previously saved codes.
+ ```
+ bash
+ $ ssh git@gitlab.example.com 2fa_recovery_codes
+ Are you sure you want to generate new two-factor recovery codes?
+ Any existing recovery codes you saved will be invalidated. (yes/no)
+
+ yes
+
+ Your two-factor authentication recovery codes are:
+
+ 119135e5a3ebce8e
+ 11f6v2a498810dcd
+ 3924c7ab2089c902
+ e79a3398bfe4f224
+ 34bd7b74adbc8861
+ f061691d5107df1a
+ 169bf32a18e63e7f
+ b510e7422e81c947
+ 20dbed24c5e74663
+ df9d3b9403b9c9f0
+
+ During sign in, use one of the codes above when prompted for your
+ two-factor code. Then, visit your Profile Settings and add a new device
+ so you do not lose access to your account again.
+ ```
+3. Go to the GitLab sign-in page and enter your username/email and password. When prompted for a two-factor code, enter one of the recovery codes obtained
+from the command-line output.
+
+> **Note:** After signing in, visit your **Profile Settings -> Account** immediately to set up two-factor authentication with a new
+ device.
+
+### Ask a GitLab administrator to disable two-factor authentication on your account
+
+If you cannot use a saved recovery code or generate new recovery codes, ask a
+GitLab global administrator to disable two-factor authentication for your
+account. This will temporarily leave your account in a less secure state.
+Sign in and re-enable two-factor authentication as soon as possible.
## Note to GitLab administrators
-You need to take special care to that 2FA keeps working after
-[restoring a GitLab backup](../../../raketasks/backup_restore.md).
+- You need to take special care to that 2FA keeps working after
+[restoring a GitLab backup](../raketasks/backup_restore.md).
+
+- To ensure 2FA authorizes correctly with TOTP server, you may want to ensure
+your GitLab server's time is synchronized via a service like NTP. Otherwise,
+you may have cases where authorization always fails because of time differences.
[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
[FreeOTP]: https://fedorahosted.org/freeotp/
diff --git a/features/steps/project/merge_requests/revert.rb b/features/steps/project/merge_requests/revert.rb
index 3cc4fe9dafb..31f95b524b3 100644
--- a/features/steps/project/merge_requests/revert.rb
+++ b/features/steps/project/merge_requests/revert.rb
@@ -30,14 +30,13 @@ class Spinach::Features::RevertMergeRequests < Spinach::FeatureSteps
end
step 'I am signed in as a developer of the project' do
+ @user = create(:user) { |u| @project.add_developer(u) }
login_as(@user)
end
step 'There is an open Merge Request' do
- @user = create(:user)
- @project = create(:project, :public, :repository)
- @project_member = create(:project_member, :developer, user: @user, project: @project)
- @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project)
+ @merge_request = create(:merge_request, :with_diffs, :simple)
+ @project = @merge_request.source_project
end
step 'I should see a revert error' do
diff --git a/features/support/env.rb b/features/support/env.rb
index 8dbe3624410..f394c30d52f 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -4,7 +4,6 @@ SimpleCovEnv.start!
ENV['RAILS_ENV'] = 'test'
require './config/environment'
require 'rspec/expectations'
-require 'sidekiq/testing/inline'
require_relative 'capybara'
require_relative 'db_cleaner'
@@ -15,7 +14,7 @@ if ENV['CI']
Knapsack::Adapters::SpinachAdapter.bind
end
-%w(select2_helper test_env repo_helpers wait_for_ajax).each do |f|
+%w(select2_helper test_env repo_helpers wait_for_ajax sidekiq).each do |f|
require Rails.root.join('spec', 'support', f)
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 0950c3d2e88..be659fa4a6a 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -129,12 +129,7 @@ module API
end
end
- # Delete all merged branches
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # DELETE /projects/:id/repository/branches/delete_merged
+ desc 'Delete all merged branches'
delete ":id/repository/merged_branches" do
DeleteMergedBranchesService.new(user_project, current_user).async_execute
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 6d04f68c8f9..a3d495a5da0 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -153,7 +153,7 @@ module Banzai
title = object_link_title(object)
klass = reference_class(object_sym)
- data = data_attributes_for(link_content || match, project, object)
+ data = data_attributes_for(link_content || match, project, object, link: !!link_content)
if matches.names.include?("url") && matches[:url]
url = matches[:url]
@@ -172,9 +172,10 @@ module Banzai
end
end
- def data_attributes_for(text, project, object)
+ def data_attributes_for(text, project, object, link: false)
data_attribute(
original: text,
+ link: link,
project: project.id,
object_sym => object.id
)
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 4d1bc687696..fd6b9704132 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -62,7 +62,7 @@ module Banzai
end
end
- def data_attributes_for(text, project, object)
+ def data_attributes_for(text, project, object, link: false)
if object.is_a?(ExternalIssue)
data_attribute(
project: project.id,
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index ab7af1cad21..6640168bfa2 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -53,6 +53,10 @@ module Banzai
context[:project]
end
+ def skip_project_check?
+ context[:skip_project_check]
+ end
+
def reference_class(type)
"gfm gfm-#{type} has-tooltip"
end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 026b81ac175..a447e2b8bff 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -20,17 +20,19 @@ module Banzai
code = node.text
css_classes = "code highlight"
lexer = lexer_for(language)
+ lang = lexer.tag
begin
code = format(lex(lexer, code))
- css_classes << " js-syntax-highlight #{lexer.tag}"
+ css_classes << " js-syntax-highlight #{lang}"
rescue
+ lang = nil
# Gracefully handle syntax highlighter bugs/errors to ensure
# users can still access an issue/comment/etc.
end
- highlighted = %(<pre class="#{css_classes}" v-pre="true"><code>#{code}</code></pre>)
+ highlighted = %(<pre class="#{css_classes}" lang="#{lang}" v-pre="true"><code>#{code}</code></pre>)
# Extracted to a method to measure it
replace_parent_pre_element(node, highlighted)
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index f842b1fb779..1aa9355b256 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -24,7 +24,7 @@ module Banzai
end
def call
- return doc if project.nil?
+ return doc if project.nil? && !skip_project_check?
ref_pattern = User.reference_pattern
ref_pattern_start = /\A#{ref_pattern}\z/
@@ -58,7 +58,7 @@ module Banzai
# have `gfm` and `gfm-project_member` class names attached for styling.
def user_link_filter(text, link_content: nil)
self.class.references_in(text) do |match, username|
- if username == 'all'
+ if username == 'all' && !skip_project_check?
link_to_all(link_content: link_content)
elsif namespace = namespaces[username]
link_to_namespace(namespace, link_content: link_content) || match
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index ac7bbcb0d10..b64a1287d4d 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -35,7 +35,8 @@ module Banzai
src: element['src'],
width: '400',
controls: true,
- 'data-setup' => '{}')
+ 'data-setup' => '{}',
+ 'data-title' => element['title'] || element['alt'])
link = doc.document.create_element(
'a',
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 5a1f873496c..ac95a79009b 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -1,6 +1,12 @@
module Banzai
module Pipeline
class GfmPipeline < BasePipeline
+ # These filters convert GitLab Flavored Markdown (GFM) to HTML.
+ # The handlers defined in app/assets/javascripts/copy_as_gfm.js.es6
+ # consequently convert that same HTML to GFM to be copied to the clipboard.
+ # Every filter that generates HTML from GFM should have a handler in
+ # app/assets/javascripts/copy_as_gfm.js.es6, in reverse order.
+ # The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb.
def self.filters
@filters ||= FilterArray[
Filter::SyntaxHighlightFilter,
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index f31fb6c3f71..74663556cbb 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -52,9 +52,9 @@ module Banzai
end
# Same as +render_field+, but without consulting or updating the cache field
- def cacheless_render_field(object, field)
+ def cacheless_render_field(object, field, options = {})
text = object.__send__(field)
- context = object.banzai_render_context(field)
+ context = object.banzai_render_context(field).merge(options)
cacheless_render(text, context)
end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index c4bdef781f7..8b939663ffd 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -18,24 +18,31 @@ module Ci
if current_runner.is_runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
+ Gitlab::Metrics.add_event(:build_not_found_cached)
return build_not_found!
end
new_update = current_runner.ensure_runner_queue_value
- build = Ci::RegisterBuildService.new.execute(current_runner)
+ result = Ci::RegisterBuildService.new(current_runner).execute
- if build
- Gitlab::Metrics.add_event(:build_found,
- project: build.project.path_with_namespace)
+ if result.valid?
+ if result.build
+ Gitlab::Metrics.add_event(:build_found,
+ project: result.build.project.path_with_namespace)
- present build, with: Entities::BuildDetails
- else
- Gitlab::Metrics.add_event(:build_not_found)
+ present result.build, with: Entities::BuildDetails
+ else
+ Gitlab::Metrics.add_event(:build_not_found)
- header 'X-GitLab-Last-Update', new_update
+ header 'X-GitLab-Last-Update', new_update
- build_not_found!
+ build_not_found!
+ end
+ else
+ # We received build that is invalid due to concurrency conflict
+ Gitlab::Metrics.add_event(:build_invalid)
+ conflict!
end
end
diff --git a/lib/gitlab/ci/trace_reader.rb b/lib/gitlab/ci/trace_reader.rb
index 37e51536e8f..1d7ddeb3e0f 100644
--- a/lib/gitlab/ci/trace_reader.rb
+++ b/lib/gitlab/ci/trace_reader.rb
@@ -42,6 +42,7 @@ module Gitlab
end
chunks.join.lines.last(max_lines).join
+ .force_encoding(Encoding.default_external)
end
end
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 2ff27e46d64..4ebd48a3fc7 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -9,7 +9,9 @@ module Gitlab
end
def ensure_application_settings!
- if connect_to_db?
+ return fake_application_settings unless connect_to_db?
+
+ unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
begin
settings = ::ApplicationSetting.current
# In case Redis isn't running or the Redis UNIX socket file is not available
@@ -20,43 +22,23 @@ module Gitlab
settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
- settings || fake_application_settings
+ settings || in_memory_application_settings
end
def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled?
end
+ def in_memory_application_settings
+ @in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting::DEFAULTS)
+ # In case migrations the application_settings table is not created yet,
+ # we fallback to a simple OpenStruct
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError
+ fake_application_settings
+ end
+
def fake_application_settings
- OpenStruct.new(
- default_projects_limit: Settings.gitlab['default_projects_limit'],
- default_branch_protection: Settings.gitlab['default_branch_protection'],
- signup_enabled: Settings.gitlab['signup_enabled'],
- signin_enabled: Settings.gitlab['signin_enabled'],
- gravatar_enabled: Settings.gravatar['enabled'],
- koding_enabled: false,
- plantuml_enabled: false,
- sign_in_text: nil,
- after_sign_up_text: nil,
- help_page_text: nil,
- shared_runners_text: nil,
- restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
- max_attachment_size: Settings.gitlab['max_attachment_size'],
- session_expire_delay: Settings.gitlab['session_expire_delay'],
- default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
- domain_whitelist: Settings.gitlab['domain_whitelist'],
- import_sources: %w[gitea github bitbucket gitlab google_code fogbugz git gitlab_project],
- shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
- max_artifacts_size: Settings.artifacts['max_size'],
- require_two_factor_authentication: false,
- two_factor_grace_period: 48,
- akismet_enabled: false,
- repository_checks_enabled: true,
- container_registry_token_expire_delay: 5,
- user_default_external: false,
- sidekiq_throttling_enabled: false,
- )
+ OpenStruct.new(::ApplicationSetting::DEFAULTS)
end
private
diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb
index 3f635be22ba..a55adc9b1c8 100644
--- a/lib/gitlab/github_import/project_creator.rb
+++ b/lib/gitlab/github_import/project_creator.rb
@@ -1,6 +1,8 @@
module Gitlab
module GithubImport
class ProjectCreator
+ include Gitlab::CurrentSettings
+
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
def initialize(repo, name, namespace, current_user, session_data, type: 'github')
@@ -34,7 +36,7 @@ module Gitlab
end
def visibility_level
- repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility
+ repo.private ? Gitlab::VisibilityLevel::PRIVATE : current_application_settings.default_project_visibility
end
#
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 45958710c13..52276cbcd9a 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -5,8 +5,6 @@
#
module Gitlab
module ImportSources
- extend CurrentSettings
-
ImportSource = Struct.new(:name, :title, :importer)
ImportTable = [
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
new file mode 100644
index 00000000000..8db91d25a4b
--- /dev/null
+++ b/lib/gitlab/job_waiter.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ # JobWaiter can be used to wait for a number of Sidekiq jobs to complete.
+ class JobWaiter
+ # The sleep interval between checking keys, in seconds.
+ INTERVAL = 0.1
+
+ # jobs - The job IDs to wait for.
+ def initialize(jobs)
+ @jobs = jobs
+ end
+
+ # Waits for all the jobs to be completed.
+ #
+ # timeout - The maximum amount of seconds to block the caller for. This
+ # ensures we don't indefinitely block a caller in case a job takes
+ # long to process, or is never processed.
+ def wait(timeout = 60)
+ start = Time.current
+
+ while (Time.current - start) <= timeout
+ break if SidekiqStatus.all_completed?(@jobs)
+
+ sleep(INTERVAL) # to not overload Redis too much.
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
new file mode 100644
index 00000000000..aadc401ff8d
--- /dev/null
+++ b/lib/gitlab/sidekiq_status.rb
@@ -0,0 +1,66 @@
+module Gitlab
+ # The SidekiqStatus module and its child classes can be used for checking if a
+ # Sidekiq job has been processed or not.
+ #
+ # To check if a job has been completed, simply pass the job ID to the
+ # `completed?` method:
+ #
+ # job_id = SomeWorker.perform_async(...)
+ #
+ # if Gitlab::SidekiqStatus.completed?(job_id)
+ # ...
+ # end
+ #
+ # For each job ID registered a separate key is stored in Redis, making lookups
+ # much faster than using Sidekiq's built-in job finding/status API. These keys
+ # expire after a certain period of time to prevent storing too many keys in
+ # Redis.
+ module SidekiqStatus
+ STATUS_KEY = 'gitlab-sidekiq-status:%s'.freeze
+
+ # The default time (in seconds) after which a status key is expired
+ # automatically. The default of 30 minutes should be more than sufficient
+ # for most jobs.
+ DEFAULT_EXPIRATION = 30.minutes.to_i
+
+ # Starts tracking of the given job.
+ #
+ # jid - The Sidekiq job ID
+ # expire - The expiration time of the Redis key.
+ def self.set(jid, expire = DEFAULT_EXPIRATION)
+ Sidekiq.redis do |redis|
+ redis.set(key_for(jid), 1, ex: expire)
+ end
+ end
+
+ # Stops the tracking of the given job.
+ #
+ # jid - The Sidekiq job ID to remove.
+ def self.unset(jid)
+ Sidekiq.redis do |redis|
+ redis.del(key_for(jid))
+ end
+ end
+
+ # Returns true if all the given job have been completed.
+ #
+ # jids - The Sidekiq job IDs to check.
+ #
+ # Returns true or false.
+ def self.all_completed?(jids)
+ keys = jids.map { |jid| key_for(jid) }
+
+ responses = Sidekiq.redis do |redis|
+ redis.pipelined do
+ keys.each { |key| redis.exists(key) }
+ end
+ end
+
+ responses.all? { |value| !value }
+ end
+
+ def self.key_for(jid)
+ STATUS_KEY % jid
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_status/client_middleware.rb b/lib/gitlab/sidekiq_status/client_middleware.rb
new file mode 100644
index 00000000000..779a9998b22
--- /dev/null
+++ b/lib/gitlab/sidekiq_status/client_middleware.rb
@@ -0,0 +1,10 @@
+module Gitlab
+ module SidekiqStatus
+ class ClientMiddleware
+ def call(_, job, _, _)
+ SidekiqStatus.set(job['jid'])
+ yield
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_status/server_middleware.rb b/lib/gitlab/sidekiq_status/server_middleware.rb
new file mode 100644
index 00000000000..31dfa46ff9d
--- /dev/null
+++ b/lib/gitlab/sidekiq_status/server_middleware.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module SidekiqStatus
+ class ServerMiddleware
+ def call(worker, job, queue)
+ ret = yield
+
+ SidekiqStatus.unset(job['jid'])
+
+ ret
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb
index 83c8ba5c1cf..dbfe0941e4d 100644
--- a/lib/gitlab/view/presenter/base.rb
+++ b/lib/gitlab/view/presenter/base.rb
@@ -1,6 +1,8 @@
module Gitlab
module View
module Presenter
+ CannotOverrideMethodError = Class.new(StandardError)
+
module Base
extend ActiveSupport::Concern
diff --git a/lib/gitlab/view/presenter/delegated.rb b/lib/gitlab/view/presenter/delegated.rb
index f4d330c590e..387ff0f5d43 100644
--- a/lib/gitlab/view/presenter/delegated.rb
+++ b/lib/gitlab/view/presenter/delegated.rb
@@ -8,6 +8,10 @@ module Gitlab
@subject = subject
attributes.each do |key, value|
+ if subject.respond_to?(key)
+ raise CannotOverrideMethodError.new("#{subject} already respond to #{key}!")
+ end
+
define_singleton_method(key) { value }
end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 9462f3368e6..c7953af29dd 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -11,6 +11,7 @@ module Gitlab
included do
scope :public_only, -> { where(visibility_level: PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
+ scope :non_public_only, -> { where.not(visibility_level: PUBLIC) }
scope :public_to_user, -> (user) { user && !user.external ? public_and_internal_only : public_only }
end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index dffea8ed155..f7c831892ee 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -11,8 +11,10 @@ namespace :gitlab do
gem_version = run_command(%W(gem --version))
# check Bundler version
bunder_version = run_and_match(%W(bundle --version), /[\d\.]+/).try(:to_s)
- # check Bundler version
+ # check Rake version
rake_version = run_and_match(%W(rake --version), /[\d\.]+/).try(:to_s)
+ # check redis version
+ redis_version = run_and_match(%W(redis-cli --version), /redis-cli (\d+\.\d+\.\d+)/).to_a
puts ""
puts "System information".color(:yellow)
@@ -24,6 +26,7 @@ namespace :gitlab do
puts "Gem Version:\t#{gem_version || "unknown".color(:red)}"
puts "Bundler Version:#{bunder_version || "unknown".color(:red)}"
puts "Rake Version:\t#{rake_version || "unknown".color(:red)}"
+ puts "Redis Version:\t#{redis_version[1] || "unknown".color(:red)}"
puts "Sidekiq Version:#{Sidekiq::VERSION}"
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index 602de72d23f..84db26a958a 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Admin::GroupsController do
let(:group) { create(:group) }
- let(:project) { create(:project, namespace: group) }
+ let(:project) { create(:empty_project, namespace: group) }
let(:admin) { create(:admin) }
before do
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
index 8eaacef2024..2c35d394b74 100644
--- a/spec/controllers/admin/projects_controller_spec.rb
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Admin::ProjectsController do
- let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let!(:project) { create(:empty_project, :public) }
before do
sign_in(create(:admin))
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index ea2fd90a9b0..7d2f6dd9d0a 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe AutocompleteController do
- let!(:project) { create(:project) }
+ let!(:project) { create(:empty_project) }
let!(:user) { create(:user) }
context 'GET users' do
diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb
index 465013231f9..2fcb4a6a528 100644
--- a/spec/controllers/blob_controller_spec.rb
+++ b/spec/controllers/blob_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::BlobController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb
index 5022a3e2c80..86f01f437a2 100644
--- a/spec/controllers/ci/projects_controller_spec.rb
+++ b/spec/controllers/ci/projects_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Ci::ProjectsController do
let(:visibility) { :public }
- let!(:project) { create(:project, visibility, ci_id: 1) }
+ let!(:project) { create(:empty_project, visibility, ci_id: 1) }
let(:ci_id) { project.ci_id }
describe '#index' do
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
index 19fbc2f7748..79ef3a1adad 100644
--- a/spec/controllers/dashboard/todos_controller_spec.rb
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Dashboard::TodosController do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:todo_service) { TodoService.new }
describe 'GET #index' do
diff --git a/spec/controllers/explore/projects_controller_spec.rb b/spec/controllers/explore/projects_controller_spec.rb
new file mode 100644
index 00000000000..9dceeca168d
--- /dev/null
+++ b/spec/controllers/explore/projects_controller_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Explore::ProjectsController do
+ describe 'GET #trending' do
+ context 'sorting by update date' do
+ let(:project1) { create(:empty_project, :public, updated_at: 3.days.ago) }
+ let(:project2) { create(:empty_project, :public, updated_at: 1.day.ago) }
+
+ before do
+ create(:trending_project, project: project1)
+ create(:trending_project, project: project2)
+ end
+
+ it 'sorts by last updated' do
+ get :trending, sort: 'updated_desc'
+
+ expect(assigns(:projects)).to eq [project2, project1]
+ end
+
+ it 'sorts by oldest updated' do
+ get :trending, sort: 'updated_asc'
+
+ expect(assigns(:projects)).to eq [project1, project2]
+ end
+ end
+ end
+end
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index 8c52f615b8b..6e4b5f78e33 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Groups::MilestonesController do
let(:group) { create(:group) }
- let(:project) { create(:project, group: group) }
+ let(:project) { create(:empty_project, group: group) }
let(:project2) { create(:empty_project, group: group) }
let(:user) { create(:user) }
let(:title) { '肯定不是中文的问题' }
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 98dfb3e5216..cad82a34fb0 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
describe GroupsController do
let(:user) { create(:user) }
let(:group) { create(:group) }
- let(:project) { create(:project, namespace: group) }
+ let(:project) { create(:empty_project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
describe 'GET #index' do
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
index 56ecf2bb644..cfe18dd4b6c 100644
--- a/spec/controllers/health_check_controller_spec.rb
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -1,10 +1,16 @@
require 'spec_helper'
describe HealthCheckController do
+ include StubENV
+
let(:token) { current_application_settings.health_check_access_token }
let(:json_response) { JSON.parse(response.body) }
let(:xml_response) { Hash.from_xml(response.body)['hash'] }
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ end
+
describe 'GET #index' do
context 'when services are up but NO access token' do
it 'returns a not found page' do
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index ce7c0b334ee..fa4cc0ebbe0 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -52,7 +52,7 @@ describe Import::BitbucketController do
end
it "assigns variables" do
- @project = create(:project, import_type: 'bitbucket', creator_id: user.id)
+ @project = create(:empty_project, import_type: 'bitbucket', creator_id: user.id)
allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo])
get :status
@@ -63,7 +63,7 @@ describe Import::BitbucketController do
end
it "does not show already added project" do
- @project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim')
+ @project = create(:empty_project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim')
allow_any_instance_of(Bitbucket::Client).to receive(:repos).and_return([@repo])
get :status
diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb
index 5f0f6dea821..fffbc805335 100644
--- a/spec/controllers/import/fogbugz_controller_spec.rb
+++ b/spec/controllers/import/fogbugz_controller_spec.rb
@@ -16,7 +16,7 @@ describe Import::FogbugzController do
end
it 'assigns variables' do
- @project = create(:project, import_type: 'fogbugz', creator_id: user.id)
+ @project = create(:empty_project, import_type: 'fogbugz', creator_id: user.id)
stub_client(repos: [@repo])
get :status
@@ -26,7 +26,7 @@ describe Import::FogbugzController do
end
it 'does not show already added project' do
- @project = create(:project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim')
+ @project = create(:empty_project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim')
stub_client(repos: [@repo])
get :status
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 6f75ebb16c8..3f73ea000ae 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -36,7 +36,7 @@ describe Import::GitlabController do
end
it "assigns variables" do
- @project = create(:project, import_type: 'gitlab', creator_id: user.id)
+ @project = create(:empty_project, import_type: 'gitlab', creator_id: user.id)
stub_client(projects: [@repo])
get :status
@@ -46,7 +46,7 @@ describe Import::GitlabController do
end
it "does not show already added project" do
- @project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim')
+ @project = create(:empty_project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim')
stub_client(projects: [@repo])
get :status
diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb
index 4241db6e771..c96fb90f70e 100644
--- a/spec/controllers/import/google_code_controller_spec.rb
+++ b/spec/controllers/import/google_code_controller_spec.rb
@@ -27,7 +27,7 @@ describe Import::GoogleCodeController do
end
it "assigns variables" do
- @project = create(:project, import_type: 'google_code', creator_id: user.id)
+ @project = create(:empty_project, import_type: 'google_code', creator_id: user.id)
stub_client(repos: [@repo], incompatible_repos: [])
get :status
@@ -38,7 +38,7 @@ describe Import::GoogleCodeController do
end
it "does not show already added project" do
- @project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
+ @project = create(:empty_project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
stub_client(repos: [@repo], incompatible_repos: [])
get :status
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
index 79b819a1377..9e3a31e1a6b 100644
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ b/spec/controllers/notification_settings_controller_spec.rb
@@ -93,7 +93,7 @@ describe NotificationSettingsController do
end
context 'not authorized' do
- let(:private_project) { create(:project, :private) }
+ let(:private_project) { create(:empty_project, :private) }
before { sign_in(user) }
it 'returns 404' do
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index f5ea097af8b..8b71d6518bb 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::AvatarsController do
- let(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:project) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb
index 4402ca43c65..addc5e7ec33 100644
--- a/spec/controllers/projects/blame_controller_spec.rb
+++ b/spec/controllers/projects/blame_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::BlameController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index f35c5d992d9..b36d0e69330 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
describe Projects::BlobController do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index b88586b8678..9de03876755 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::BranchesController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:developer) { create(:user) }
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 0fa06a38d2a..a95cfc5c6be 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::CommitController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:commit) { project.commit("master") }
let(:pipeline) { create(:ci_pipeline, project: project, commit: commit) }
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index 1ac7e03a2db..54b8d1108a5 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::CommitsController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index b03c4b52de6..e811c76fb31 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::CompareController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:ref_from) { "improve%2Fawesome" }
let(:ref_to) { "feature" }
diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb
index a971adf0539..6a6d71a16ee 100644
--- a/spec/controllers/projects/cycle_analytics_controller_spec.rb
+++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::CycleAnalyticsController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb
index ff617fea847..79ab364a6f3 100644
--- a/spec/controllers/projects/discussions_controller_spec.rb
+++ b/spec/controllers/projects/discussions_controller_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
describe Projects::DiscussionsController do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.source_project }
let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
let(:discussion) { note.discussion }
diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb
index 038dfeb8466..a4884256c92 100644
--- a/spec/controllers/projects/find_file_controller_spec.rb
+++ b/spec/controllers/projects/find_file_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::FindFileController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index 028ea067a97..a867668d97b 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::ForksController do
let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:forked_project) { Projects::ForkService.new(project, user).execute }
let(:group) { create(:group, owner: forked_project.creator) }
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index 74e6603b0cb..bbe8e4bf6b2 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::GraphsController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 17dc101b7ee..a976a9c27ab 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::GroupLinksController do
let(:group) { create(:group, :private) }
let(:group2) { create(:group, :private) }
- let(:project) { create(:project, :private, group: group2) }
+ let(:project) { create(:empty_project, :private, group: group2) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index b5987a83df0..5f27f336f72 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -98,7 +98,7 @@ describe Projects::IssuesController do
end
it 'fills in an issue for a merge request' do
- project_with_repository = create(:project)
+ project_with_repository = create(:project, :repository)
project_with_repository.team << [user, :developer]
mr = create(:merge_request_with_diff_notes, source_project: project_with_repository)
@@ -124,7 +124,7 @@ describe Projects::IssuesController do
describe 'PUT #update' do
context 'when moving issue to another private project' do
- let(:another_project) { create(:project, :private) }
+ let(:another_project) { create(:empty_project, :private) }
before do
sign_in(user)
@@ -466,7 +466,7 @@ describe Projects::IssuesController do
context "when the user is owner" do
let(:owner) { create(:user) }
let(:namespace) { create(:namespace, owner: owner) }
- let(:project) { create(:project, namespace: namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
before { sign_in(owner) }
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 6d30d085056..14207bf6b7a 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::MilestonesController do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:user) { create(:user) }
let(:milestone) { create(:milestone, project: project) }
let(:issue) { create(:issue, project: project, milestone: milestone) }
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 9f6d4ec6537..dc597202050 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::NotesController do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:note) { create(:note, noteable: issue, project: project) }
@@ -16,6 +16,7 @@ describe Projects::NotesController do
describe 'POST create' do
let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.source_project }
let(:request_params) do
{
note: { note: 'some note', noteable_id: merge_request.id, noteable_type: 'MergeRequest' },
@@ -88,6 +89,7 @@ describe Projects::NotesController do
end
describe "resolving and unresolving" do
+ let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 442f81187dc..416eaa0037e 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -143,7 +143,7 @@ describe Projects::ProjectMembersController do
end
context 'and is an owner' do
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
before { project.team << [user, :master] }
@@ -234,7 +234,7 @@ describe Projects::ProjectMembersController do
end
describe 'POST apply_import' do
- let(:another_project) { create(:project, :private) }
+ let(:another_project) { create(:empty_project, :private) }
let(:member) { create(:user) }
before do
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 04bd9a01f7b..b23d6e257ba 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::RawController do
- let(:public_project) { create(:project, :public) }
+ let(:public_project) { create(:project, :public, :repository) }
describe "#show" do
context 'regular filename' do
diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb
index abd45a74e2d..d8fb4667c67 100644
--- a/spec/controllers/projects/refs_controller_spec.rb
+++ b/spec/controllers/projects/refs_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::RefsController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb
index 9fd5c3b85f6..69fcc26c77e 100644
--- a/spec/controllers/projects/releases_controller_spec.rb
+++ b/spec/controllers/projects/releases_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::ReleasesController do
- let!(:project) { create(:project) }
+ let!(:project) { create(:project, :repository) }
let!(:user) { create(:user) }
let!(:release) { create(:release, project: project) }
let!(:tag) { release.tag }
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index 38e02a46626..04e88879fb8 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -1,7 +1,7 @@
require "spec_helper"
describe Projects::RepositoriesController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
describe "GET archive" do
context 'as a guest' do
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index a6e708c01e4..16365642a34 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::ServicesController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:service) { create(:service, project: project) }
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index e0f9a5b24a6..65f7bb34f4a 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::Settings::IntegrationsController do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb
index 5e661c2c41d..c36a5fdd66c 100644
--- a/spec/controllers/projects/tags_controller_spec.rb
+++ b/spec/controllers/projects/tags_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::TagsController do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let!(:release) { create(:release, project: project) }
let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') }
diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb
index 19a152bcb05..99d0bcfa8d1 100644
--- a/spec/controllers/projects/templates_controller_spec.rb
+++ b/spec/controllers/projects/templates_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::TemplatesController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:file_path_1) { '.gitlab/issue_templates/bug.md' }
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index 1cc050247c6..b81645a3d2d 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::TreeController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index 71d0e4be834..f1c8891e87a 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -1,7 +1,7 @@
require('spec_helper')
describe Projects::UploadsController do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:user) { create(:user) }
let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index d0a63aa9403..9323f723bdb 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -1,11 +1,11 @@
require('spec_helper')
describe ProjectsController do
- let(:project) { create(:project) }
- let(:public_project) { create(:project, :public) }
- let(:user) { create(:user) }
- let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
- let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:project) { create(:empty_project) }
+ let(:public_project) { create(:empty_project, :public) }
+ let(:user) { create(:user) }
+ let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
+ let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
describe 'GET index' do
context 'as a user' do
@@ -32,7 +32,7 @@ describe ProjectsController do
before { sign_in(user) }
context "user does not have access to project" do
- let(:private_project) { create(:project, :private) }
+ let(:private_project) { create(:empty_project, :private) }
it "does not initialize notification setting" do
get :show, namespace_id: private_project.namespace.path, id: private_project.path
@@ -146,6 +146,8 @@ describe ProjectsController do
end
context "rendering default project view" do
+ let(:public_project) { create(:project, :public, :repository) }
+
render_views
it "renders the activity view" do
@@ -190,25 +192,11 @@ describe ProjectsController do
expect(assigns(:project)).to eq(public_project)
expect(response).to redirect_to("/#{public_project.path_with_namespace}")
end
-
- # MySQL queries are case insensitive by default, so this spec would fail.
- if Gitlab::Database.postgresql?
- context "when there is also a match with the same casing" do
- let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) }
-
- it "loads the exactly matched project" do
- get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
-
- expect(assigns(:project)).to eq(other_project)
- expect(response).to have_http_status(200)
- end
- end
- end
end
end
context "when the url contains .atom" do
- let(:public_project_with_dot_atom) { build(:project, :public, name: 'my.atom', path: 'my.atom') }
+ let(:public_project_with_dot_atom) { build(:empty_project, :public, name: 'my.atom', path: 'my.atom') }
it 'expects an error creating the project' do
expect(public_project_with_dot_atom).not_to be_valid
@@ -217,7 +205,7 @@ describe ProjectsController do
context 'when the project is pending deletions' do
it 'renders a 404 error' do
- project = create(:project, pending_delete: true)
+ project = create(:empty_project, pending_delete: true)
sign_in(user)
get :show, namespace_id: project.namespace.path, id: project.path
@@ -233,6 +221,7 @@ describe ProjectsController do
let(:admin) { create(:admin) }
it "sets the repository to the right path after a rename" do
+ project = create(:project, :repository)
new_path = 'renamed_path'
project_params = { path: new_path }
controller.instance_variable_set(:@project, project)
@@ -384,6 +373,8 @@ describe ProjectsController do
end
describe "GET refs" do
+ let(:public_project) { create(:project, :public) }
+
it "gets a list of branches and tags" do
get :refs, namespace_id: public_project.namespace.path, id: public_project.path
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 69124ab06bf..570d9fa43f8 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -41,7 +41,7 @@ describe UploadsController do
end
context "when viewing a project avatar" do
- let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let!(:project) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
context "when the project is public" do
before do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 19a8b1fe524..bbe9aaf737f 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -73,7 +73,7 @@ describe UsersController do
end
context 'forked project' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:forked_project) { Projects::ForkService.new(project, user).execute }
before do
@@ -91,7 +91,7 @@ describe UsersController do
end
describe 'GET #calendar_activities' do
- let!(:project) { create(:project) }
+ let!(:project) { create(:empty_project) }
let!(:user) { create(:user) }
before do
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index ed4acca23f1..c3b4aff55ba 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -16,6 +16,10 @@ FactoryGirl.define do
is_shared true
end
+ trait :specific do
+ is_shared false
+ end
+
trait :inactive do
active false
end
diff --git a/spec/factories/deploy_keys_projects.rb b/spec/factories/deploy_keys_projects.rb
index 27cece487bd..75f8982ecd9 100644
--- a/spec/factories/deploy_keys_projects.rb
+++ b/spec/factories/deploy_keys_projects.rb
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :deploy_keys_project do
deploy_key
- project
+ project factory: :empty_project
end
end
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
index 8820d527c61..bfe41f71b57 100644
--- a/spec/factories/events.rb
+++ b/spec/factories/events.rb
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :event do
- project
+ project factory: :empty_project
author factory: :user
factory :closed_issue_event do
diff --git a/spec/factories/file_uploader.rb b/spec/factories/file_uploader.rb
index 1b36e21f2b0..bc74aeecc3b 100644
--- a/spec/factories/file_uploader.rb
+++ b/spec/factories/file_uploader.rb
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :file_uploader do
- project
+ project factory: :empty_project
secret nil
transient do
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index ece6beb9fa9..86f51ffca99 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -1,8 +1,9 @@
FactoryGirl.define do
- factory :group do
+ factory :group, class: Group, parent: :namespace do
sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') }
type 'Group'
+ owner nil
trait :public do
visibility_level Gitlab::VisibilityLevel::PUBLIC
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 2b4670be468..7e09f1ba8ea 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -6,7 +6,7 @@ FactoryGirl.define do
factory :issue do
title
author
- project
+ project factory: :empty_project
trait :confidential do
confidential true
diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb
index 3e8822faf97..5ba8443c62c 100644
--- a/spec/factories/labels.rb
+++ b/spec/factories/labels.rb
@@ -2,7 +2,7 @@ FactoryGirl.define do
factory :label, class: ProjectLabel do
sequence(:title) { |n| "label#{n}" }
color "#990000"
- project
+ project factory: :empty_project
transient do
priority nil
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 37eb49c94df..22f84150bb3 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -2,7 +2,7 @@ FactoryGirl.define do
factory :merge_request do
title
author
- source_project factory: :project
+ association :source_project, :repository, factory: :project
target_project { source_project }
# $ git log --pretty=oneline feature..master
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index 84da71ed6dc..841ab3c73b8 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -1,7 +1,7 @@
FactoryGirl.define do
factory :milestone do
title
- project
+ project factory: :empty_project
trait :active do
state "active"
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index a10ba629760..a21da7074f9 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -4,7 +4,7 @@ include ActionDispatch::TestProcess
FactoryGirl.define do
factory :note do
- project
+ project factory: :empty_project
note "Note"
author
on_issue
@@ -13,12 +13,19 @@ FactoryGirl.define do
factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
factory :note_on_merge_request, traits: [:on_merge_request]
factory :note_on_project_snippet, traits: [:on_project_snippet]
+ factory :note_on_personal_snippet, traits: [:on_personal_snippet]
factory :system_note, traits: [:system]
- factory :legacy_diff_note_on_commit, traits: [:on_commit, :legacy_diff_note], class: LegacyDiffNote
- factory :legacy_diff_note_on_merge_request, traits: [:on_merge_request, :legacy_diff_note], class: LegacyDiffNote
+ factory :legacy_diff_note_on_commit, traits: [:on_commit, :legacy_diff_note], class: LegacyDiffNote do
+ association :project, :repository
+ end
+
+ factory :legacy_diff_note_on_merge_request, traits: [:on_merge_request, :legacy_diff_note], class: LegacyDiffNote do
+ association :project, :repository
+ end
factory :diff_note_on_merge_request, traits: [:on_merge_request], class: DiffNote do
+ association :project, :repository
position do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
@@ -36,6 +43,7 @@ FactoryGirl.define do
end
factory :diff_note_on_commit, traits: [:on_commit], class: DiffNote do
+ association :project, :repository
position do
Gitlab::Diff::Position.new(
old_path: "files/ruby/popen.rb",
@@ -48,6 +56,7 @@ FactoryGirl.define do
end
trait :on_commit do
+ association :project, :repository
noteable nil
noteable_type 'Commit'
noteable_id nil
@@ -70,6 +79,11 @@ FactoryGirl.define do
noteable { create(:project_snippet, project: project) }
end
+ trait :on_personal_snippet do
+ noteable { create(:personal_snippet) }
+ project nil
+ end
+
trait :system do
system true
end
diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb
index e73cc05f9d7..50341d943f5 100644
--- a/spec/factories/project_group_links.rb
+++ b/spec/factories/project_group_links.rb
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :project_group_link do
- project
+ project factory: :empty_project
group
end
end
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index c21927640d1..d62799a5a47 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -1,7 +1,7 @@
FactoryGirl.define do
factory :project_member do
user
- project
+ project factory: :empty_project
master
trait(:guest) { access_level ProjectMember::GUEST }
diff --git a/spec/factories/project_snippets.rb b/spec/factories/project_snippets.rb
index d681a2c8483..e0fe1b36fd3 100644
--- a/spec/factories/project_snippets.rb
+++ b/spec/factories/project_snippets.rb
@@ -1,5 +1,5 @@
FactoryGirl.define do
factory :project_snippet, parent: :snippet, class: :ProjectSnippet do
- project
+ project factory: :empty_project
end
end
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index 74497dc82c0..6a6d6fa171f 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -2,6 +2,6 @@ FactoryGirl.define do
factory :release do
tag "v1.1.0"
description "Awesome release"
- project
+ project factory: :empty_project
end
end
diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb
index 78eb929c6e7..6287c40afe9 100644
--- a/spec/factories/sent_notifications.rb
+++ b/spec/factories/sent_notifications.rb
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :sent_notification do
- project
+ project factory: :empty_project
recipient factory: :user
noteable factory: :issue
reply_key "0123456789abcdef" * 2
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 9de78d68280..a14a46c803e 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -1,5 +1,5 @@
FactoryGirl.define do
factory :service do
- project
+ project factory: :empty_project
end
end
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 85a8c263643..91d6f39a5bf 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :todo do
- project
+ project factory: :empty_project
author
user
target factory: :issue
diff --git a/spec/factories/trending_project.rb b/spec/factories/trending_project.rb
new file mode 100644
index 00000000000..246176611dc
--- /dev/null
+++ b/spec/factories/trending_project.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ # TrendingProject
+ factory :trending_project, class: 'TrendingProject' do
+ project
+ end
+end
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index 66044b44495..e8e080ce3e2 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -1,10 +1,13 @@
require 'rails_helper'
feature 'Admin disables Git access protocol', feature: true do
+ include StubENV
+
let(:project) { create(:empty_project, :empty_repo) }
let(:admin) { create(:admin) }
background do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as(admin)
end
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index dec2dedf2b5..f7e49a56deb 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -1,9 +1,11 @@
require 'spec_helper'
feature "Admin Health Check", feature: true do
+ include StubENV
include WaitForAjax
before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
@@ -12,11 +14,12 @@ feature "Admin Health Check", feature: true do
visit admin_health_check_path
end
- it { page.has_text? 'Health Check' }
- it { page.has_text? 'Health information can be retrieved' }
-
it 'has a health check access token' do
+ page.has_text? 'Health Check'
+ page.has_text? 'Health information can be retrieved'
+
token = current_application_settings.health_check_access_token
+
expect(page).to have_content("Access token is #{token}")
expect(page).to have_selector('#health-check-token', text: token)
end
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index d92c66b689d..f05fbe3d062 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -1,7 +1,10 @@
require 'spec_helper'
describe "Admin Runners" do
+ include StubENV
+
before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 47fa2f14307..de42ab81fac 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -1,7 +1,10 @@
require 'spec_helper'
feature 'Admin updates settings', feature: true do
- before(:each) do
+ include StubENV
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
visit admin_application_settings_path
end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index 661fb761809..855247de2ea 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -1,7 +1,12 @@
require 'rails_helper'
feature 'Admin uses repository checks', feature: true do
- before { login_as :admin }
+ include StubENV
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ login_as :admin
+ end
scenario 'to trigger a single check' do
project = create(:empty_project)
@@ -29,7 +34,7 @@ feature 'Admin uses repository checks', feature: true do
scenario 'to clear all repository checks', js: true do
visit admin_application_settings_path
-
+
expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
click_link 'Clear all repository checks'
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
new file mode 100644
index 00000000000..f3a5b565122
--- /dev/null
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -0,0 +1,432 @@
+require 'spec_helper'
+
+describe 'Copy as GFM', feature: true, js: true do
+ include GitlabMarkdownHelper
+ include ActionView::Helpers::JavaScriptHelper
+
+ before do
+ @feat = MarkdownFeature.new
+
+ # `markdown` helper expects a `@project` variable
+ @project = @feat.project
+
+ visit namespace_project_issue_path(@project.namespace, @project, @feat.issue)
+ end
+
+ # The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert GitLab Flavored Markdown (GFM) to HTML.
+ # The handlers defined in app/assets/javascripts/copy_as_gfm.js.es6 consequently convert that same HTML to GFM.
+ # To make sure these filters and handlers are properly aligned, this spec tests the GFM-to-HTML-to-GFM cycle
+ # by verifying (`html_to_gfm(gfm_to_html(gfm)) == gfm`) for a number of examples of GFM for every filter, using the `verify` helper.
+
+ # These are all in a single `it` for performance reasons.
+ it 'works', :aggregate_failures do
+ verify(
+ 'nesting',
+
+ '> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**'
+ )
+
+ verify(
+ 'a real world example from the gitlab-ce README',
+
+ <<-GFM.strip_heredoc
+ # GitLab
+
+ [![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
+ [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
+ [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
+ [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
+
+ ## Canonical source
+
+ The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/).
+
+ ## Open source software to collaborate on code
+
+ To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/).
+
+
+ - Manage Git repositories with fine grained access controls that keep your code secure
+
+ - Perform code reviews and enhance collaboration with merge requests
+
+ - Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications
+
+ - Each project can also have an issue tracker, issue board, and a wiki
+
+ - Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
+
+ - Completely free and open source (MIT Expat license)
+ GFM
+ )
+
+ verify(
+ 'InlineDiffFilter',
+
+ '{-Deleted text-}',
+ '{+Added text+}'
+ )
+
+ verify(
+ 'TaskListFilter',
+
+ '- [ ] Unchecked task',
+ '- [x] Checked task',
+ '1. [ ] Unchecked numbered task',
+ '1. [x] Checked numbered task'
+ )
+
+ verify(
+ 'ReferenceFilter',
+
+ # issue reference
+ @feat.issue.to_reference,
+ # full issue reference
+ @feat.issue.to_reference(full: true),
+ # issue URL
+ namespace_project_issue_url(@project.namespace, @project, @feat.issue),
+ # issue URL with note anchor
+ namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123'),
+ # issue link
+ "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue)})",
+ # issue link with note anchor
+ "[Issue](#{namespace_project_issue_url(@project.namespace, @project, @feat.issue, anchor: 'note_123')})",
+ )
+
+ verify(
+ 'AutolinkFilter',
+
+ 'https://example.com'
+ )
+
+ verify(
+ 'TableOfContentsFilter',
+
+ '[[_TOC_]]'
+ )
+
+ verify(
+ 'EmojiFilter',
+
+ ':thumbsup:'
+ )
+
+ verify(
+ 'ImageLinkFilter',
+
+ '![Image](https://example.com/image.png)'
+ )
+
+ verify(
+ 'VideoLinkFilter',
+
+ '![Video](https://example.com/video.mp4)'
+ )
+
+ verify(
+ 'MathFilter: math as converted from GFM to HTML',
+
+ '$`c = \pm\sqrt{a^2 + b^2}`$',
+
+ # math block
+ <<-GFM.strip_heredoc
+ ```math
+ c = \pm\sqrt{a^2 + b^2}
+ ```
+ GFM
+ )
+
+ aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do
+ gfm = '$`c = \pm\sqrt{a^2 + b^2}`$'
+
+ html = <<-HTML.strip_heredoc
+ <span class="katex">
+ <span class="katex-mathml">
+ <math>
+ <semantics>
+ <mrow>
+ <mi>c</mi>
+ <mo>=</mo>
+ <mo>±</mo>
+ <msqrt>
+ <mrow>
+ <msup>
+ <mi>a</mi>
+ <mn>2</mn>
+ </msup>
+ <mo>+</mo>
+ <msup>
+ <mi>b</mi>
+ <mn>2</mn>
+ </msup>
+ </mrow>
+ </msqrt>
+ </mrow>
+ <annotation encoding="application/x-tex">c = \\pm\\sqrt{a^2 + b^2}</annotation>
+ </semantics>
+ </math>
+ </span>
+ <span class="katex-html" aria-hidden="true">
+ <span class="strut" style="height: 0.913389em;"></span>
+ <span class="strut bottom" style="height: 1.04em; vertical-align: -0.126611em;"></span>
+ <span class="base textstyle uncramped">
+ <span class="mord mathit">c</span>
+ <span class="mrel">=</span>
+ <span class="mord">±</span>
+ <span class="sqrt mord"><span class="sqrt-sign" style="top: -0.073389em;">
+ <span class="style-wrap reset-textstyle textstyle uncramped">√</span>
+ </span>
+ <span class="vlist">
+ <span class="" style="top: 0em;">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 1em;">​</span>
+ </span>
+ <span class="mord textstyle cramped">
+ <span class="mord">
+ <span class="mord mathit">a</span>
+ <span class="msupsub">
+ <span class="vlist">
+ <span class="" style="top: -0.289em; margin-right: 0.05em;">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 0em;">​</span>
+ </span>
+ <span class="reset-textstyle scriptstyle cramped">
+ <span class="mord mathrm">2</span>
+ </span>
+ </span>
+ <span class="baseline-fix">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 0em;">​</span>
+ </span>
+ ​</span>
+ </span>
+ </span>
+ </span>
+ <span class="mbin">+</span>
+ <span class="mord">
+ <span class="mord mathit">b</span>
+ <span class="msupsub">
+ <span class="vlist">
+ <span class="" style="top: -0.289em; margin-right: 0.05em;">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 0em;">​</span>
+ </span>
+ <span class="reset-textstyle scriptstyle cramped">
+ <span class="mord mathrm">2</span>
+ </span>
+ </span>
+ <span class="baseline-fix">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 0em;">​</span>
+ </span>
+ ​</span>
+ </span>
+ </span>
+ </span>
+ </span>
+ </span>
+ <span class="" style="top: -0.833389em;">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 1em;">​</span>
+ </span>
+ <span class="reset-textstyle textstyle uncramped sqrt-line"></span>
+ </span>
+ <span class="baseline-fix">
+ <span class="fontsize-ensurer reset-size5 size5">
+ <span class="" style="font-size: 1em;">​</span>
+ </span>
+ ​</span>
+ </span>
+ </span>
+ </span>
+ </span>
+ </span>
+ HTML
+
+ output_gfm = html_to_gfm(html)
+ expect(output_gfm.strip).to eq(gfm.strip)
+ end
+
+ verify(
+ 'SanitizationFilter',
+
+ <<-GFM.strip_heredoc
+ <sub>sub</sub>
+
+ <dl>
+ <dt>dt</dt>
+ <dd>dd</dd>
+ </dl>
+
+ <kbd>kbd</kbd>
+
+ <q>q</q>
+
+ <samp>samp</samp>
+
+ <var>var</var>
+
+ <ruby>ruby</ruby>
+
+ <rt>rt</rt>
+
+ <rp>rp</rp>
+
+ <abbr>abbr</abbr>
+ GFM
+ )
+
+ verify(
+ 'SanitizationFilter',
+
+ <<-GFM.strip_heredoc,
+ ```
+ Plain text
+ ```
+ GFM
+
+ <<-GFM.strip_heredoc,
+ ```ruby
+ def foo
+ bar
+ end
+ ```
+ GFM
+
+ <<-GFM.strip_heredoc
+ Foo
+
+ This is an example of GFM
+
+ ```js
+ Code goes here
+ ```
+ GFM
+ )
+
+ verify(
+ 'MarkdownFilter',
+
+ "Line with two spaces at the end \nto insert a linebreak",
+
+ '`code`',
+ '`` code with ` ticks ``',
+
+ '> Quote',
+
+ # multiline quote
+ <<-GFM.strip_heredoc,
+ > Multiline
+ > Quote
+ >
+ > With multiple paragraphs
+ GFM
+
+ '![Image](https://example.com/image.png)',
+
+ '# Heading with no anchor link',
+
+ '[Link](https://example.com)',
+
+ '- List item',
+
+ # multiline list item
+ <<-GFM.strip_heredoc,
+ - Multiline
+ List item
+ GFM
+
+ # nested lists
+ <<-GFM.strip_heredoc,
+ - Nested
+
+
+ - Lists
+ GFM
+
+ # list with blockquote
+ <<-GFM.strip_heredoc,
+ - List
+
+ > Blockquote
+ GFM
+
+ '1. Numbered list item',
+
+ # multiline numbered list item
+ <<-GFM.strip_heredoc,
+ 1. Multiline
+ Numbered list item
+ GFM
+
+ # nested numbered list
+ <<-GFM.strip_heredoc,
+ 1. Nested
+
+
+ 1. Numbered lists
+ GFM
+
+ '# Heading',
+ '## Heading',
+ '### Heading',
+ '#### Heading',
+ '##### Heading',
+ '###### Heading',
+
+ '**Bold**',
+
+ '_Italics_',
+
+ '~~Strikethrough~~',
+
+ '2^2',
+
+ '-----',
+
+ # table
+ <<-GFM.strip_heredoc,
+ | Centered | Right | Left |
+ |:--------:|------:|------|
+ | Foo | Bar | **Baz** |
+ | Foo | Bar | **Baz** |
+ GFM
+
+ # table with empty heading
+ <<-GFM.strip_heredoc,
+ | | x | y |
+ |---|---|---|
+ | a | 1 | 0 |
+ | b | 0 | 1 |
+ GFM
+ )
+ end
+
+ alias_method :gfm_to_html, :markdown
+
+ def html_to_gfm(html)
+ js = <<-JS.strip_heredoc
+ (function(html) {
+ var node = document.createElement('div');
+ node.innerHTML = html;
+ return window.gl.CopyAsGFM.nodeToGFM(node);
+ })("#{escape_javascript(html)}")
+ JS
+ page.evaluate_script(js)
+ end
+
+ def verify(label, *gfms)
+ aggregate_failures(label) do
+ gfms.each do |gfm|
+ html = gfm_to_html(gfm)
+ output_gfm = html_to_gfm(html)
+ expect(output_gfm.strip).to eq(gfm.strip)
+ end
+ end
+ end
+
+ # Fake a `current_user` helper
+ def current_user
+ @feat.user
+ 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 8a155c3bfc5..93763f092fb 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -43,14 +43,6 @@ describe 'Dropdown assignee', js: true, feature: true do
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
- it 'shows assigned to me link' do
- filtered_search.set('assignee:')
-
- page.within js_dropdown_assignee do
- expect(page).to have_content('Assigned to me')
- end
- end
-
it 'closes when the search bar is unfocused' do
find('body').click()
@@ -129,14 +121,6 @@ describe 'Dropdown assignee', js: true, feature: true do
filtered_search.set('assignee:')
end
- it 'filters by current user' do
- page.within js_dropdown_assignee do
- click_button 'Assigned to me'
- end
-
- expect(filtered_search.value).to eq("assignee:#{user.to_reference} ")
- end
-
it 'fills in the assignee username when the assignee has not been filtered' do
click_assignee(user_jacob.name)
@@ -185,4 +169,22 @@ describe 'Dropdown assignee', js: true, feature: true do
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
end
+
+ describe 'caching requests' do
+ it 'caches requests after the first load' do
+ filtered_search.set('assignee')
+ send_keys_to_filtered_search(':')
+ initial_size = dropdown_assignee_size
+
+ expect(initial_size).to be > 0
+
+ new_user = create(:user)
+ project.team << [new_user, :master]
+ find('.filtered-search-input-container .clear-search').click
+ filtered_search.set('assignee')
+ send_keys_to_filtered_search(':')
+
+ expect(dropdown_assignee_size).to eq(initial_size)
+ end
+ end
end
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index a5d5d9d4c5e..59e302f0e2d 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -157,4 +157,22 @@ describe 'Dropdown author', js: true, feature: true do
expect(page).to have_css(js_dropdown_author, visible: true)
end
end
+
+ describe 'caching requests' do
+ it 'caches requests after the first load' do
+ filtered_search.set('author')
+ send_keys_to_filtered_search(':')
+ initial_size = dropdown_author_size
+
+ expect(initial_size).to be > 0
+
+ new_user = create(:user)
+ project.team << [new_user, :master]
+ find('.filtered-search-input-container .clear-search').click
+ filtered_search.set('author')
+ send_keys_to_filtered_search(':')
+
+ expect(dropdown_author_size).to eq(initial_size)
+ end
+ end
end
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index bea00160f96..5079eb8dd00 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -40,6 +40,16 @@ describe 'Dropdown label', js: true, feature: true do
visit namespace_project_issues_path(project.namespace, project)
end
+ describe 'keyboard navigation' do
+ it 'selects label' do
+ send_keys_to_filtered_search('label:')
+
+ filtered_search.native.send_keys(:down, :down, :enter)
+
+ expect(filtered_search.value).to eq("label:~#{special_label.name} ")
+ end
+ end
+
describe 'behavior' do
it 'opens when the search bar has label:' do
filtered_search.set('label:')
@@ -239,4 +249,21 @@ describe 'Dropdown label', js: true, feature: true do
expect(page).to have_css(js_dropdown_label, visible: true)
end
end
+
+ describe 'caching requests' do
+ it 'caches requests after the first load' do
+ filtered_search.set('label')
+ send_keys_to_filtered_search(':')
+ initial_size = dropdown_label_size
+
+ expect(initial_size).to be > 0
+
+ create(:label, project: project)
+ find('.filtered-search-input-container .clear-search').click
+ filtered_search.set('label')
+ send_keys_to_filtered_search(':')
+
+ expect(dropdown_label_size).to eq(initial_size)
+ end
+ end
end
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 134e58ad586..0ce16715b86 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -219,4 +219,21 @@ describe 'Dropdown milestone', js: true, feature: true do
expect(page).to have_css(js_dropdown_milestone, visible: true)
end
end
+
+ describe 'caching requests' do
+ it 'caches requests after the first load' do
+ filtered_search.set('milestone')
+ send_keys_to_filtered_search(':')
+ initial_size = dropdown_milestone_size
+
+ expect(initial_size).to be > 0
+
+ create(:milestone, project: project)
+ find('.filtered-search-input-container .clear-search').click
+ filtered_search.set('milestone')
+ send_keys_to_filtered_search(':')
+
+ expect(dropdown_milestone_size).to eq(initial_size)
+ end
+ end
end
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 56b1d354eb0..90eb60eb337 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -20,6 +20,22 @@ describe 'Search bar', js: true, feature: true do
left_style.to_s.gsub('left: ', '').to_f
end
+ describe 'keyboard navigation' do
+ it 'makes item active' do
+ filtered_search.native.send_keys(:down)
+
+ page.within '#js-dropdown-hint' do
+ expect(page).to have_selector('.dropdown-active')
+ end
+ end
+
+ it 'selects item' do
+ filtered_search.native.send_keys(:down, :down, :enter)
+
+ expect(filtered_search.value).to eq('author:')
+ end
+ end
+
describe 'clear search button' do
it 'clears text' do
search_text = 'search_text'
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 8771cc8e157..741ca95f1ca 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -68,6 +68,22 @@ describe 'New/edit issue', feature: true, js: true do
end
end
end
+
+ it 'correctly updates the dropdown toggle when removing a label' do
+ click_button 'Labels'
+
+ page.within '.dropdown-menu-labels' do
+ click_link label.title
+ end
+
+ expect(find('.js-label-select')).to have_content(label.title)
+
+ page.within '.dropdown-menu-labels' do
+ click_link label.title
+ end
+
+ expect(find('.js-label-select')).to have_content('Labels')
+ end
end
context 'edit issue' do
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
new file mode 100644
index 00000000000..b6728960fb8
--- /dev/null
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -0,0 +1,108 @@
+require 'spec_helper'
+
+feature 'Merge Request button', feature: true do
+ shared_examples 'Merge Request button only shown when allowed' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:forked_project) { create(:project, :public, forked_from_project: project) }
+
+ context 'not logged in' do
+ it 'does not show Create Merge Request button' do
+ visit url
+
+ within("#content-body") do
+ expect(page).not_to have_link(label)
+ end
+ end
+ end
+
+ context 'logged in as developer' do
+ before do
+ login_as(user)
+ project.team << [user, :developer]
+ end
+
+ it 'shows Create Merge Request button' do
+ href = new_namespace_project_merge_request_path(project.namespace,
+ project,
+ merge_request: { source_branch: 'feature',
+ target_branch: 'master' })
+
+ visit url
+
+ within("#content-body") do
+ expect(page).to have_link(label, href: href)
+ end
+ end
+
+ context 'merge requests are disabled' do
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
+ end
+
+ it 'does not show Create Merge Request button' do
+ visit url
+
+ within("#content-body") do
+ expect(page).not_to have_link(label)
+ end
+ end
+ end
+ end
+
+ context 'logged in as non-member' do
+ before do
+ login_as(user)
+ end
+
+ it 'does not show Create Merge Request button' do
+ visit url
+
+ within("#content-body") do
+ expect(page).not_to have_link(label)
+ end
+ end
+
+ context 'on own fork of project' do
+ let(:user) { forked_project.owner }
+
+ it 'shows Create Merge Request button' do
+ href = new_namespace_project_merge_request_path(forked_project.namespace,
+ forked_project,
+ merge_request: { source_branch: 'feature',
+ target_branch: 'master' })
+
+ visit fork_url
+
+ within("#content-body") do
+ expect(page).to have_link(label, href: href)
+ end
+ end
+ end
+ end
+ end
+
+ context 'on branches page' do
+ it_behaves_like 'Merge Request button only shown when allowed' do
+ let(:label) { 'Merge Request' }
+ let(:url) { namespace_project_branches_path(project.namespace, project) }
+ let(:fork_url) { namespace_project_branches_path(forked_project.namespace, forked_project) }
+ end
+ end
+
+ context 'on compare page' do
+ it_behaves_like 'Merge Request button only shown when allowed' do
+ let(:label) { 'Create Merge Request' }
+ let(:url) { namespace_project_compare_path(project.namespace, project, from: 'master', to: 'feature') }
+ let(:fork_url) { namespace_project_compare_path(forked_project.namespace, forked_project, from: 'master', to: 'feature') }
+ end
+ end
+
+ context 'on commits page' do
+ it_behaves_like 'Merge Request button only shown when allowed' do
+ let(:label) { 'Create Merge Request' }
+ let(:url) { namespace_project_commits_path(project.namespace, project, 'feature') }
+ let(:fork_url) { namespace_project_commits_path(forked_project.namespace, forked_project, 'feature') }
+ end
+ end
+end
diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb
index 55d5d082c6e..5d0314d5c09 100644
--- a/spec/features/projects/project_settings_spec.rb
+++ b/spec/features/projects/project_settings_spec.rb
@@ -37,7 +37,7 @@ describe 'Edit Project Settings', feature: true do
it 'shows errors for invalid project path/name' do
visit edit_namespace_project_path(project.namespace, project)
- fill_in 'Project name', with: 'foo&bar'
+ fill_in 'project_name', with: 'foo&bar'
fill_in 'Path', with: 'foo&bar'
click_button 'Rename project'
@@ -53,7 +53,7 @@ describe 'Edit Project Settings', feature: true do
it 'shows error for invalid project name' do
visit edit_namespace_project_path(project.namespace, project)
- fill_in 'Project name', with: '🚀 foo bar ☁️'
+ fill_in 'project_name', with: '🚀 foo bar ☁️'
click_button 'Rename project'
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb
index db60c01db0d..91f34973ba5 100644
--- a/spec/finders/branches_finder_spec.rb
+++ b/spec/finders/branches_finder_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe BranchesFinder do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
describe '#execute' do
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
index 65d7f14c721..ad2d456529a 100644
--- a/spec/finders/contributed_projects_finder_spec.rb
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -6,8 +6,8 @@ describe ContributedProjectsFinder do
let(:finder) { described_class.new(source_user) }
- let!(:public_project) { create(:project, :public) }
- let!(:private_project) { create(:project, :private) }
+ let!(:public_project) { create(:empty_project, :public) }
+ let!(:private_project) { create(:empty_project, :private) }
before do
private_project.team << [source_user, Gitlab::Access::MASTER]
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
index 00eec3f3f4c..ef97b061ca7 100644
--- a/spec/finders/group_projects_finder_spec.rb
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -6,11 +6,11 @@ describe GroupProjectsFinder do
let(:finder) { described_class.new(source_user) }
- let!(:public_project) { create(:project, :public, group: group, path: '1') }
- let!(:private_project) { create(:project, :private, group: group, path: '2') }
- let!(:shared_project_1) { create(:project, :public, path: '3') }
- let!(:shared_project_2) { create(:project, :private, path: '4') }
- let!(:shared_project_3) { create(:project, :internal, path: '5') }
+ let!(:public_project) { create(:empty_project, :public, group: group, path: '1') }
+ let!(:private_project) { create(:empty_project, :private, group: group, path: '2') }
+ let!(:shared_project_1) { create(:empty_project, :public, path: '3') }
+ let!(:shared_project_2) { create(:empty_project, :private, path: '4') }
+ let!(:shared_project_3) { create(:empty_project, :internal, path: '5') }
before do
shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb
index 29a47e005a6..4c389746252 100644
--- a/spec/finders/joined_groups_finder_spec.rb
+++ b/spec/finders/joined_groups_finder_spec.rb
@@ -42,7 +42,7 @@ describe JoinedGroupsFinder do
context 'if profile visitor is in one of the private group projects' do
before do
- project = create(:project, :private, group: private_group, name: 'B', path: 'B')
+ project = create(:empty_project, :private, group: private_group, name: 'B', path: 'B')
project.add_user(profile_visitor, Gitlab::Access::DEVELOPER)
end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index e4ba1d2f1c2..3dcd7781e5b 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -4,9 +4,9 @@ describe MergeRequestsFinder do
let(:user) { create :user }
let(:user2) { create :user }
- let(:project1) { create(:project) }
- let(:project2) { create(:project, forked_from_project: project1) }
- let(:project3) { create(:project, :archived, forked_from_project: project1) }
+ let(:project1) { create(:empty_project) }
+ let(:project2) { create(:empty_project, forked_from_project: project1) }
+ let(:project3) { create(:empty_project, :archived, forked_from_project: project1) }
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') }
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 4d21254323c..bac653ea451 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -28,7 +28,7 @@ describe NotesFinder do
end
it "excludes notes on commits the author can't download" do
- project = create(:project, :private)
+ project = create(:project, :private, :repository)
note = create(:note_on_commit, project: project)
params = { target_type: 'commit', target_id: note.noteable.id }
@@ -76,7 +76,7 @@ describe NotesFinder do
end
context 'for target' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:note1) { create :note_on_commit, project: project }
let(:note2) { create :note_on_commit, project: project }
let(:commit) { note1.noteable }
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index a4681fe59d8..e0e17af681a 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -4,14 +4,14 @@ describe PersonalProjectsFinder do
let(:source_user) { create(:user) }
let(:current_user) { create(:user) }
let(:finder) { described_class.new(source_user) }
- let!(:public_project) { create(:project, :public, namespace: source_user.namespace) }
+ let!(:public_project) { create(:empty_project, :public, namespace: source_user.namespace) }
let!(:private_project) do
- create(:project, :private, namespace: source_user.namespace, path: 'mepmep')
+ create(:empty_project, :private, namespace: source_user.namespace, path: 'mepmep')
end
let!(:internal_project) do
- create(:project, :internal, namespace: source_user.namespace, path: 'C')
+ create(:empty_project, :internal, namespace: source_user.namespace, path: 'C')
end
before do
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb
index b0811d134fa..fdc8215aa47 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/pipelines_finder_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe PipelinesFinder do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let!(:tag_pipeline) { create(:ci_pipeline, project: project, ref: 'v1.0.0') }
let!(:branch_pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 13bda5f7c5a..e44e7434c80 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -6,19 +6,19 @@ describe ProjectsFinder do
let(:group) { create(:group, :public) }
let!(:private_project) do
- create(:project, :private, name: 'A', path: 'A')
+ create(:empty_project, :private, name: 'A', path: 'A')
end
let!(:internal_project) do
- create(:project, :internal, group: group, name: 'B', path: 'B')
+ create(:empty_project, :internal, group: group, name: 'B', path: 'B')
end
let!(:public_project) do
- create(:project, :public, group: group, name: 'C', path: 'C')
+ create(:empty_project, :public, group: group, name: 'C', path: 'C')
end
let!(:shared_project) do
- create(:project, :private, name: 'D', path: 'D')
+ create(:empty_project, :private, name: 'D', path: 'D')
end
let(:finder) { described_class.new }
diff --git a/spec/finders/tags_finder_spec.rb b/spec/finders/tags_finder_spec.rb
index 98b42e264dc..460e278e2d3 100644
--- a/spec/finders/tags_finder_spec.rb
+++ b/spec/finders/tags_finder_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe TagsFinder do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
describe '#execute' do
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 92053e5a7c6..8b201f348f1 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -55,7 +55,7 @@ describe ApplicationHelper do
describe 'project_icon' do
it 'returns an url for the avatar' do
- project = create(:project, avatar: File.open(uploaded_image_temp_path))
+ project = create(:empty_project, avatar: File.open(uploaded_image_temp_path))
avatar_url = "http://#{Gitlab.config.gitlab.host}/uploads/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s).
@@ -63,7 +63,7 @@ describe ApplicationHelper do
end
it 'gives uploaded icon when present' do
- project = create(:project)
+ project = create(:empty_project)
allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true)
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index a43a7238c70..fa516f9903e 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -70,7 +70,7 @@ describe BlobHelper do
describe "#edit_blob_link" do
let(:namespace) { create(:namespace, name: 'gitlab' )}
- let(:project) { create(:project, namespace: namespace) }
+ let(:project) { create(:project, :repository, namespace: namespace) }
before do
allow(self).to receive(:current_user).and_return(double)
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 810311e2b1a..b8ec3521edb 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe GitlabMarkdownHelper do
include ApplicationHelper
- let!(:project) { create(:project) }
+ let!(:project) { create(:project, :repository) }
let(:user) { create(:user, username: 'gfm') }
let(:commit) { project.commit }
@@ -55,18 +55,18 @@ describe GitlabMarkdownHelper do
end
describe '#link_to_gfm' do
- let(:commit_path) { namespace_project_commit_path(project.namespace, project, commit) }
- let(:issues) { create_list(:issue, 2, project: project) }
+ let(:link) { '/commits/0a1b2c3d' }
+ let(:issues) { create_list(:issue, 2, project: project) }
it 'handles references nested in links with all the text' do
- actual = helper.link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path)
+ actual = helper.link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", link)
doc = Nokogiri::HTML.parse(actual)
# Make sure we didn't create invalid markup
expect(doc.errors).to be_empty
# Leading commit link
- expect(doc.css('a')[0].attr('href')).to eq commit_path
+ expect(doc.css('a')[0].attr('href')).to eq link
expect(doc.css('a')[0].text).to eq 'This should finally fix '
# First issue link
@@ -75,7 +75,7 @@ describe GitlabMarkdownHelper do
expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link
- expect(doc.css('a')[2].attr('href')).to eq commit_path
+ expect(doc.css('a')[2].attr('href')).to eq link
expect(doc.css('a')[2].text).to eq ' and '
# Second issue link
@@ -84,12 +84,12 @@ describe GitlabMarkdownHelper do
expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link
- expect(doc.css('a')[4].attr('href')).to eq commit_path
+ expect(doc.css('a')[4].attr('href')).to eq link
expect(doc.css('a')[4].text).to eq ' for real'
end
it 'forwards HTML options' do
- actual = helper.link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
+ actual = helper.link_to_gfm("Fixed in #{commit.id}", link, class: 'foo')
doc = Nokogiri::HTML.parse(actual)
expect(doc.css('a')).to satisfy do |v|
@@ -100,7 +100,7 @@ describe GitlabMarkdownHelper do
it "escapes HTML passed in as the body" do
actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}"
- expect(helper.link_to_gfm(actual, commit_path)).
+ expect(helper.link_to_gfm(actual, link)).
to match('&lt;h1&gt;test&lt;/h1&gt;')
end
diff --git a/spec/helpers/graph_helper_spec.rb b/spec/helpers/graph_helper_spec.rb
index 51c49f0e587..400635abdde 100644
--- a/spec/helpers/graph_helper_spec.rb
+++ b/spec/helpers/graph_helper_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe GraphHelper do
describe '#get_refs' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:commit) { project.commit("master") }
let(:graph) { Network::Graph.new(project, 'master', commit, '') }
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 9c7e0ee2fe0..13fb9c1f1a7 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -1,7 +1,7 @@
require "spec_helper"
describe IssuesHelper do
- let(:project) { create :project }
+ let(:project) { create(:empty_project) }
let(:issue) { create :issue, project: project }
let(:ext_project) { create :redmine_project }
diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb
index 33934cdf8b1..2b455571d52 100644
--- a/spec/helpers/members_helper_spec.rb
+++ b/spec/helpers/members_helper_spec.rb
@@ -46,7 +46,7 @@ describe MembersHelper do
end
describe '#leave_confirmation_message' do
- let(:project) { build_stubbed(:project) }
+ let(:project) { build_stubbed(:empty_project) }
let(:group) { build_stubbed(:group) }
let(:user) { build_stubbed(:user) }
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 1f221487393..550b4a66a6a 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe MergeRequestsHelper do
describe 'ci_build_details_path' do
- let(:project) { create :project }
+ let(:project) { create(:empty_project) }
let(:merge_request) { MergeRequest.new }
let(:ci_service) { CiService.new }
let(:last_commit) { Ci::Pipeline.new({}) }
@@ -30,7 +30,7 @@ describe MergeRequestsHelper do
it { is_expected.to eq('#1, #2, and #3') }
context 'for JIRA issues' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issues) do
[
ExternalIssue.new('JIRA-123', project),
@@ -52,8 +52,8 @@ describe MergeRequestsHelper do
end
describe 'within different projects' do
- let(:project) { create(:project) }
- let(:fork_project) { create(:project, forked_from_project: project) }
+ let(:project) { create(:empty_project) }
+ let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) }
subject { format_mr_branch_names(merge_request) }
let(:source_title) { "#{fork_project.path_with_namespace}:#{merge_request.source_branch}" }
@@ -64,8 +64,8 @@ describe MergeRequestsHelper do
end
describe 'mr_widget_refresh_url' do
+ let(:project) { create(:empty_project) }
let(:merge_request) { create(:merge_request, source_project: project) }
- let(:project) { create(:project) }
it 'returns correct url for MR' do
expected_url = "#{project.path_with_namespace}/merge_requests/#{merge_request.iid}/merge_widget_refresh"
diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb
index ea744dbb629..14a95479339 100644
--- a/spec/helpers/milestones_helper_spec.rb
+++ b/spec/helpers/milestones_helper_spec.rb
@@ -21,24 +21,22 @@ describe MilestonesHelper do
end
describe '#milestone_counts' do
- let(:project) { FactoryGirl.create(:project) }
+ let(:project) { create(:empty_project) }
let(:counts) { helper.milestone_counts(project.milestones) }
context 'when there are milestones' do
- let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) }
- let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) }
- let!(:milestone_3) { FactoryGirl.create(:closed_milestone, project: project) }
-
it 'returns the correct counts' do
+ create_list(:active_milestone, 2, project: project)
+ create(:closed_milestone, project: project)
+
expect(counts).to eq(opened: 2, closed: 1, all: 3)
end
end
context 'when there are only milestones of one type' do
- let!(:milestone_1) { FactoryGirl.create(:active_milestone, project: project) }
- let!(:milestone_2) { FactoryGirl.create(:active_milestone, project: project) }
-
it 'returns the correct counts' do
+ create_list(:active_milestone, 2, project: project)
+
expect(counts).to eq(opened: 2, closed: 0, all: 2)
end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 77841e85223..1f02e06e312 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -110,7 +110,7 @@ describe PreferencesHelper do
end
context 'when repository is not empty' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
it 'returns readme if user has repository access' do
allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true)
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 8113742923b..8d1570aa6f3 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -10,7 +10,7 @@ describe ProjectsHelper do
end
describe "can_change_visibility_level?" do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:project_member, :reporter, user: create(:user), project: project).user }
let(:fork_project) { Projects::ForkService.new(project, user).execute }
@@ -97,7 +97,7 @@ describe ProjectsHelper do
end
describe '#license_short_name' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context 'when project.repository has a license_key' do
it 'returns the nickname of the license if present' do
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 4b2ca3514f8..e51720f10ed 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -42,7 +42,7 @@ describe SearchHelper do
end
it "includes the user's projects" do
- project = create(:project, namespace: create(:namespace, owner: user))
+ project = create(:empty_project, namespace: create(:namespace, owner: user))
expect(search_autocomplete_opts(project.name).size).to eq(1)
end
@@ -52,7 +52,9 @@ describe SearchHelper do
end
context "with a current project" do
- before { @project = create(:project) }
+ before do
+ @project = create(:project, :repository)
+ end
it "includes project-specific sections" do
expect(search_autocomplete_opts("Files").size).to eq(1)
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index 37ac6a2699d..4da1569e59f 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -116,7 +116,7 @@ describe SubmoduleHelper do
context 'submodules with relative links' do
let(:group) { create(:group, name: "Master Project", path: "master-project") }
- let(:project) { create(:project, group: group) }
+ let(:project) { create(:empty_project, group: group) }
let(:commit_id) { sample_commit[:id] }
before do
@@ -145,7 +145,7 @@ describe SubmoduleHelper do
context 'personal project' do
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
it 'one level down with personal project' do
result = relative_self_links('../test.git', commit_id)
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index 8d6537ba4b5..9523d0f4aa6 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe TreeHelper do
describe 'flatten_tree' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
@repository = project.repository
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index db3ad1b99e9..8942b00b128 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe VisibilityLevelHelper do
- let(:project) { build(:project) }
+ let(:project) { build(:empty_project) }
let(:group) { build(:group) }
let(:personal_snippet) { build(:personal_snippet) }
let(:project_snippet) { build(:project_snippet) }
@@ -60,8 +60,8 @@ describe VisibilityLevelHelper do
describe "skip_level?" do
describe "forks" do
- let(:project) { create(:project, :internal) }
- let(:fork_project) { create(:project, forked_from_project: project) }
+ let(:project) { create(:empty_project, :internal) }
+ let(:fork_project) { create(:empty_project, forked_from_project: project) }
it "skips levels" do
expect(skip_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy
@@ -71,7 +71,7 @@ describe VisibilityLevelHelper do
end
describe "non-forked project" do
- let(:project) { create(:project, :internal) }
+ let(:project) { create(:empty_project, :internal) }
it "skips levels" do
expect(skip_level?(project, Gitlab::VisibilityLevel::PUBLIC)).to be_falsey
diff --git a/spec/initializers/metrics_spec.rb b/spec/initializers/metrics_spec.rb
new file mode 100644
index 00000000000..bb595162370
--- /dev/null
+++ b/spec/initializers/metrics_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+require_relative '../../config/initializers/metrics'
+
+describe 'instrument_classes', lib: true do
+ let(:config) { double(:config) }
+
+ before do
+ allow(config).to receive(:instrument_method)
+ allow(config).to receive(:instrument_methods)
+ allow(config).to receive(:instrument_instance_methods)
+ end
+
+ it 'can autoload and instrument all files' do
+ expect { instrument_classes(config) }.not_to raise_error
+ end
+end
diff --git a/spec/javascripts/abuse_reports_spec.js.es6 b/spec/javascripts/abuse_reports_spec.js.es6
index cf19aa05031..a2d57824585 100644
--- a/spec/javascripts/abuse_reports_spec.js.es6
+++ b/spec/javascripts/abuse_reports_spec.js.es6
@@ -21,7 +21,6 @@
messages = $('.abuse-reports .message');
});
-
it('should truncate long messages', () => {
const $longMessage = findMessage('LONG MESSAGE');
expect($longMessage.data('original-message')).toEqual(jasmine.anything());
diff --git a/spec/javascripts/environments/environment_rollback_spec.js.es6 b/spec/javascripts/environments/environment_rollback_spec.js.es6
index 21241116e29..95796f23894 100644
--- a/spec/javascripts/environments/environment_rollback_spec.js.es6
+++ b/spec/javascripts/environments/environment_rollback_spec.js.es6
@@ -33,7 +33,6 @@ describe('Rollback Component', () => {
expect(component.$el.querySelector('span').textContent).toContain('Re-deploy');
});
-
it('Should render Rollback label when isLastDeployment is false', () => {
const component = new window.gl.environmentsList.RollbackComponent({
el: document.querySelector('.test-dom-element'),
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6
index 6df7c0e44ef..9d9097419ea 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6
@@ -72,6 +72,12 @@
const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
expect(result).toEqual(tokenKeys[0]);
});
+
+ it('should return alternative tokenKey when found by key param', () => {
+ const tokenKeys = gl.FilteredSearchTokenKeys.getAlternatives();
+ const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ expect(result).toEqual(tokenKeys[0]);
+ });
});
describe('searchByConditionUrl', () => {
diff --git a/spec/javascripts/gfm_auto_complete_spec.js.es6 b/spec/javascripts/gfm_auto_complete_spec.js.es6
index 6b48d82cb23..99cebb32a8b 100644
--- a/spec/javascripts/gfm_auto_complete_spec.js.es6
+++ b/spec/javascripts/gfm_auto_complete_spec.js.es6
@@ -62,4 +62,30 @@ describe('GfmAutoComplete', function () {
});
});
});
+
+ describe('isLoading', function () {
+ it('should be true with loading data object item', function () {
+ expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true);
+ });
+
+ it('should be true with loading data array', function () {
+ expect(GfmAutoComplete.isLoading(['loading'])).toBe(true);
+ });
+
+ it('should be true with loading data object array', function () {
+ expect(GfmAutoComplete.isLoading([{ name: 'loading' }])).toBe(true);
+ });
+
+ it('should be false with actual array data', function () {
+ expect(GfmAutoComplete.isLoading([
+ { title: 'Foo' },
+ { title: 'Bar' },
+ { title: 'Qux' },
+ ])).toBe(false);
+ });
+
+ it('should be false with actual data item', function () {
+ expect(GfmAutoComplete.isLoading({ title: 'Foo' })).toBe(false);
+ });
+ });
});
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js.es6 b/spec/javascripts/lib/utils/common_utils_spec.js.es6
index 031f9ca03c9..1ce8f28e568 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js.es6
+++ b/spec/javascripts/lib/utils/common_utils_spec.js.es6
@@ -52,5 +52,22 @@
expect(value).toBe(null);
});
});
+
+ describe('gl.utils.normalizedHeaders', () => {
+ it('should upperCase all the header keys to keep them consistent', () => {
+ const apiHeaders = {
+ 'X-Something-Workhorse': { workhorse: 'ok' },
+ 'x-something-nginx': { nginx: 'ok' },
+ };
+
+ const normalized = gl.utils.normalizeHeaders(apiHeaders);
+
+ const WORKHORSE = 'X-SOMETHING-WORKHORSE';
+ const NGINX = 'X-SOMETHING-NGINX';
+
+ expect(normalized[WORKHORSE].workhorse).toBe('ok');
+ expect(normalized[NGINX].nginx).toBe('ok');
+ });
+ });
});
})();
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index 755dba19744..386fc8f514e 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -1,6 +1,7 @@
/* eslint-disable space-before-function-paren, no-return-assign, no-var, quotes */
/* global ShortcutsIssuable */
+/*= require copy_as_gfm */
/*= require shortcuts_issuable */
(function() {
@@ -14,10 +15,12 @@
});
return describe('#replyWithSelectedText', function() {
var stubSelection;
- // Stub window.getSelection to return the provided String.
- stubSelection = function(text) {
- return window.getSelection = function() {
- return text;
+ // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML.
+ stubSelection = function(html) {
+ window.gl.utils.getSelectedFragment = function() {
+ var node = document.createElement('div');
+ node.innerHTML = html;
+ return node;
};
};
beforeEach(function() {
@@ -32,13 +35,13 @@
});
describe('with any selection', function() {
beforeEach(function() {
- return stubSelection('Selected text.');
+ return stubSelection('<p>Selected text.</p>');
});
it('leaves existing input intact', function() {
$(this.selector).val('This text was already here.');
expect($(this.selector).val()).toBe('This text was already here.');
this.shortcut.replyWithSelectedText();
- return expect($(this.selector).val()).toBe("This text was already here.\n> Selected text.\n\n");
+ return expect($(this.selector).val()).toBe("This text was already here.\n\n> Selected text.\n\n");
});
it('triggers `input`', function() {
var triggered;
@@ -61,16 +64,16 @@
});
describe('with a one-line selection', function() {
return it('quotes the selection', function() {
- stubSelection('This text has been selected.');
+ stubSelection('<p>This text has been selected.</p>');
this.shortcut.replyWithSelectedText();
return expect($(this.selector).val()).toBe("> This text has been selected.\n\n");
});
});
return describe('with a multi-line selection', function() {
return it('quotes the selected lines as a group', function() {
- stubSelection("Selected line one.\n\nSelected line two.\nSelected line three.\n");
+ stubSelection("<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>");
this.shortcut.replyWithSelectedText();
- return expect($(this.selector).val()).toBe("> Selected line one.\n> Selected line two.\n> Selected line three.\n\n");
+ return expect($(this.selector).val()).toBe("> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n");
});
});
});
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index 9703e2315b8..deadc36524c 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:commit1) { project.commit("HEAD~2") }
let(:commit2) { project.commit }
@@ -99,7 +99,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
end
context 'cross-project / cross-namespace complete reference' do
- let(:project2) { create(:project, :public) }
+ let(:project2) { create(:project, :public, :repository) }
let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
@@ -133,8 +133,8 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
context 'cross-project / same-namespace complete reference' do
let(:namespace) { create(:namespace) }
- let(:project) { create(:project, :public, namespace: namespace) }
- let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
+ let(:project) { create(:project, :public, :repository, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, path: "same-namespace", namespace: namespace) }
let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
@@ -168,8 +168,8 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
context 'cross-project shorthand reference' do
let(:namespace) { create(:namespace) }
- let(:project) { create(:project, :public, namespace: namespace) }
- let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
+ let(:project) { create(:project, :public, :repository, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, path: "same-namespace", namespace: namespace) }
let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
@@ -203,7 +203,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
context 'cross-project URL reference' do
let(:namespace) { create(:namespace) }
- let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:range) { CommitRange.new("#{commit1.id}...master", project) }
let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') }
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index 2e6dcc3a434..a19aac61229 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::Filter::CommitReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:commit) { project.commit }
it 'requires project context' do
@@ -96,7 +96,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
context 'cross-project / cross-namespace complete reference' do
let(:namespace) { create(:namespace) }
- let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
@@ -122,7 +122,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
context 'cross-project / same-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
- let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
@@ -148,7 +148,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
context 'cross-project shorthand reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
- let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
@@ -173,7 +173,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
context 'cross-project URL reference' do
let(:namespace) { create(:namespace) }
- let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, :repository, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) }
diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
index fe2ce092e6b..082c0d4dd0d 100644
--- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
+++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::Filter::GollumTagsFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:user) { double }
let(:project_wiki) { ProjectWiki.new(project, user) }
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 275010c1a2c..3d3d36061f4 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -188,7 +188,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
- let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:merge) { create(:merge_request, source_project: project2, target_project: project2) }
let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' }
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 73b5edb99b3..a317c751d32 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:milestone) { create(:milestone, project: project) }
let(:reference) { milestone.to_reference }
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index df2dd173b57..1957ba739e2 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -25,7 +25,7 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
%(<a href="#{path}">#{path}</a>)
end
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:project_path) { project.path_with_namespace }
let(:ref) { 'markdown' }
let(:commit) { project.commit(ref) }
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index d265d29ee86..69e3c52b35a 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -6,21 +6,21 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
context "when no language is specified" do
it "highlights as plaintext" do
result = filter('<pre><code>def fun end</code></pre>')
- expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>def fun end</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code>def fun end</code></pre>')
end
end
context "when a valid language is specified" do
it "highlights as that language" do
result = filter('<pre><code class="ruby">def fun end</code></pre>')
- expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" v-pre="true"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" lang="ruby" v-pre="true"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
end
end
context "when an invalid language is specified" do
it "highlights as plaintext" do
result = filter('<pre><code class="gnuplot">This is a test</code></pre>')
- expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>This is a test</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code>This is a test</code></pre>')
end
end
@@ -31,7 +31,7 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
it "highlights as plaintext" do
result = filter('<pre><code class="ruby">This is a test</code></pre>')
- expect(result.to_html).to eq('<pre class="code highlight" v-pre="true"><code>This is a test</code></pre>')
+ expect(result.to_html).to eq('<pre class="code highlight" lang="" v-pre="true"><code>This is a test</code></pre>')
end
end
end
diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb
index 8b76c1d73c9..639cac6406a 100644
--- a/spec/lib/banzai/filter/upload_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb
@@ -29,7 +29,7 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do
%(<div><a href="#{path}">#{path}</a></div>)
end
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
shared_examples :preserve_unchanged do
it 'does not modify any relative URL in anchor' do
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index 5bfeb82e738..3e1ac9fb2b2 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -152,6 +152,30 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
end
end
+ context 'when a project is not specified' do
+ let(:project) { nil }
+
+ it 'does not link a User' do
+ doc = reference_filter("Hey #{reference}")
+
+ expect(doc).not_to include('a')
+ end
+
+ context 'when skip_project_check set to true' do
+ it 'links to a User' do
+ doc = reference_filter("Hey #{reference}", skip_project_check: true)
+
+ expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
+ end
+
+ it 'does not link users using @all reference' do
+ doc = reference_filter("Hey #{User.reference_prefix}all", skip_project_check: true)
+
+ expect(doc).not_to include('a')
+ end
+ end
+ end
+
describe '#namespaces' do
it 'returns a Hash containing all Namespaces' do
document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>")
diff --git a/spec/lib/banzai/filter/video_link_filter_spec.rb b/spec/lib/banzai/filter/video_link_filter_spec.rb
index 6ab1be9ccb7..00494f545a3 100644
--- a/spec/lib/banzai/filter/video_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/video_link_filter_spec.rb
@@ -13,7 +13,7 @@ describe Banzai::Filter::VideoLinkFilter, lib: true do
%(<img src="#{path}" />)
end
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
context 'when the element src has a video extension' do
UploaderHelper::VIDEO_EXT.each do |ext|
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index fafc2cec546..31ca9d27b0b 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -147,7 +147,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
describe '#nodes_user_can_reference' do
context 'when the link has a data-author attribute' do
it 'returns the nodes when the user is a member of the project' do
- other_project = create(:project)
+ other_project = create(:empty_project)
other_project.team << [user, :developer]
link['data-project'] = other_project.id.to_s
@@ -164,7 +164,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
end
it 'returns an empty Array when the user could not be found' do
- other_project = create(:project)
+ other_project = create(:empty_project)
link['data-project'] = other_project.id.to_s
link['data-author'] = ''
@@ -173,7 +173,7 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
end
it 'returns an empty Array when the user is not a team member' do
- other_project = create(:project)
+ other_project = create(:empty_project)
link['data-project'] = other_project.id.to_s
link['data-author'] = user.id.to_s
diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb
index 94266f6653b..a5251e9a8c2 100644
--- a/spec/lib/constraints/project_url_constrainer_spec.rb
+++ b/spec/lib/constraints/project_url_constrainer_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe ProjectUrlConstrainer, lib: true do
- let!(:project) { create(:project) }
+ let!(:project) { create(:empty_project) }
let!(:namespace) { project.namespace }
describe '#matches?' do
diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb
index ec2f66b1136..e3066311b7d 100644
--- a/spec/lib/event_filter_spec.rb
+++ b/spec/lib/event_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe EventFilter, lib: true do
describe '#apply_filter' do
let(:source_user) { create(:user) }
- let!(:public_project) { create(:project, :public) }
+ let!(:public_project) { create(:empty_project, :public) }
let!(:push_event) { create(:event, action: Event::PUSHED, project: public_project, target: public_project, author: source_user) }
let!(:merged_event) { create(:event, action: Event::MERGED, project: public_project, target: public_project, author: source_user) }
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 0e85e302f29..29c07655ae8 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -24,7 +24,7 @@ describe ExtractsPath, lib: true do
let(:params) { { path: sample_commit[:line_code_path], ref: ref } }
before do
- @project = create(:project)
+ @project = create(:project, :repository)
end
it "log tree path has no escape sequences" do
diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/build/metadata_spec.rb
index d678e522721..9df96ea04eb 100644
--- a/spec/lib/gitlab/badge/build/metadata_spec.rb
+++ b/spec/lib/gitlab/badge/build/metadata_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'lib/gitlab/badge/shared/metadata'
describe Gitlab::Badge::Build::Metadata do
- let(:badge) { double(project: create(:project), ref: 'feature') }
+ let(:badge) { double(project: create(:empty_project), ref: 'feature') }
let(:metadata) { described_class.new(badge) }
it_behaves_like 'badge metadata'
diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb
index 70f03021d36..3c5414701a7 100644
--- a/spec/lib/gitlab/badge/build/status_spec.rb
+++ b/spec/lib/gitlab/badge/build/status_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Badge::Build::Status do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:sha) { project.commit.sha }
let(:branch) { 'master' }
let(:badge) { described_class.new(project, branch) }
diff --git a/spec/lib/gitlab/badge/coverage/metadata_spec.rb b/spec/lib/gitlab/badge/coverage/metadata_spec.rb
index 74eaf7eaf8b..5e93935ea37 100644
--- a/spec/lib/gitlab/badge/coverage/metadata_spec.rb
+++ b/spec/lib/gitlab/badge/coverage/metadata_spec.rb
@@ -3,7 +3,7 @@ require 'lib/gitlab/badge/shared/metadata'
describe Gitlab::Badge::Coverage::Metadata do
let(:badge) do
- double(project: create(:project), ref: 'feature', job: 'test')
+ double(project: create(:empty_project), ref: 'feature', job: 'test')
end
let(:metadata) { described_class.new(badge) }
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 72b1ba36b58..0a2fe5af2c3 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -52,7 +52,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
let(:project) do
create(
- :project,
+ :empty_project,
import_source: project_identifier,
import_data: ProjectImportData.new(credentials: data)
)
diff --git a/spec/lib/gitlab/blame_spec.rb b/spec/lib/gitlab/blame_spec.rb
index 89245761b6f..26b1baf75be 100644
--- a/spec/lib/gitlab/blame_spec.rb
+++ b/spec/lib/gitlab/blame_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Blame, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:path) { 'files/ruby/popen.rb' }
let(:commit) { project.commit('master') }
let(:blob) { project.repository.blob_at(commit.id, path) }
diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb
index a2d84977f58..1e81eaef18c 100644
--- a/spec/lib/gitlab/chat_commands/command_spec.rb
+++ b/spec/lib/gitlab/chat_commands/command_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::ChatCommands::Command, service: true do
context 'when no command is available' do
let(:params) { { text: 'issue show 1' } }
- let(:project) { create(:project, has_external_issue_tracker: true) }
+ let(:project) { create(:empty_project, has_external_issue_tracker: true) }
it 'displays 404 messages' do
expect(subject[:response_type]).to be(:ephemeral)
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 98effecdbbc..cadfbadca10 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Checks::ChangeAccess, lib: true do
describe '#exec' do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user_access) { Gitlab::UserAccess.new(user, project: project) }
let(:changes) do
{
diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb
index f6288011494..7a84bbebd02 100644
--- a/spec/lib/gitlab/checks/force_push_spec.rb
+++ b/spec/lib/gitlab/checks/force_push_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Checks::ChangeAccess, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
context "exit code checking" do
it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do
diff --git a/spec/lib/gitlab/ci/trace_reader_spec.rb b/spec/lib/gitlab/ci/trace_reader_spec.rb
index f06d78694d6..ff5551bf703 100644
--- a/spec/lib/gitlab/ci/trace_reader_spec.rb
+++ b/spec/lib/gitlab/ci/trace_reader_spec.rb
@@ -11,13 +11,25 @@ describe Gitlab::Ci::TraceReader do
last_lines = random_lines
expected = lines.last(last_lines).join
+ result = subject.read(last_lines: last_lines)
- expect(subject.read(last_lines: last_lines)).to eq(expected)
+ expect(result).to eq(expected)
+ expect(result.encoding).to eq(Encoding.default_external)
end
end
it 'returns everything if trying to get too many lines' do
- expect(build_subject.read(last_lines: lines.size * 2)).to eq(lines.join)
+ result = build_subject.read(last_lines: lines.size * 2)
+
+ expect(result).to eq(lines.join)
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+
+ it 'returns all contents if last_lines is not specified' do
+ result = build_subject.read
+
+ expect(result).to eq(lines.join)
+ expect(result.encoding).to eq(Encoding.default_external)
end
it 'raises an error if not passing an integer for last_lines' do
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 1bbaca0739a..97af1c2523d 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
describe Gitlab::ClosingIssueExtractor, lib: true do
- let(:project) { create(:project) }
- let(:project2) { create(:project) }
+ let(:project) { create(:empty_project) }
+ let(:project2) { create(:empty_project) }
let(:forked_project) { Projects::ForkService.new(project, project.creator).execute }
- let(:issue) { create(:issue, project: project) }
- let(:issue2) { create(:issue, project: project2) }
+ let(:issue) { create(:issue, project: project) }
+ let(:issue2) { create(:issue, project: project2) }
let(:reference) { issue.to_reference }
let(:cross_reference) { issue2.to_reference(project) }
let(:fork_cross_reference) { issue.to_reference(forked_project) }
diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb
index 648d342ecf8..fbf679c5215 100644
--- a/spec/lib/gitlab/conflict/file_spec.rb
+++ b/spec/lib/gitlab/conflict/file_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Conflict::File, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:rugged) { repository.rugged }
let(:their_commit) { rugged.branches['conflict-start'].target }
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 004341ffd02..b01c4805a34 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -1,36 +1,64 @@
require 'spec_helper'
describe Gitlab::CurrentSettings do
+ include StubENV
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+ end
+
describe '#current_application_settings' do
- it 'attempts to use cached values first' do
- allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
- expect(ApplicationSetting).to receive(:current).and_return(::ApplicationSetting.create_from_defaults)
- expect(ApplicationSetting).not_to receive(:last)
+ context 'with DB available' do
+ before do
+ allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
+ end
- expect(current_application_settings).to be_a(ApplicationSetting)
- end
+ it 'attempts to use cached values first' do
+ expect(ApplicationSetting).to receive(:current)
+ expect(ApplicationSetting).not_to receive(:last)
+
+ expect(current_application_settings).to be_a(ApplicationSetting)
+ end
- it 'does not attempt to connect to DB or Redis' do
- allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false)
- expect(ApplicationSetting).not_to receive(:current)
- expect(ApplicationSetting).not_to receive(:last)
+ it 'falls back to DB if Redis returns an empty value' do
+ expect(ApplicationSetting).to receive(:last).and_call_original
- expect(current_application_settings).to eq fake_application_settings
+ expect(current_application_settings).to be_a(ApplicationSetting)
+ end
+
+ it 'falls back to DB if Redis fails' do
+ expect(ApplicationSetting).to receive(:current).and_raise(::Redis::BaseError)
+ expect(ApplicationSetting).to receive(:last).and_call_original
+
+ expect(current_application_settings).to be_a(ApplicationSetting)
+ end
end
- it 'falls back to DB if Redis returns an empty value' do
- allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
- expect(ApplicationSetting).to receive(:last).and_call_original
+ context 'with DB unavailable' do
+ before do
+ allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false)
+ end
- expect(current_application_settings).to be_a(ApplicationSetting)
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).not_to receive(:current)
+ expect(ApplicationSetting).not_to receive(:last)
+
+ expect(current_application_settings).to be_a(OpenStruct)
+ end
end
- it 'falls back to DB if Redis fails' do
- allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
- expect(ApplicationSetting).to receive(:current).and_raise(::Redis::BaseError)
- expect(ApplicationSetting).to receive(:last).and_call_original
+ context 'when ENV["IN_MEMORY_APPLICATION_SETTINGS"] is true' do
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'true')
+ end
+
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).not_to receive(:current)
+ expect(ApplicationSetting).not_to receive(:last)
- expect(current_application_settings).to be_a(ApplicationSetting)
+ expect(current_application_settings).to be_a(ApplicationSetting)
+ expect(current_application_settings).not_to be_persisted
+ end
end
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
index fb6b6c4a8d2..3dd76ba5b8a 100644
--- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::CycleAnalytics::StageSummary, models: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from) { 1.day.ago }
let(:user) { create(:user, :admin) }
subject { described_class.new(project, from: Time.now, current_user: user).data }
@@ -15,7 +15,7 @@ describe Gitlab::CycleAnalytics::StageSummary, models: true do
end
it "doesn't find issues from other projects" do
- Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project)) }
+ Timecop.freeze(5.days.from_now) { create(:issue, project: create(:empty_project)) }
expect(subject.first[:value]).to eq(0)
end
@@ -30,7 +30,7 @@ describe Gitlab::CycleAnalytics::StageSummary, models: true do
end
it "doesn't find commits from other projects" do
- Timecop.freeze(5.days.from_now) { create_commit("Test message", create(:project), user, 'master') }
+ Timecop.freeze(5.days.from_now) { create_commit("Test message", create(:project, :repository), user, 'master') }
expect(subject.second[:value]).to eq(0)
end
@@ -51,7 +51,9 @@ describe Gitlab::CycleAnalytics::StageSummary, models: true do
end
it "doesn't find commits from other projects" do
- Timecop.freeze(5.days.from_now) { create(:deployment, project: create(:project)) }
+ Timecop.freeze(5.days.from_now) do
+ create(:deployment, project: create(:project, :repository))
+ end
expect(subject.third[:value]).to eq(0)
end
diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb
index 9a4dec91e56..04ec34492e1 100644
--- a/spec/lib/gitlab/data_builder/note_spec.rb
+++ b/spec/lib/gitlab/data_builder/note_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::DataBuilder::Note, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:data) { described_class.build(note, user) }
let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index a68f5943a6a..f13041e498c 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::DataBuilder::Pipeline do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline,
diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb
index a379f798a16..dbcfb9b7400 100644
--- a/spec/lib/gitlab/data_builder/push_spec.rb
+++ b/spec/lib/gitlab/data_builder/push_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::DataBuilder::Push, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
describe '.build_sample' do
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 38475792d93..050689b7c9a 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Diff::File, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:commit) { project.commit(sample_commit.id) }
let(:diff) { commit.raw_diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index 1c2ddeed692..1e21270d928 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Diff::Highlight, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:commit) { project.commit(sample_commit.id) }
let(:diff) { commit.raw_diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
diff --git a/spec/lib/gitlab/diff/line_mapper_spec.rb b/spec/lib/gitlab/diff/line_mapper_spec.rb
index 4b943fa382d..2c7ecd1907e 100644
--- a/spec/lib/gitlab/diff/line_mapper_spec.rb
+++ b/spec/lib/gitlab/diff/line_mapper_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Diff::LineMapper, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:commit) { project.commit(sample_commit.id) }
let(:diffs) { commit.raw_diffs }
diff --git a/spec/lib/gitlab/diff/parallel_diff_spec.rb b/spec/lib/gitlab/diff/parallel_diff_spec.rb
index af18d3c25a6..fe5fa048413 100644
--- a/spec/lib/gitlab/diff/parallel_diff_spec.rb
+++ b/spec/lib/gitlab/diff/parallel_diff_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Diff::ParallelDiff, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:commit) { project.commit(sample_commit.id) }
let(:diffs) { commit.raw_diffs }
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index 6e8fff6f516..cdf0af6d7ef 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Diff::Position, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
describe "position for an added file" do
let(:commit) { project.commit("2ea1f3dec713d940208fb5ce4a38765ecb5d3f73") }
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index c268f84c759..f5822fed37c 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -51,7 +51,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:current_user) { project.owner }
let(:repository) { project.repository }
let(:file_name) { "test-file" }
diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
index 08897a4c310..4a9c9a7fe34 100644
--- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::Email::Handler::CreateIssueHandler, lib: true do
let(:email_raw) { fixture_file('emails/valid_new_issue.eml') }
let(:namespace) { create(:namespace, path: 'gitlabhq') }
- let!(:project) { create(:project, :public, namespace: namespace) }
+ let!(:project) { create(:project, :public, :repository, namespace: namespace) }
let!(:user) do
create(
:user,
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index cebbeff50cf..17a4ef25210 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do
end
let(:email_raw) { fixture_file('emails/valid_reply.eml') }
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:user) { create(:user) }
let(:note) { create(:diff_note_on_merge_request, project: project) }
let(:noteable) { note.noteable }
diff --git a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
index a444257754b..0939e6c4514 100644
--- a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::Email::Handler::UnsubscribeHandler, lib: true do
end
let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "#{mail_key}+unsubscribe") }
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
let(:noteable) { create(:issue, project: project) }
diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb
index 5b966bddb6a..7b3291b8315 100644
--- a/spec/lib/gitlab/email/message/repository_push_spec.rb
+++ b/spec/lib/gitlab/email/message/repository_push_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::Email::Message::RepositoryPush do
include RepoHelpers
let!(:group) { create(:group, name: 'my_group') }
- let!(:project) { create(:project, name: 'my_project', namespace: group) }
+ let!(:project) { create(:project, :repository, name: 'my_project', namespace: group) }
let!(:author) { create(:author, name: 'Author') }
let(:message) do
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index f4703dc704f..5d416c9eec3 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
describe Gitlab::Gfm::ReferenceRewriter do
let(:text) { 'some text' }
- let(:old_project) { create(:project, name: 'old') }
- let(:new_project) { create(:project, name: 'new') }
+ let(:old_project) { create(:empty_project, name: 'old-project') }
+ let(:new_project) { create(:empty_project, name: 'new-project') }
let(:user) { create(:user) }
before { old_project.team << [user, :reporter] }
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 6eca33f9fee..c3016f63ebf 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
describe Gitlab::Gfm::UploadsRewriter do
let(:user) { create(:user) }
- let(:old_project) { create(:project) }
- let(:new_project) { create(:project) }
+ let(:old_project) { create(:empty_project) }
+ let(:new_project) { create(:empty_project) }
let(:rewriter) { described_class.new(text, old_project, user) }
context 'text contains links to uploads' do
diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb
index d1f947b6850..3f279c21865 100644
--- a/spec/lib/gitlab/git/hook_spec.rb
+++ b/spec/lib/gitlab/git/hook_spec.rb
@@ -3,7 +3,7 @@ require 'fileutils'
describe Gitlab::Git::Hook, lib: true do
describe "#trigger" do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
def create_hook(name)
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
index 1f9c987be0b..d48629a296d 100644
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ b/spec/lib/gitlab/git/rev_list_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Git::RevList, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
context "validations" do
described_class::ALLOWED_VARIABLES.each do |var|
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 44b84afde47..b080be62b34 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::GitAccess, lib: true do
let(:access) { Gitlab::GitAccess.new(actor, project, 'web', authentication_abilities: authentication_abilities) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:actor) { user }
let(:authentication_abilities) do
@@ -88,7 +88,7 @@ describe Gitlab::GitAccess, lib: true do
end
context 'when project is public' do
- let(:public_project) { create(:project, :public) }
+ let(:public_project) { create(:project, :public, :repository) }
let(:guest_access) { Gitlab::GitAccess.new(nil, public_project, 'web', authentication_abilities: []) }
subject { guest_access.check('git-upload-pack', '_any') }
@@ -124,19 +124,19 @@ describe Gitlab::GitAccess, lib: true do
context 'when unauthorized' do
context 'from public project' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
it { expect(subject).to be_allowed }
end
context 'from internal project' do
- let(:project) { create(:project, :internal) }
+ let(:project) { create(:project, :internal, :repository) }
it { expect(subject).not_to be_allowed }
end
context 'from private project' do
- let(:project) { create(:project, :private) }
+ let(:project) { create(:project, :private, :repository) }
it { expect(subject).not_to be_allowed }
end
@@ -148,7 +148,7 @@ describe Gitlab::GitAccess, lib: true do
let(:authentication_abilities) { build_authentication_abilities }
describe 'owner' do
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:project, :repository, namespace: user.namespace) }
context 'pull code' do
it { expect(subject).to be_allowed }
@@ -364,19 +364,19 @@ describe Gitlab::GitAccess, lib: true do
context 'when unauthorized' do
context 'to public project' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
it { expect(subject).not_to be_allowed }
end
context 'to internal project' do
- let(:project) { create(:project, :internal) }
+ let(:project) { create(:project, :internal, :repository) }
it { expect(subject).not_to be_allowed }
end
context 'to private project' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :private, :repository) }
it { expect(subject).not_to be_allowed }
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index a5d172233cc..4a0cdc6887e 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::GitAccessWiki, lib: true do
let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:authentication_abilities) do
[
diff --git a/spec/lib/gitlab/github_import/branch_formatter_spec.rb b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
index 462caa5b5fe..36e7d739f7e 100644
--- a/spec/lib/gitlab/github_import/branch_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GithubImport::BranchFormatter, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:commit) { create(:commit, project: project) }
let(:repo) { double }
let(:raw) do
diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb
index c520a9c53ad..e6e33d3686a 100644
--- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GithubImport::CommentFormatter, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') }
let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') }
diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
index e31ed9c1fa0..eec1fabab54 100644
--- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GithubImport::IssueFormatter, lib: true do
- let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
+ let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) }
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb
index 8098754d735..10449ef5fcb 100644
--- a/spec/lib/gitlab/github_import/label_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GithubImport::LabelFormatter, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:raw) { double(name: 'improvements', color: 'e6e6e6') }
subject { described_class.new(project, raw) }
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index 2b3256edcb2..90947ff4707 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:source_sha) { create(:commit, project: project).id }
let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id }
let(:repository) { double(id: 1, fork: false) }
diff --git a/spec/lib/gitlab/github_import/release_formatter_spec.rb b/spec/lib/gitlab/github_import/release_formatter_spec.rb
index 793128c6ab9..13b15e669ab 100644
--- a/spec/lib/gitlab/github_import/release_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/release_formatter_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::GithubImport::ReleaseFormatter, lib: true do
- let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
+ let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) }
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 097861fd34d..ccaa88a5c79 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::GoogleCodeImport::Importer, lib: true do
'user_map' => { 'thilo...' => "@#{mapped_user.username}" }
}
end
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
subject { described_class.new(project) }
diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb
index f5c064303ad..abb5a26060f 100644
--- a/spec/lib/gitlab/graphs/commits_spec.rb
+++ b/spec/lib/gitlab/graphs/commits_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Graphs::Commits, lib: true do
- let!(:project) { create(:project, :public, :empty_repo) }
+ let!(:project) { create(:empty_project, :public) }
let!(:commit1) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: Time.now) }
let!(:commit1_yesterday) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: 1.day.ago)}
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index fc021416d92..fadfe4d378e 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Highlight, lib: true do
include RepoHelpers
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:commit) { project.commit(sample_commit.id) }
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
index d6409a29550..53f7d244d88 100644
--- a/spec/lib/gitlab/import_export/import_export_spec.rb
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport, services: true do
describe 'export filename' do
- let(:project) { create(:project, :public, path: 'project-path') }
+ let(:project) { create(:empty_project, :public, path: 'project-path') }
it 'contains the project path' do
expect(described_class.export_filename(project: project)).to include(project.path)
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index b069696b5c7..0b7984d6ca9 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper, services: true do
describe 'map members' do
let(:user) { create(:admin, authorized_projects_populated: true) }
- let(:project) { create(:project, :public, name: 'searchable_project') }
+ let(:project) { create(:empty_project, :public, name: 'searchable_project') }
let(:user2) { create(:user, authorized_projects_populated: true) }
let(:exported_user_id) { 99 }
let(:exported_members) do
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index c8bba553558..d480c3821ec 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -151,6 +151,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
project = create(:project,
:public,
+ :repository,
issues: [issue],
snippets: [snippet],
releases: [release],
diff --git a/spec/lib/gitlab/import_export/repo_bundler_spec.rb b/spec/lib/gitlab/import_export/repo_bundler_spec.rb
index 135e99bc953..d39ea60ff7f 100644
--- a/spec/lib/gitlab/import_export/repo_bundler_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_bundler_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::RepoSaver, services: true do
describe 'bundle a project Git repo' do
let(:user) { create(:user) }
- let!(:project) { create(:project, :public, name: 'searchable_project') }
+ let!(:project) { create(:empty_project, :public, name: 'searchable_project') }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
let(:bundler) { described_class.new(project: project, shared: shared) }
diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb
index b628da0f3e8..47d5d2fc150 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::WikiRepoSaver, services: true do
describe 'bundle a wiki Git repo' do
let(:user) { create(:user) }
- let!(:project) { create(:project, :public, name: 'searchable_project') }
+ let!(:project) { create(:empty_project, :public, name: 'searchable_project') }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
let(:wiki_bundler) { described_class.new(project: project, shared: shared) }
diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb
new file mode 100644
index 00000000000..780f5b1f8d7
--- /dev/null
+++ b/spec/lib/gitlab/job_waiter_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe Gitlab::JobWaiter do
+ describe '#wait' do
+ let(:waiter) { described_class.new(%w(a)) }
+ it 'returns when all jobs have been completed' do
+ expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a)).
+ and_return(true)
+
+ expect(waiter).not_to receive(:sleep)
+
+ waiter.wait
+ end
+
+ it 'sleeps between checking the job statuses' do
+ expect(Gitlab::SidekiqStatus).to receive(:all_completed?).
+ with(%w(a)).
+ and_return(false, true)
+
+ expect(waiter).to receive(:sleep).with(described_class::INTERVAL)
+
+ waiter.wait
+ end
+
+ it 'returns when timing out' do
+ expect(waiter).not_to receive(:sleep)
+ waiter.wait(0)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index d94eb52f838..92e3624a8d8 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ProjectSearchResults, lib: true do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:query) { 'hello world' }
describe 'initialize with empty ref' do
@@ -22,6 +22,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
describe 'blob search' do
+ let(:project) { create(:project, :repository) }
let(:results) { described_class.new(user, project, 'files').objects('blobs') }
it 'finds by name' do
@@ -74,6 +75,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
describe 'confidential issues' do
+ let(:project) { create(:empty_project) }
let(:query) { 'issue' }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
@@ -187,7 +189,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
#
shared_examples 'access restricted commits' do
context 'when project is internal' do
- let(:project) { create(:project, :internal) }
+ let(:project) { create(:project, :internal, :repository) }
it 'does not search if user is not authenticated' do
commits = described_class.new(nil, project, search_phrase).objects('commits')
@@ -204,7 +206,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
context 'when project is private' do
let!(:creator) { create(:user, username: 'private-project-author') }
- let!(:private_project) { create(:project, :private, creator: creator, namespace: creator.namespace) }
+ let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) }
let(:team_master) do
user = create(:user, username: 'private-project-master')
private_project.team << [user, :master]
@@ -246,7 +248,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
describe 'commit search' do
context 'by commit message' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:commit) { project.repository.commit('59e29889be61e6e0e5e223bfa9ac2721d31605b8') }
let(:message) { 'Sorry, I did a mistake' }
@@ -269,7 +271,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
context 'by commit hash' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:commit) { project.repository.commit('0b4bc9a') }
commit_hashes = { short: '0b4bc9a', full: '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index bf0ab9635fd..6b689c41ef6 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -1,9 +1,11 @@
require 'spec_helper'
describe Gitlab::ReferenceExtractor, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
- before { project.team << [project.creator, :developer] }
+ before do
+ project.team << [project.creator, :developer]
+ end
subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
@@ -78,22 +80,27 @@ describe Gitlab::ReferenceExtractor, lib: true do
end
it 'accesses valid commits' do
+ project = create(:project, :repository) { |p| p.add_developer(p.creator) }
commit = project.commit('master')
- subject.analyze("this references commits #{commit.sha[0..6]} and 012345")
- extracted = subject.commits
+ extractor = described_class.new(project, project.creator)
+ extractor.analyze("this references commits #{commit.sha[0..6]} and 012345")
+ extracted = extractor.commits
+
expect(extracted.size).to eq(1)
expect(extracted[0].sha).to eq(commit.sha)
expect(extracted[0].message).to eq(commit.message)
end
it 'accesses valid commit ranges' do
+ project = create(:project, :repository) { |p| p.add_developer(p.creator) }
commit = project.commit('master')
earlier_commit = project.commit('master~2')
- subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}")
+ extractor = described_class.new(project, project.creator)
+ extractor.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}")
+ extracted = extractor.commit_ranges
- extracted = subject.commit_ranges
expect(extracted.size).to eq(1)
expect(extracted.first).to be_kind_of(CommitRange)
expect(extracted.first.commit_from).to eq earlier_commit
@@ -102,7 +109,6 @@ describe Gitlab::ReferenceExtractor, lib: true do
context 'with an external issue tracker' do
let(:project) { create(:jira_project) }
- subject { described_class.new(project, project.creator) }
it 'returns JIRA issues for a JIRA-integrated project' do
subject.analyze('JIRA-123 and FOOBAR-4567')
@@ -112,7 +118,7 @@ describe Gitlab::ReferenceExtractor, lib: true do
end
context 'with a project with an underscore' do
- let(:other_project) { create(:project, path: 'test_project') }
+ let(:other_project) { create(:empty_project, path: 'test_project') }
let(:issue) { create(:issue, project: other_project) }
before do
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index 9614aad3e73..847fb977400 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::SearchResults do
let(:user) { create(:user) }
- let!(:project) { create(:project, name: 'foo') }
+ let!(:project) { create(:empty_project, name: 'foo') }
let!(:issue) { create(:issue, project: project, title: 'foo') }
let!(:merge_request) do
diff --git a/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
new file mode 100644
index 00000000000..287bf62d9bd
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqStatus::ClientMiddleware do
+ describe '#call' do
+ it 'tracks the job in Redis' do
+ expect(Gitlab::SidekiqStatus).to receive(:set).with('123')
+
+ described_class.new.
+ call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
new file mode 100644
index 00000000000..80728197b8c
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqStatus::ServerMiddleware do
+ describe '#call' do
+ it 'stops tracking of a job upon completion' do
+ expect(Gitlab::SidekiqStatus).to receive(:unset).with('123')
+
+ ret = described_class.new.
+ call(double(:worker), { 'jid' => '123' }, double(:queue)) { 10 }
+
+ expect(ret).to eq(10)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_status_spec.rb b/spec/lib/gitlab/sidekiq_status_spec.rb
new file mode 100644
index 00000000000..0aa36a3416b
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_status_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqStatus do
+ describe '.set', :redis do
+ it 'stores the job ID' do
+ described_class.set('123')
+
+ key = described_class.key_for('123')
+
+ Sidekiq.redis do |redis|
+ expect(redis.exists(key)).to eq(true)
+ expect(redis.ttl(key) > 0).to eq(true)
+ end
+ end
+ end
+
+ describe '.unset', :redis do
+ it 'removes the job ID' do
+ described_class.set('123')
+ described_class.unset('123')
+
+ key = described_class.key_for('123')
+
+ Sidekiq.redis do |redis|
+ expect(redis.exists(key)).to eq(false)
+ end
+ end
+ end
+
+ describe '.all_completed?', :redis do
+ it 'returns true if all jobs have been completed' do
+ expect(described_class.all_completed?(%w(123))).to eq(true)
+ end
+
+ it 'returns false if a job has not yet been completed' do
+ described_class.set('123')
+
+ expect(described_class.all_completed?(%w(123 456))).to eq(false)
+ end
+ end
+
+ describe '.key_for' do
+ it 'returns the key for a job ID' do
+ key = described_class.key_for('123')
+
+ expect(key).to be_an_instance_of(String)
+ expect(key).to include('123')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb
index d2d334e6413..45cec65a284 100644
--- a/spec/lib/gitlab/template/issue_template_spec.rb
+++ b/spec/lib/gitlab/template/issue_template_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::Template::IssueTemplate do
subject { described_class }
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:file_path_1) { '.gitlab/issue_templates/bug.md' }
let(:file_path_2) { '.gitlab/issue_templates/template_test.md' }
let(:file_path_3) { '.gitlab/issue_templates/feature_proposal.md' }
diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb
index ddf68c4cf78..ae51b79be22 100644
--- a/spec/lib/gitlab/template/merge_request_template_spec.rb
+++ b/spec/lib/gitlab/template/merge_request_template_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::Template::MergeRequestTemplate do
subject { described_class }
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:file_path_1) { '.gitlab/merge_request_templates/bug.md' }
let(:file_path_2) { '.gitlab/merge_request_templates/template_test.md' }
let(:file_path_3) { '.gitlab/merge_request_templates/feature_proposal.md' }
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index a826b24419a..3fe8cf43934 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -99,7 +99,7 @@ describe Gitlab::UrlBuilder, lib: true do
context 'on another object' do
it 'returns a proper URL' do
- project = build_stubbed(:project)
+ project = build_stubbed(:empty_project)
expect { described_class.build(project) }.
to raise_error(NotImplementedError, 'No URL builder defined for Project')
diff --git a/spec/lib/gitlab/view/presenter/delegated_spec.rb b/spec/lib/gitlab/view/presenter/delegated_spec.rb
index 888ab80cad5..e9d4af54389 100644
--- a/spec/lib/gitlab/view/presenter/delegated_spec.rb
+++ b/spec/lib/gitlab/view/presenter/delegated_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::View::Presenter::Delegated do
- let(:project) { double(:project, bar: 'baz') }
+ let(:project) { double(:project, user: 'John Doe') }
let(:presenter_class) do
Class.new(described_class)
end
@@ -12,10 +12,14 @@ describe Gitlab::View::Presenter::Delegated do
describe '#initialize' do
it 'takes arbitrary key/values and exposes them' do
- presenter = presenter_class.new(project, user: 'user', foo: 'bar')
+ presenter = presenter_class.new(project, current_user: 'Jane Doe')
- expect(presenter.user).to eq('user')
- expect(presenter.foo).to eq('bar')
+ expect(presenter.current_user).to eq('Jane Doe')
+ end
+
+ it 'raise an error if the presentee already respond to method' do
+ expect { presenter_class.new(project, user: 'Jane Doe') }.
+ to raise_error Gitlab::View::Presenter::CannotOverrideMethodError
end
end
@@ -23,7 +27,7 @@ describe Gitlab::View::Presenter::Delegated do
it 'forwards missing methods to subject' do
presenter = presenter_class.new(project)
- expect(presenter.bar).to eq('baz')
+ expect(presenter.user).to eq('John Doe')
end
end
end
diff --git a/spec/lib/gitlab/view/presenter/factory_spec.rb b/spec/lib/gitlab/view/presenter/factory_spec.rb
index 55c5ecbf92f..70d2e22b48f 100644
--- a/spec/lib/gitlab/view/presenter/factory_spec.rb
+++ b/spec/lib/gitlab/view/presenter/factory_spec.rb
@@ -22,13 +22,6 @@ describe Gitlab::View::Presenter::Factory do
end
describe '#fabricate!' do
- it 'exposes given params' do
- presenter = described_class.new(build, user: 'user', foo: 'bar').fabricate!
-
- expect(presenter.user).to eq('user')
- expect(presenter.foo).to eq('bar')
- end
-
it 'detects the presenter based on the given subject' do
presenter = described_class.new(build).fabricate!
diff --git a/spec/lib/gitlab/view/presenter/simple_spec.rb b/spec/lib/gitlab/view/presenter/simple_spec.rb
index b489bdf1981..1795ed2405b 100644
--- a/spec/lib/gitlab/view/presenter/simple_spec.rb
+++ b/spec/lib/gitlab/view/presenter/simple_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::View::Presenter::Simple do
- let(:project) { double(:project) }
+ let(:project) { double(:project, user: 'John Doe') }
let(:presenter_class) do
Class.new(described_class)
end
@@ -12,10 +12,15 @@ describe Gitlab::View::Presenter::Simple do
describe '#initialize' do
it 'takes arbitrary key/values and exposes them' do
- presenter = presenter_class.new(project, user: 'user', foo: 'bar')
+ presenter = presenter_class.new(project, current_user: 'Jane Doe')
- expect(presenter.user).to eq('user')
- expect(presenter.foo).to eq('bar')
+ expect(presenter.current_user).to eq('Jane Doe')
+ end
+
+ it 'override the presentee attributes' do
+ presenter = presenter_class.new(project, user: 'Jane Doe')
+
+ expect(presenter.user).to eq('Jane Doe')
end
end
@@ -23,7 +28,7 @@ describe Gitlab::View::Presenter::Simple do
it 'does not forward missing methods to subject' do
presenter = presenter_class.new(project)
- expect { presenter.foo }.to raise_error(NoMethodError)
+ expect { presenter.user }.to raise_error(NoMethodError)
end
end
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 4b1cd466677..7dd4d76d1a3 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Workhorse, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
def decode_workhorse_header(array)
diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb
index a826b24419a..3fe8cf43934 100644
--- a/spec/lib/light_url_builder_spec.rb
+++ b/spec/lib/light_url_builder_spec.rb
@@ -99,7 +99,7 @@ describe Gitlab::UrlBuilder, lib: true do
context 'on another object' do
it 'returns a proper URL' do
- project = build_stubbed(:project)
+ project = build_stubbed(:empty_project)
expect { described_class.build(project) }.
to raise_error(NotImplementedError, 'No URL builder defined for Project')
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
index f227926f39c..5892f3481a4 100644
--- a/spec/lib/repository_cache_spec.rb
+++ b/spec/lib/repository_cache_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe RepositoryCache, lib: true do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:backend) { double('backend').as_null_object }
let(:cache) { RepositoryCache.new('example', project.id, backend) }
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 1bdf005c823..2f4a33a1868 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Ability, lib: true do
describe '.can_edit_note?' do
let(:project) { create(:empty_project) }
- let!(:note) { create(:note_on_issue, project: project) }
+ let(:note) { create(:note_on_issue, project: project) }
context 'using an anonymous user' do
it 'returns false' do
@@ -60,7 +60,7 @@ describe Ability, lib: true do
describe '.users_that_can_read_project' do
context 'using a public project' do
it 'returns all the users' do
- project = create(:project, :public)
+ project = create(:empty_project, :public)
user = build(:user)
expect(described_class.users_that_can_read_project([user], project)).
@@ -69,7 +69,7 @@ describe Ability, lib: true do
end
context 'using an internal project' do
- let(:project) { create(:project, :internal) }
+ let(:project) { create(:empty_project, :internal) }
it 'returns users that are administrators' do
user = build(:user, admin: true)
@@ -120,7 +120,7 @@ describe Ability, lib: true do
end
context 'using a private project' do
- let(:project) { create(:project, :private) }
+ let(:project) { create(:empty_project, :private) }
it 'returns users that are administrators' do
user = build(:user, admin: true)
@@ -171,6 +171,33 @@ describe Ability, lib: true do
end
end
+ describe '.users_that_can_read_personal_snippet' do
+ def users_for_snippet(snippet)
+ described_class.users_that_can_read_personal_snippet(users, snippet)
+ end
+
+ let(:users) { create_list(:user, 3) }
+ let(:author) { users[0] }
+
+ it 'private snippet is readable only by its author' do
+ snippet = create(:personal_snippet, :private, author: author)
+
+ expect(users_for_snippet(snippet)).to match_array([author])
+ end
+
+ it 'internal snippet is readable by all registered users' do
+ snippet = create(:personal_snippet, :public, author: author)
+
+ expect(users_for_snippet(snippet)).to match_array(users)
+ end
+
+ it 'public snippet is readable by all users' do
+ snippet = create(:personal_snippet, :public, author: author)
+
+ expect(users_for_snippet(snippet)).to match_array(users)
+ end
+ end
+
describe '.issues_readable_by_user' do
context 'with an admin user' do
it 'returns all given issues' do
@@ -220,7 +247,7 @@ describe Ability, lib: true do
end
describe '.project_disabled_features_rules' do
- let(:project) { create(:project, wiki_access_level: ProjectFeature::DISABLED) }
+ let(:project) { create(:empty_project, wiki_access_level: ProjectFeature::DISABLED) }
subject { described_class.allowed(project.owner, project) }
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index f031876e812..47cd5075a7d 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Ci::Build, :models do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:test_trace) { 'This is a test' }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 2bdd611aeed..426be74cd02 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -284,7 +284,7 @@ describe Ci::Pipeline, models: true do
end
describe 'merge request metrics' do
- let(:project) { FactoryGirl.create :project }
+ let(:project) { create(:project, :repository) }
let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
@@ -339,7 +339,7 @@ describe Ci::Pipeline, models: true do
end
context 'with non-empty project' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline,
@@ -890,7 +890,7 @@ describe Ci::Pipeline, models: true do
end
describe "#merge_requests" do
- let(:project) { FactoryGirl.create :project }
+ let(:project) { create(:project, :repository) }
let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
it "returns merge requests whose `diff_head_sha` matches the pipeline's SHA" do
@@ -956,7 +956,7 @@ describe Ci::Pipeline, models: true do
end
describe 'notifications when pipeline success or failed' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline,
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 2b856ca7af7..3f32248e52b 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -91,8 +91,7 @@ describe Ci::Runner, models: true do
end
describe '#can_pick?' do
- let(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:pipeline) { create(:ci_pipeline) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:runner) { create(:ci_runner) }
@@ -321,8 +320,8 @@ describe Ci::Runner, models: true do
describe '.assignable_for' do
let(:runner) { create(:ci_runner) }
- let(:project) { create(:project) }
- let(:another_project) { create(:project) }
+ let(:project) { create(:empty_project) }
+ let(:another_project) { create(:empty_project) }
before do
project.runners << runner
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index 30782ca75a0..e4bddf67096 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -7,7 +7,7 @@ describe CommitRange, models: true do
it { is_expected.to include_module(Referable) }
end
- let!(:project) { create(:project, :public) }
+ let!(:project) { create(:project, :public, :repository) }
let!(:commit1) { project.commit("HEAD~2") }
let!(:commit2) { project.commit }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index b2202f0fd44..32f9366a14c 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Commit, models: true do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:commit) { project.commit }
describe 'modules' do
@@ -34,7 +34,7 @@ describe Commit, models: true do
end
describe '#to_reference' do
- let(:project) { create(:project, path: 'sample-project') }
+ let(:project) { create(:project, :repository, path: 'sample-project') }
let(:commit) { project.commit }
it 'returns a String reference to the object' do
@@ -42,13 +42,13 @@ describe Commit, models: true do
end
it 'supports a cross-project reference' do
- another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ another_project = build(:project, :repository, name: 'another-project', namespace: project.namespace)
expect(commit.to_reference(another_project)).to eq "sample-project@#{commit.id}"
end
end
describe '#reference_link_text' do
- let(:project) { create(:project, path: 'sample-project') }
+ let(:project) { create(:project, :repository, path: 'sample-project') }
let(:commit) { project.commit }
it 'returns a String reference to the object' do
@@ -56,7 +56,7 @@ describe Commit, models: true do
end
it 'supports a cross-project reference' do
- another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ another_project = build(:project, :repository, name: 'another-project', namespace: project.namespace)
expect(commit.reference_link_text(another_project)).to eq "sample-project@#{commit.short_id}"
end
end
@@ -131,7 +131,7 @@ eos
describe '#closes_issues' do
let(:issue) { create :issue, project: project }
- let(:other_project) { create :project, :public }
+ let(:other_project) { create(:empty_project, :public) }
let(:other_issue) { create :issue, project: other_project }
let(:commiter) { create :user }
@@ -154,7 +154,7 @@ eos
end
it_behaves_like 'a mentionable' do
- subject { create(:project).commit }
+ subject { create(:project, :repository).commit }
let(:author) { create(:user, email: subject.author_email) }
let(:backref_text) { "commit #{subject.id}" }
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 64ea607eb95..bf4394f7d5b 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe CommitStatus, models: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline, project: project, sha: project.commit.id)
diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb
index 49ab3c4b6e9..da003dbf794 100644
--- a/spec/models/compare_spec.rb
+++ b/spec/models/compare_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Compare, models: true do
include RepoHelpers
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:commit) { project.commit }
let(:start_commit) { sample_image_commit }
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index d7d31892e12..545a11912e3 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -301,7 +301,7 @@ describe Issue, "Issuable" do
end
describe '#labels_array' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:bug) { create(:label, project: project, title: 'bug') }
let(:issue) { create(:issue, project: project) }
@@ -315,7 +315,7 @@ describe Issue, "Issuable" do
end
describe '#user_notes_count' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issue1) { create(:issue, project: project) }
let(:issue2) { create(:issue, project: project) }
@@ -359,7 +359,7 @@ describe Issue, "Issuable" do
end
describe ".with_label" do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:bug) { create(:label, project: project, title: 'bug') }
let(:feature) { create(:label, project: project, title: 'feature') }
let(:enhancement) { create(:label, project: project, title: 'enhancement') }
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 132858950d5..2092576e981 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -13,7 +13,7 @@ describe Mentionable do
end
describe 'references' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:mentionable) { Example.new }
it 'excludes JIRA references' do
@@ -30,12 +30,20 @@ describe Issue, "Mentionable" do
describe '#mentioned_users' do
let!(:user) { create(:user, username: 'stranger') }
let!(:user2) { create(:user, username: 'john') }
- let!(:issue) { create(:issue, description: "#{user.to_reference} mentioned") }
+ let!(:user3) { create(:user, username: 'jim') }
+ let(:issue) { create(:issue, description: "#{user.to_reference} mentioned") }
subject { issue.mentioned_users }
- it { is_expected.to include(user) }
- it { is_expected.not_to include(user2) }
+ it { expect(subject).to contain_exactly(user) }
+
+ context 'when a note on personal snippet' do
+ let!(:note) { create(:note_on_personal_snippet, note: "#{user.to_reference} mentioned #{user3.to_reference}") }
+
+ subject { note.mentioned_users }
+
+ it { expect(subject).to contain_exactly(user, user3) }
+ end
end
describe '#referenced_mentionables' do
@@ -75,13 +83,13 @@ describe Issue, "Mentionable" do
end
describe '#create_cross_references!' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:author) { build(:user) }
let(:commit) { project.commit }
let(:commit2) { project.commit }
let!(:issue) do
- create(:issue, project: project, description: commit.to_reference)
+ create(:issue, project: project, description: "See #{commit.to_reference}")
end
it 'correctly removes already-mentioned Commits' do
@@ -92,7 +100,7 @@ describe Issue, "Mentionable" do
end
describe '#create_new_cross_references!' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:author) { create(:author) }
let(:issues) { create_list(:issue, 2, project: project, author: author) }
@@ -138,6 +146,16 @@ describe Issue, "Mentionable" do
issue.update_attributes(description: issues[1].to_reference)
issue.create_new_cross_references!
end
+
+ it 'notifies new references from project snippet note' do
+ snippet = create(:snippet, project: project)
+ note = create(:note, note: issues[0].to_reference, noteable: snippet, project: project, author: author)
+
+ expect(SystemNoteService).to receive(:cross_reference).with(issues[1], any_args)
+
+ note.update_attributes(note: issues[1].to_reference)
+ note.create_new_cross_references!
+ end
end
def create_issue(description:)
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 0e097559b59..ad703a6c8bb 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -7,7 +7,7 @@ describe Milestone, 'Milestoneish' do
let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) }
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:milestone) { create(:milestone, project: project) }
let!(:issue) { create(:issue, project: project, milestone: milestone) }
let!(:security_issue_1) { create(:issue, :confidential, project: project, author: author, milestone: milestone) }
diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb
index 9041690023f..6cf5877424d 100644
--- a/spec/models/concerns/project_features_compatibility_spec.rb
+++ b/spec/models/concerns/project_features_compatibility_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe ProjectFeaturesCompatibility do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:features) { %w(issues wiki builds merge_requests snippets) }
# We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index b556135532f..30443534cca 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -68,4 +68,14 @@ describe Group, 'Routable' do
end
end
end
+
+ describe '.member_descendants' do
+ let!(:user) { create(:user) }
+ let!(:nested_group) { create(:group, parent: group) }
+
+ before { group.add_owner(user) }
+ subject { described_class.member_descendants(user.id) }
+
+ it { is_expected.to eq([nested_group]) }
+ end
end
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index 70f985afefb..3b7cc7d9e2e 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'CycleAnalytics#code', feature: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index e4b6a8f4518..5c73edbbc53 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'CycleAnalytics#issue', models: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index dc5b04852d6..55483fc876a 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'CycleAnalytics#plan', feature: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb
index 5e99188f318..591bbdddf55 100644
--- a/spec/models/cycle_analytics/production_spec.rb
+++ b/spec/models/cycle_analytics/production_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'CycleAnalytics#production', feature: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index 45baa5f7006..33d2c0a7416 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'CycleAnalytics#review', feature: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb
index 77625aad580..00693d67475 100644
--- a/spec/models/cycle_analytics/staging_spec.rb
+++ b/spec/models/cycle_analytics/staging_spec.rb
@@ -3,9 +3,10 @@ require 'spec_helper'
describe 'CycleAnalytics#staging', feature: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
+
subject { CycleAnalytics.new(project, from: from_date) }
generate_cycle_analytics_spec(
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index 27a117d2d76..f857ea6cbec 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'CycleAnalytics#test', feature: true do
extend CycleAnalyticsHelpers::TestGeneration
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
subject { CycleAnalytics.new(project, from: from_date) }
diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb
index 8a1e337c1a3..aacc178a19e 100644
--- a/spec/models/deploy_keys_project_spec.rb
+++ b/spec/models/deploy_keys_project_spec.rb
@@ -12,7 +12,7 @@ describe DeployKeysProject, models: true do
end
describe "Destroying" do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
subject { create(:deploy_keys_project, project: project) }
let(:deploy_key) { subject.deploy_key }
@@ -39,7 +39,7 @@ describe DeployKeysProject, models: true do
end
context "when the deploy key is used by more than one project" do
- let!(:other_project) { create(:project) }
+ let!(:other_project) { create(:empty_project) }
before do
other_project.deploy_keys << deploy_key
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index ca594a320c0..fc4435a2f64 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -17,7 +17,7 @@ describe Deployment, models: true do
it { is_expected.to validate_presence_of(:sha) }
describe '#includes_commit?' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
let(:deployment) do
create(:deployment, environment: environment, sha: project.commit.id)
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 3db5937a4f3..9ea3a4b7020 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe DiffNote, models: true do
include RepoHelpers
- let(:project) { create(:project) }
- let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
let(:commit) { project.commit(sample_commit.id) }
let(:path) { "files/ruby/popen.rb" }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 96efe1696c3..b2e06541e66 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -32,7 +32,7 @@ describe Environment, models: true do
end
describe '#includes_commit?' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
context 'without a last deployment' do
it "returns false" do
@@ -81,7 +81,7 @@ describe Environment, models: true do
end
describe '#first_deployment_for' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let!(:deployment) { create(:deployment, environment: environment, ref: commit.parent.id) }
let!(:deployment1) { create(:deployment, environment: environment, ref: commit.id) }
let(:head_commit) { project.commit }
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index f8660da031d..349474bb656 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -27,7 +27,7 @@ describe Event, models: true do
end
describe "Push event" do
- let(:project) { create(:project, :private) }
+ let(:project) { create(:empty_project, :private) }
let(:user) { project.owner }
let(:event) { create_event(project, user) }
@@ -187,7 +187,7 @@ describe Event, models: true do
end
context 'merge request diff note event' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:merge_request) { create(:merge_request, source_project: project, author: author, assignee: assignee) }
let(:note_on_merge_request) { create(:legacy_diff_note_on_merge_request, noteable: merge_request, project: project) }
let(:target) { note_on_merge_request }
@@ -202,7 +202,7 @@ describe Event, models: true do
end
context 'private project' do
- let(:project) { create(:project, :private) }
+ let(:project) { create(:empty_project, :private) }
it do
expect(event.visible_to_user?(non_member)).to eq false
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
index 1863581f57b..454550c9710 100644
--- a/spec/models/forked_project_link_spec.rb
+++ b/spec/models/forked_project_link_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe ForkedProjectLink, "add link on fork" do
- let(:project_from) { create(:project) }
+ let(:project_from) { create(:project, :repository) }
let(:namespace) { create(:namespace) }
let(:user) { create(:user, namespace: namespace) }
@@ -21,7 +21,7 @@ end
describe '#forked?' do
let(:forked_project_link) { build(:forked_project_link) }
- let(:project_from) { create(:project) }
+ let(:project_from) { create(:project, :repository) }
let(:project_to) { create(:project, forked_project_link: forked_project_link) }
before :each do
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index d87684fd49e..cacbab8bcb1 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -4,9 +4,9 @@ describe GlobalMilestone, models: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:group) { create(:group) }
- let(:project1) { create(:project, group: group) }
- let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
- let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
+ let(:project1) { create(:empty_project, group: group) }
+ let(:project2) { create(:empty_project, path: 'gitlab-ci', group: group) }
+ let(:project3) { create(:empty_project, path: 'cookbook-gitlab', group: group) }
describe '.build_collection' do
let(:milestone1_due_date) { 2.weeks.from_now.to_date }
diff --git a/spec/models/group_milestone_spec.rb b/spec/models/group_milestone_spec.rb
index 601167c3bd3..916afb7aaf5 100644
--- a/spec/models/group_milestone_spec.rb
+++ b/spec/models/group_milestone_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe GroupMilestone, models: true do
let(:group) { create(:group) }
- let(:project) { create(:project, group: group) }
+ let(:project) { create(:empty_project, group: group) }
let(:project_milestone) do
create(:milestone, title: "Milestone v1.2", project: project)
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 45fe927202b..9ca50555191 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -81,13 +81,19 @@ describe Group, models: true do
describe 'public_only' do
subject { described_class.public_only.to_a }
- it{ is_expected.to eq([group]) }
+ it { is_expected.to eq([group]) }
end
describe 'public_and_internal_only' do
subject { described_class.public_and_internal_only.to_a }
- it{ is_expected.to match_array([group, internal_group]) }
+ it { is_expected.to match_array([group, internal_group]) }
+ end
+
+ describe 'non_public_only' do
+ subject { described_class.non_public_only.to_a }
+
+ it { is_expected.to match_array([private_group, internal_group]) }
end
end
@@ -269,6 +275,12 @@ describe Group, models: true do
it 'returns the canonical URL' do
expect(group.web_url).to include("groups/#{group.name}")
end
+
+ context 'nested group' do
+ let(:nested_group) { create(:group, :nested) }
+
+ it { expect(nested_group.web_url).to include("groups/#{nested_group.full_path}") }
+ end
end
describe 'nested group' do
diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb
index d79f929f7a1..582b54c0712 100644
--- a/spec/models/guest_spec.rb
+++ b/spec/models/guest_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
describe Guest, lib: true do
- let(:public_project) { create(:project, :public) }
- let(:private_project) { create(:project, :private) }
- let(:internal_project) { create(:project, :internal) }
+ let(:public_project) { build_stubbed(:empty_project, :public) }
+ let(:private_project) { build_stubbed(:empty_project, :private) }
+ let(:internal_project) { build_stubbed(:empty_project, :internal) }
describe '.can_pull?' do
context 'when project is private' do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index ad2b710041a..e8caad00c44 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -4,7 +4,7 @@ describe SystemHook, models: true do
describe "execute" do
let(:system_hook) { create(:system_hook) }
let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
let(:group) { create(:group) }
before do
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index e52b9d75cef..9d4db1bfb52 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -25,7 +25,7 @@ describe WebHook, models: true do
end
describe "execute" do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:project_hook) { create(:project_hook) }
before(:each) do
diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb
index 2459a49f095..08712f2a768 100644
--- a/spec/models/issue/metrics_spec.rb
+++ b/spec/models/issue/metrics_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Issue::Metrics, models: true do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
subject { create(:issue, project: project) }
diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb
index d742c814680..d8aed25c041 100644
--- a/spec/models/issue_collection_spec.rb
+++ b/spec/models/issue_collection_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe IssueCollection do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issue1) { create(:issue, project: project) }
let(:issue2) { create(:issue, project: project) }
let(:collection) { described_class.new([issue1, issue2]) }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index bfa36d92ac3..40c0a75c364 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -35,7 +35,7 @@ describe Issue, models: true do
end
it 'supports a cross-project reference' do
- another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ another_project = build(:empty_project, name: 'another-project', namespace: project.namespace)
expect(issue.to_reference(another_project)).to eq "sample-project#1"
end
end
@@ -60,9 +60,9 @@ describe Issue, models: true do
end
describe '#closed_by_merge_requests' do
- let(:project) { create(:project) }
- let(:issue) { create(:issue, project: project, state: "opened")}
- let(:closed_issue) { build(:issue, project: project, state: "closed")}
+ let(:project) { create(:project, :repository) }
+ let(:issue) { create(:issue, project: project)}
+ let(:closed_issue) { build(:issue, :closed, project: project)}
let(:mr) do
opts = {
@@ -104,7 +104,7 @@ describe Issue, models: true do
describe '#referenced_merge_requests' do
it 'returns the referenced merge requests' do
- project = create(:project, :public)
+ project = create(:empty_project, :public)
mr1 = create(:merge_request,
source_project: project,
@@ -137,7 +137,7 @@ describe Issue, models: true do
end
context 'user is reporter in project issue belongs to' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
before { project.team << [user, :reporter] }
@@ -151,7 +151,7 @@ describe Issue, models: true do
context 'checking destination project also' do
subject { issue.can_move?(user, to_project) }
- let(:to_project) { create(:project) }
+ let(:to_project) { create(:empty_project) }
context 'destination project allowed' do
before { to_project.team << [user, :reporter] }
@@ -217,7 +217,7 @@ describe Issue, models: true do
end
it_behaves_like 'an editable mentionable' do
- subject { create(:issue) }
+ subject { create(:issue, project: create(:project, :repository)) }
let(:backref_text) { "issue #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
@@ -246,7 +246,7 @@ describe Issue, models: true do
describe '#participants' do
context 'using a public project' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:issue) { create(:issue, project: project) }
let!(:note1) do
@@ -268,7 +268,7 @@ describe Issue, models: true do
context 'using a private project' do
it 'does not include mentioned users that do not have access to the project' do
- project = create(:project)
+ project = create(:empty_project)
user = create(:user)
issue = create(:issue, project: project)
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 4f7c8a36cb5..16e2144d6a1 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -481,7 +481,7 @@ describe Member, models: true do
describe "destroying a record", truncate: true do
it "refreshes user's authorized projects" do
- project = create(:project, :private)
+ project = create(:empty_project, :private)
user = create(:user)
member = project.team << [user, :reporter]
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 68f72f5c86e..90d14c2c0b9 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -83,8 +83,8 @@ describe ProjectMember, models: true do
describe '.import_team' do
before do
- @project_1 = create :project
- @project_2 = create :project
+ @project_1 = create(:empty_project)
+ @project_2 = create(:empty_project)
@user_1 = create :user
@user_2 = create :user
@@ -131,8 +131,8 @@ describe ProjectMember, models: true do
describe '.truncate_teams' do
before do
- @project_1 = create :project
- @project_2 = create :project
+ @project_1 = create(:empty_project)
+ @project_2 = create(:empty_project)
@user_1 = create :user
@user_2 = create :user
diff --git a/spec/models/merge_request/metrics_spec.rb b/spec/models/merge_request/metrics_spec.rb
index 255db41cb19..9afed311e27 100644
--- a/spec/models/merge_request/metrics_spec.rb
+++ b/spec/models/merge_request/metrics_spec.rb
@@ -1,9 +1,7 @@
require 'spec_helper'
describe MergeRequest::Metrics, models: true do
- let(:project) { create(:project) }
-
- subject { create(:merge_request, source_project: project) }
+ subject { create(:merge_request) }
describe "when recording the default set of metrics on merge request save" do
it "records the merge time" do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 064f29d2d66..3cee2b7714f 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -24,7 +24,7 @@ describe Milestone, models: true do
it { is_expected.to have_many(:issues) }
end
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:milestone) { create(:milestone, project: project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
@@ -44,7 +44,7 @@ describe Milestone, models: true do
end
it "accepts the same title in another project" do
- project = build(:project)
+ project = build(:empty_project)
new_milestone = Milestone.new(project: project, title: milestone.title)
expect(new_milestone).to be_valid
@@ -257,7 +257,7 @@ describe Milestone, models: true do
end
it 'supports a cross-project reference' do
- another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ another_project = build(:empty_project, name: 'another-project', namespace: project.namespace)
expect(milestone.to_reference(another_project)).to eq "sample-project%1"
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index f8e03fa114a..4e96f19eb6f 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -5,6 +5,8 @@ describe Namespace, models: true do
it { is_expected.to have_many :projects }
it { is_expected.to have_many :project_statistics }
+ it { is_expected.to belong_to :parent }
+ it { is_expected.to have_many :children }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) }
@@ -105,7 +107,7 @@ describe Namespace, models: true do
describe '#move_dir' do
before do
@namespace = create :namespace
- @project = create :project, namespace: @namespace
+ @project = create(:empty_project, namespace: @namespace)
allow(@namespace).to receive(:path_changed?).and_return(true)
end
@@ -137,7 +139,7 @@ describe Namespace, models: true do
end
describe :rm_dir do
- let!(:project) { create(:project, namespace: namespace) }
+ let!(:project) { create(:empty_project, namespace: namespace) }
let!(:path) { File.join(Gitlab.config.repositories.storages.default, namespace.path) }
it "removes its dirs when deleted" do
@@ -189,17 +191,31 @@ describe Namespace, models: true do
it { expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}") }
end
- describe '#parents' do
+ describe '#ancestors' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
- it 'returns the correct parents' do
- expect(very_deep_nested_group.parents).to eq([group, nested_group, deep_nested_group])
- expect(deep_nested_group.parents).to eq([group, nested_group])
- expect(nested_group.parents).to eq([group])
- expect(group.parents).to eq([])
+ it 'returns the correct ancestors' do
+ expect(very_deep_nested_group.ancestors).to eq([group, nested_group, deep_nested_group])
+ expect(deep_nested_group.ancestors).to eq([group, nested_group])
+ expect(nested_group.ancestors).to eq([group])
+ expect(group.ancestors).to eq([])
+ end
+ end
+
+ describe '#descendants' do
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:deep_nested_group) { create(:group, parent: nested_group) }
+ let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+
+ it 'returns the correct descendants' do
+ expect(very_deep_nested_group.descendants.to_a).to eq([])
+ expect(deep_nested_group.descendants.to_a).to eq([very_deep_nested_group])
+ expect(nested_group.descendants.to_a).to eq([deep_nested_group, very_deep_nested_group])
+ expect(group.descendants.to_a).to eq([nested_group, deep_nested_group, very_deep_nested_group])
end
end
end
diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb
index b76513d2a3c..492c4e01bd8 100644
--- a/spec/models/network/graph_spec.rb
+++ b/spec/models/network/graph_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Network::Graph, models: true do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let!(:note_on_commit) { create(:note_on_commit, project: project) }
it '#initialize' do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 310fecd8a5c..1cde9e04951 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -42,7 +42,7 @@ describe Note, models: true do
context 'when noteable and note project differ' do
subject do
build(:note, noteable: build_stubbed(:issue),
- project: build_stubbed(:project))
+ project: build_stubbed(:empty_project))
end
it { is_expected.to be_invalid }
@@ -52,6 +52,19 @@ describe Note, models: true do
subject { create(:note) }
it { is_expected.to be_valid }
end
+
+ context 'when project is missing for a project related note' do
+ subject { build(:note, project: nil, noteable: build_stubbed(:issue)) }
+ it { is_expected.to be_invalid }
+ end
+
+ context 'when noteable is a personal snippet' do
+ subject { build(:note_on_personal_snippet) }
+
+ it 'is valid without project' do
+ is_expected.to be_valid
+ end
+ end
end
describe "Commit notes" do
@@ -80,8 +93,8 @@ describe Note, models: true do
describe 'authorization' do
before do
- @p1 = create(:project)
- @p2 = create(:project)
+ @p1 = create(:empty_project)
+ @p2 = create(:empty_project)
@u1 = create(:user)
@u2 = create(:user)
@u3 = create(:user)
@@ -125,7 +138,7 @@ describe Note, models: true do
it_behaves_like 'an editable mentionable' do
subject { create :note, noteable: issue, project: issue.project }
- let(:issue) { create :issue }
+ let(:issue) { create(:issue, project: create(:project, :repository)) }
let(:backref_text) { issue.gfm_reference }
let(:set_mentionable_text) { ->(txt) { subject.note = txt } }
end
@@ -139,6 +152,7 @@ describe Note, models: true do
with([{
text: note1.note,
context: {
+ skip_project_check: false,
pipeline: :note,
cache_key: [note1, "note"],
project: note1.project,
@@ -150,6 +164,7 @@ describe Note, models: true do
with([{
text: note2.note,
context: {
+ skip_project_check: false,
pipeline: :note,
cache_key: [note2, "note"],
project: note2.project,
@@ -176,10 +191,10 @@ describe Note, models: true do
describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) }
- let(:private_project) { create(:project, namespace: private_user.namespace).tap { |p| p.team << [private_user, :master] } }
+ let(:private_project) { create(:empty_project, namespace: private_user.namespace) { |p| p.team << [private_user, :master] } }
let(:private_issue) { create(:issue, project: private_project) }
- let(:ext_proj) { create(:project, :public) }
+ let(:ext_proj) { create(:empty_project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let(:note) do
@@ -222,7 +237,7 @@ describe Note, models: true do
describe '#participants' do
it 'includes the note author' do
- project = create(:project, :public)
+ project = create(:empty_project, :public)
issue = create(:issue, project: project)
note = create(:note_on_issue, noteable: issue, project: project)
@@ -306,4 +321,70 @@ describe Note, models: true do
end
end
end
+
+ describe '#for_personal_snippet?' do
+ it 'returns false for a project snippet note' do
+ expect(build(:note_on_project_snippet).for_personal_snippet?).to be_falsy
+ end
+
+ it 'returns true for a personal snippet note' do
+ expect(build(:note_on_personal_snippet).for_personal_snippet?).to be_truthy
+ end
+ end
+
+ describe '#to_ability_name' do
+ it 'returns snippet for a project snippet note' do
+ expect(build(:note_on_project_snippet).to_ability_name).to eq('snippet')
+ end
+
+ it 'returns personal_snippet for a personal snippet note' do
+ expect(build(:note_on_personal_snippet).to_ability_name).to eq('personal_snippet')
+ end
+
+ it 'returns merge_request for an MR note' do
+ expect(build(:note_on_merge_request).to_ability_name).to eq('merge_request')
+ end
+
+ it 'returns issue for an issue note' do
+ expect(build(:note_on_issue).to_ability_name).to eq('issue')
+ end
+
+ it 'returns issue for a commit note' do
+ expect(build(:note_on_commit).to_ability_name).to eq('commit')
+ end
+ end
+
+ describe '#cache_markdown_field' do
+ let(:html) { '<p>some html</p>'}
+
+ context 'note for a project snippet' do
+ let(:note) { build(:note_on_project_snippet) }
+
+ before do
+ expect(Banzai::Renderer).to receive(:cacheless_render_field).
+ with(note, :note, { skip_project_check: false }).and_return(html)
+
+ note.save
+ end
+
+ it 'creates a note' do
+ expect(note.note_html).to eq(html)
+ end
+ end
+
+ context 'note for a personal snippet' do
+ let(:note) { build(:note_on_personal_snippet) }
+
+ before do
+ expect(Banzai::Renderer).to receive(:cacheless_render_field).
+ with(note, :note, { skip_project_check: true }).and_return(html)
+
+ note.save
+ end
+
+ it 'creates a note' do
+ expect(note.note_html).to eq(html)
+ end
+ end
+ end
end
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index a55d43ab2f9..8589f1eb712 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe ProjectFeature do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:user) { create(:user) }
describe '#feature_available?' do
@@ -35,7 +35,7 @@ describe ProjectFeature do
it "returns true when user is a member of project group" do
group = create(:group)
- project = create(:project, namespace: group)
+ project = create(:empty_project, namespace: group)
group.add_developer(user)
features.each do |feature|
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 47397a822c1..59a4ae1b799 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -17,7 +17,7 @@ describe ProjectGroupLink do
describe "destroying a record", truncate: true do
it "refreshes group users' authorized projects" do
- project = create(:project, :private)
+ project = create(:empty_project, :private)
group = create(:group)
reporter = create(:user)
group_users = group.users
diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb
index 4d538cac007..9cdbfa44e5b 100644
--- a/spec/models/project_label_spec.rb
+++ b/spec/models/project_label_spec.rb
@@ -100,7 +100,7 @@ describe ProjectLabel, models: true do
end
context 'cross project reference' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context 'using name' do
it 'returns cross reference with label name' do
diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb
index 8e5145e824b..48aef3a93f2 100644
--- a/spec/models/project_services/asana_service_spec.rb
+++ b/spec/models/project_services/asana_service_spec.rb
@@ -18,7 +18,7 @@ describe AsanaService, models: true do
describe 'Execute' do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
def create_data_for_commits(*messages)
{
diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb
index 4c5acb7990b..96f00af898e 100644
--- a/spec/models/project_services/assembla_service_spec.rb
+++ b/spec/models/project_services/assembla_service_spec.rb
@@ -8,7 +8,7 @@ describe AssemblaService, models: true do
describe "Execute" do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
@assembla_service = AssemblaService.new
diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb
index a3b9d084a75..953e664fb66 100644
--- a/spec/models/project_services/campfire_service_spec.rb
+++ b/spec/models/project_services/campfire_service_spec.rb
@@ -22,7 +22,7 @@ describe CampfireService, models: true do
describe "#execute" do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
@campfire_service = CampfireService.new
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
index 42c2ed668bc..f9307d6de7b 100644
--- a/spec/models/project_services/drone_ci_service_spec.rb
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -27,7 +27,7 @@ describe DroneCiService, models: true, caching: true do
shared_context :drone_ci_service do
let(:drone) { DroneCiService.new }
- let(:project) { create(:project, name: 'project') }
+ let(:project) { create(:project, :repository, name: 'project') }
let(:path) { "#{project.namespace.path}/#{project.path}" }
let(:drone_url) { 'http://drone.example.com' }
let(:sha) { '2ab7834c' }
diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb
index 342d86aeca9..bdeea1db1e3 100644
--- a/spec/models/project_services/external_wiki_service_spec.rb
+++ b/spec/models/project_services/external_wiki_service_spec.rb
@@ -23,7 +23,7 @@ describe ExternalWikiService, models: true do
end
describe 'External wiki' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context 'when it is active' do
before do
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index d6db02d6e76..a97e8c6e4ce 100644
--- a/spec/models/project_services/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -22,7 +22,7 @@ describe FlowdockService, models: true do
describe "Execute" do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
@flowdock_service = FlowdockService.new
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
index 529044d1d8b..a13fbae03eb 100644
--- a/spec/models/project_services/gemnasium_service_spec.rb
+++ b/spec/models/project_services/gemnasium_service_spec.rb
@@ -24,7 +24,7 @@ describe GemnasiumService, models: true do
describe "Execute" do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
before do
@gemnasium_service = GemnasiumService.new
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index 9b80f0e7296..dcb70ee28a8 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -8,21 +8,21 @@ describe GitlabIssueTrackerService, models: true do
describe 'Validations' do
context 'when service is active' do
- subject { described_class.new(project: create(:project), active: true) }
+ subject { described_class.new(project: create(:empty_project), active: true) }
it { is_expected.to validate_presence_of(:issues_url) }
it_behaves_like 'issue tracker service URL attribute', :issues_url
end
context 'when service is inactive' do
- subject { described_class.new(project: create(:project), active: false) }
+ subject { described_class.new(project: create(:empty_project), active: false) }
it { is_expected.not_to validate_presence_of(:issues_url) }
end
end
describe 'project and issue urls' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context 'with absolute urls' do
before do
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index 2da3a9cb09f..bf422ac7ce1 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -22,8 +22,8 @@ describe HipchatService, models: true do
describe "Execute" do
let(:hipchat) { HipchatService.new }
- let(:user) { create(:user, username: 'username') }
- let(:project) { create(:project, name: 'project') }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' }
let(:project_name) { project.name_with_namespace.gsub(/\s/, '') }
let(:token) { 'verySecret' }
@@ -165,7 +165,7 @@ describe HipchatService, models: true do
context "Note events" do
let(:user) { create(:user) }
- let(:project) { create(:project, creator_id: user.id) }
+ let(:project) { create(:project, :repository, creator: user) }
context 'when commit comment event triggered' do
let(:commit_note) do
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index f8c45b37561..b9fb6f3f6f4 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -25,7 +25,7 @@ describe IrkerService, models: true do
describe 'Execute' do
let(:irker) { IrkerService.new }
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:sample_data) do
Gitlab::DataBuilder::Push.build_sample(project, user)
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 862e3a72a73..2f6b159d76e 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -71,7 +71,7 @@ describe JiraService, models: true do
describe '#close_issue' do
let(:custom_base_url) { 'http://custom_url' }
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:merge_request) { create(:merge_request) }
before do
@@ -207,12 +207,12 @@ describe JiraService, models: true do
end
describe "Stored password invalidation" do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context "when a password was previously set" do
before do
@jira_service = JiraService.create!(
- project: create(:project),
+ project: project,
properties: {
url: 'http://jira.example.com/rest/api/2',
username: 'mic',
@@ -252,7 +252,7 @@ describe JiraService, models: true do
context "when no password was previously set" do
before do
@jira_service = JiraService.create(
- project: create(:project),
+ project: project,
properties: {
url: 'http://jira.example.com/rest/api/2',
username: 'mic'
@@ -281,7 +281,7 @@ describe JiraService, models: true do
end
describe 'description and title' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context 'when it is not set' do
before do
@@ -316,7 +316,7 @@ describe JiraService, models: true do
end
describe 'project and issue urls' do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
context 'when gitlab.yml was initialized' do
before do
diff --git a/spec/models/project_services/pipeline_email_service_spec.rb b/spec/models/project_services/pipeline_email_service_spec.rb
index 7c8824485f5..03932895b0e 100644
--- a/spec/models/project_services/pipeline_email_service_spec.rb
+++ b/spec/models/project_services/pipeline_email_service_spec.rb
@@ -7,7 +7,7 @@ describe PipelinesEmailService do
create(:ci_pipeline, project: project, sha: project.commit('master').sha)
end
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:recipient) { 'test@gitlab.com' }
let(:data) do
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index 8fc92a9ab51..a7e7594a7d5 100644
--- a/spec/models/project_services/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -27,7 +27,7 @@ describe PushoverService, models: true do
describe 'Execute' do
let(:pushover) { PushoverService.new }
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:sample_data) do
Gitlab::DataBuilder::Push.build_sample(project, user)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8048e86fc3a..646a1311462 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -832,6 +832,26 @@ describe Project, models: true do
it { expect(project.builds_enabled?).to be_truthy }
end
+ describe '.with_shared_runners' do
+ subject { Project.with_shared_runners }
+
+ context 'when shared runners are enabled for project' do
+ let!(:project) { create(:empty_project, shared_runners_enabled: true) }
+
+ it "returns a project" do
+ is_expected.to eq([project])
+ end
+ end
+
+ context 'when shared runners are disabled for project' do
+ let!(:project) { create(:empty_project, shared_runners_enabled: false) }
+
+ it "returns an empty array" do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.cached_count', caching: true do
let(:group) { create(:group, :public) }
let!(:project1) { create(:empty_project, :public, group: group) }
@@ -974,6 +994,28 @@ describe Project, models: true do
end
end
+ describe '#shared_runners' do
+ let!(:runner) { create(:ci_runner, :shared) }
+
+ subject { project.shared_runners }
+
+ context 'when shared runners are enabled for project' do
+ let!(:project) { create(:empty_project, shared_runners_enabled: true) }
+
+ it "returns a list of shared runners" do
+ is_expected.to eq([runner])
+ end
+ end
+
+ context 'when shared runners are disabled for project' do
+ let!(:project) { create(:empty_project, shared_runners_enabled: false) }
+
+ it "returns a empty list" do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '#visibility_level_allowed?' do
let(:project) { create(:empty_project, :internal) }
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 0475cecaa2d..942eeab251d 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -265,10 +265,10 @@ describe ProjectTeam, models: true do
let(:group) { create(:group) }
let(:developer) { create(:user) }
let(:master) { create(:user) }
- let(:personal_project) { create(:project, namespace: developer.namespace) }
- let(:group_project) { create(:project, namespace: group) }
- let(:members_project) { create(:project) }
- let(:shared_project) { create(:project) }
+ let(:personal_project) { create(:empty_project, namespace: developer.namespace) }
+ let(:group_project) { create(:empty_project, namespace: group) }
+ let(:members_project) { create(:empty_project) }
+ let(:shared_project) { create(:empty_project) }
before do
group.add_master(master)
@@ -330,7 +330,7 @@ describe ProjectTeam, models: true do
reporter = create(:user)
promoted_guest = create(:user)
guest = create(:user)
- project = create(:project)
+ project = create(:empty_project)
project.add_master(master)
project.add_reporter(reporter)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 99ca53938c8..829b69093c9 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -4,7 +4,7 @@ describe Repository, models: true do
include RepoHelpers
TestBlob = Struct.new(:name)
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:user) { create(:user) }
@@ -90,6 +90,30 @@ describe Repository, models: true do
it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
end
+
+ context 'annotated tag pointing to a blob' do
+ let(:annotated_tag_name) { 'annotated-tag' }
+
+ subject { repository.tags_sorted_by('updated_asc').map(&:name) }
+
+ before do
+ options = { message: 'test tag message\n',
+ tagger: { name: 'John Smith', email: 'john@gmail.com' } }
+ repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options)
+
+ double_first = double(committed_date: Time.now - 1.second)
+ double_last = double(committed_date: Time.now)
+
+ allow(tag_a).to receive(:dereferenced_target).and_return(double_last)
+ allow(tag_b).to receive(:dereferenced_target).and_return(double_first)
+ end
+
+ it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) }
+
+ after do
+ repository.rugged.tags.delete(annotated_tag_name)
+ end
+ end
end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 8481a9bef16..dd2a5109abc 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -14,7 +14,7 @@ describe Route, models: true do
it { is_expected.to validate_uniqueness_of(:path) }
end
- describe '#rename_children' do
+ describe '#rename_descendants' do
let!(:nested_group) { create(:group, path: "test", parent: group) }
let!(:deep_nested_group) { create(:group, path: "foo", parent: nested_group) }
let!(:similar_group) { create(:group, path: 'gitlab-org') }
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 691511cd93f..0e2f07e945f 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -12,7 +12,7 @@ describe Service, models: true do
end
describe "Testable" do
- let(:project) { create :project }
+ let(:project) { create(:project, :repository) }
before do
allow(@service).to receive(:project).and_return(project)
@@ -35,7 +35,7 @@ describe Service, models: true do
end
describe "With commits" do
- let(:project) { create :project }
+ let(:project) { create(:project, :repository) }
before do
allow(@service).to receive(:project).and_return(project)
@@ -60,7 +60,7 @@ describe Service, models: true do
api_key: '123456789'
})
end
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
describe 'is prefilled for projects pushover service' do
it "has all fields prefilled" do
@@ -79,7 +79,7 @@ describe Service, models: true do
describe "{property}_changed?" do
let(:service) do
BambooService.create(
- project: create(:project),
+ project: create(:empty_project),
properties: {
bamboo_url: 'http://gitlab.com',
username: 'mic',
@@ -119,7 +119,7 @@ describe Service, models: true do
describe "{property}_touched?" do
let(:service) do
BambooService.create(
- project: create(:project),
+ project: create(:empty_project),
properties: {
bamboo_url: 'http://gitlab.com',
username: 'mic',
@@ -159,7 +159,7 @@ describe Service, models: true do
describe "{property}_was" do
let(:service) do
BambooService.create(
- project: create(:project),
+ project: create(:empty_project),
properties: {
bamboo_url: 'http://gitlab.com',
username: 'mic',
@@ -199,7 +199,7 @@ describe Service, models: true do
describe 'initialize service with no properties' do
let(:service) do
GitlabIssueTrackerService.create(
- project: create(:project),
+ project: create(:empty_project),
title: 'random title'
)
end
@@ -214,7 +214,7 @@ describe Service, models: true do
end
describe "callbacks" do
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let!(:service) do
RedmineService.new(
project: project,
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 7425a897769..219ab1989ea 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -42,7 +42,7 @@ describe Snippet, models: true do
end
it 'supports a cross-project reference' do
- another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ another_project = build(:empty_project, name: 'another-project', namespace: project.namespace)
expect(snippet.to_reference(another_project)).to eq "sample-project$1"
end
end
@@ -55,7 +55,7 @@ describe Snippet, models: true do
end
it 'still returns shortest reference when project arg present' do
- another_project = build(:project, name: 'another-project')
+ another_project = build(:empty_project, name: 'another-project')
expect(snippet.to_reference(another_project)).to eq "$1"
end
end
@@ -173,7 +173,7 @@ describe Snippet, models: true do
end
describe '#participants' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:snippet) { create(:snippet, content: 'foo', project: project) }
let!(:note1) do
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index 623b82c01d8..8017d1c3324 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe Todo, models: true do
- let(:project) { create(:project) }
- let(:commit) { project.commit }
let(:issue) { create(:issue) }
describe 'relationships' do
@@ -82,6 +80,9 @@ describe Todo, models: true do
describe '#target' do
context 'for commits' do
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit }
+
it 'returns an instance of Commit when exists' do
subject.project = project
subject.target_type = 'Commit'
@@ -109,6 +110,9 @@ describe Todo, models: true do
describe '#target_reference' do
it 'returns the short commit id for commits' do
+ project = create(:project, :repository)
+ commit = project.commit
+
subject.project = project
subject.target_type = 'Commit'
subject.commit_id = commit.id
diff --git a/spec/models/tree_spec.rb b/spec/models/tree_spec.rb
index 0737999e125..a87983b7492 100644
--- a/spec/models/tree_spec.rb
+++ b/spec/models/tree_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Tree, models: true do
- let(:repository) { create(:project).repository }
+ let(:repository) { create(:project, :repository).repository }
let(:sha) { repository.root_ref }
subject { described_class.new(repository, '54fcc214') }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index ca3d4ff0aa9..6ca5ad747d1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -797,14 +797,14 @@ describe User, models: true do
describe '#avatar_type' do
let(:user) { create(:user) }
- it "is true if avatar is image" do
+ it 'is true if avatar is image' do
user.update_attribute(:avatar, 'uploads/avatar.png')
expect(user.avatar_type).to be_truthy
end
- it "is false if avatar is html page" do
+ it 'is false if avatar is html page' do
user.update_attribute(:avatar, 'uploads/avatar.html')
- expect(user.avatar_type).to eq(["only images allowed"])
+ expect(user.avatar_type).to eq(['only images allowed'])
end
end
@@ -926,8 +926,8 @@ describe User, models: true do
end
end
- describe "#starred?" do
- it "determines if user starred a project" do
+ describe '#starred?' do
+ it 'determines if user starred a project' do
user = create :user
project1 = create(:empty_project, :public)
project2 = create(:empty_project, :public)
@@ -953,8 +953,8 @@ describe User, models: true do
end
end
- describe "#toggle_star" do
- it "toggles stars" do
+ describe '#toggle_star' do
+ it 'toggles stars' do
user = create :user
project = create(:empty_project, :public)
@@ -966,31 +966,44 @@ describe User, models: true do
end
end
- describe "#sort" do
+ describe '#sort' do
before do
User.delete_all
@user = create :user, created_at: Date.today, last_sign_in_at: Date.today, name: 'Alpha'
@user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega'
+ @user2 = create :user, created_at: Date.today - 2, last_sign_in_at: nil, name: 'Beta'
end
- it "sorts users by the recent sign-in time" do
- expect(User.sort('recent_sign_in').first).to eq(@user)
+ context 'when sort by recent_sign_in' do
+ it 'sorts users by the recent sign-in time' do
+ expect(User.sort('recent_sign_in').first).to eq(@user)
+ end
+
+ it 'pushes users who never signed in to the end' do
+ expect(User.sort('recent_sign_in').third).to eq(@user2)
+ end
end
- it "sorts users by the oldest sign-in time" do
- expect(User.sort('oldest_sign_in').first).to eq(@user1)
+ context 'when sort by oldest_sign_in' do
+ it 'sorts users by the oldest sign-in time' do
+ expect(User.sort('oldest_sign_in').first).to eq(@user1)
+ end
+
+ it 'pushes users who never signed in to the end' do
+ expect(User.sort('oldest_sign_in').third).to eq(@user2)
+ end
end
- it "sorts users in descending order by their creation time" do
+ it 'sorts users in descending order by their creation time' do
expect(User.sort('created_desc').first).to eq(@user)
end
- it "sorts users in ascending order by their creation time" do
- expect(User.sort('created_asc').first).to eq(@user1)
+ it 'sorts users in ascending order by their creation time' do
+ expect(User.sort('created_asc').first).to eq(@user2)
end
- it "sorts users by id in descending order when nil is passed" do
- expect(User.sort(nil).first).to eq(@user1)
+ it 'sorts users by id in descending order when nil is passed' do
+ expect(User.sort(nil).first).to eq(@user2)
end
end
@@ -1350,6 +1363,39 @@ describe User, models: true do
end
end
+ describe '#nested_groups' do
+ let!(:user) { create(:user) }
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, parent: group) }
+
+ before do
+ group.add_owner(user)
+
+ # Add more data to ensure method does not include wrong groups
+ create(:group).add_owner(create(:user))
+ end
+
+ it { expect(user.nested_groups).to eq([nested_group]) }
+ end
+
+ describe '#nested_projects' do
+ let!(:user) { create(:user) }
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:project) { create(:empty_project, namespace: group) }
+ let!(:nested_project) { create(:empty_project, namespace: nested_group) }
+
+ before do
+ group.add_owner(user)
+
+ # Add more data to ensure method does not include wrong projects
+ other_project = create(:empty_project, namespace: create(:group, :nested))
+ other_project.add_developer(create(:user))
+ end
+
+ it { expect(user.nested_projects).to eq([nested_project]) }
+ end
+
describe '#refresh_authorized_projects', redis: true do
let(:project1) { create(:empty_project) }
let(:project2) { create(:empty_project) }
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 2878e0cb59b..5a3ffc284f2 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -6,7 +6,7 @@ describe API::Branches, api: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let!(:project) { create(:project, creator_id: user.id) }
+ let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
let!(:branch_name) { 'feature' }
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index 7be7acebb19..645e36683bc 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -5,7 +5,7 @@ describe API::Builds, api: true do
let(:user) { create(:user) }
let(:api_user) { user }
- let!(:project) { create(:project, creator_id: user.id, public_builds: false) }
+ let!(:project) { create(:project, :repository, creator: user, public_builds: false) }
let!(:developer) { create(:project_member, :developer, user: user, project: project) }
let(:reporter) { create(:project_member, :reporter, project: project) }
let(:guest) { create(:project_member, :guest, project: project) }
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index c1c7c0882de..88361def3cf 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::CommitStatuses, api: true do
include ApiHelpers
- let!(:project) { create(:project) }
+ let!(:project) { create(:project, :repository) }
let(:commit) { project.repository.commit }
let(:commit_status) { create(:commit_status, pipeline: pipeline) }
let(:guest) { create_user(:guest) }
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 7f8ea5251f0..af9028a8978 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -5,7 +5,7 @@ describe API::Commits, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
+ let!(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 685da28c673..5e26e779366 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe API::Files, api: true do
include ApiHelpers
let(:user) { create(:user) }
- let!(:project) { create(:project, namespace: user.namespace ) }
- let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } }
+ let!(:project) { create(:project, :repository, namespace: user.namespace ) }
+ let(:guest) { create(:user) { |u| project.add_guest(u) } }
let(:file_path) { 'files/ruby/popen.rb' }
let(:params) do
{
diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb
index df29099bc2f..92ac4fd334d 100644
--- a/spec/requests/api/fork_spec.rb
+++ b/spec/requests/api/fork_spec.rb
@@ -4,7 +4,6 @@ describe API::Projects, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:group) { create(:group) }
let(:group2) do
@@ -13,17 +12,14 @@ describe API::Projects, api: true do
group
end
- let(:project) do
- create(:project, creator_id: user.id, namespace: user.namespace)
- end
-
- let(:project_user2) do
- create(:project_member, :reporter, user: user2, project: project)
- end
-
describe 'POST /projects/fork/:id' do
- before { project_user2 }
- before { user3 }
+ let(:project) do
+ create(:project, :repository, creator: user, namespace: user.namespace)
+ end
+
+ before do
+ project.add_reporter(user2)
+ end
context 'when authenticated' do
it 'forks if user has sufficient access to project' do
@@ -49,7 +45,8 @@ describe API::Projects, api: true do
end
it 'fails on missing project access for the project to fork' do
- post api("/projects/fork/#{project.id}", user3)
+ new_user = create(:user)
+ post api("/projects/fork/#{project.id}", new_user)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Project Not Found')
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index a3798c8cd6c..ffeacb15f17 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -4,7 +4,7 @@ describe API::Internal, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:secret_token) { Gitlab::Shell.secret_token }
describe "GET /internal/check", no_db: true do
@@ -337,8 +337,7 @@ describe API::Internal, api: true do
context 'ssh access has been disabled' do
before do
- settings = ::ApplicationSetting.create_from_defaults
- settings.update_attribute(:enabled_git_access_protocol, 'http')
+ stub_application_setting(enabled_git_access_protocol: 'http')
end
it 'rejects the SSH push' do
@@ -360,8 +359,7 @@ describe API::Internal, api: true do
context 'http access has been disabled' do
before do
- settings = ::ApplicationSetting.create_from_defaults
- settings.update_attribute(:enabled_git_access_protocol, 'ssh')
+ stub_application_setting(enabled_git_access_protocol: 'ssh')
end
it 'rejects the HTTP push' do
@@ -383,8 +381,7 @@ describe API::Internal, api: true do
context 'web actions are always allowed' do
it 'allows WEB push' do
- settings = ::ApplicationSetting.create_from_defaults
- settings.update_attribute(:enabled_git_access_protocol, 'ssh')
+ stub_application_setting(enabled_git_access_protocol: 'ssh')
project.team << [user, :developer]
push(key, project, 'web')
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 71a7994e544..21a2c583aa8 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -6,12 +6,10 @@ describe API::MergeRequests, api: true do
let(:user) { create(:user) }
let(:admin) { create(:user, :admin) }
let(:non_member) { create(:user) }
- let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace) }
- let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
- let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
- let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds, merge_commit_sha: '9999999999999999999999999999999999999999') }
- let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
- let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
+ let!(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace) }
+ let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, title: "Test", created_at: base_time) }
+ let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, title: "Closed test", created_at: base_time + 1.second) }
+ let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, title: "Merged test", created_at: base_time + 2.seconds, merge_commit_sha: '9999999999999999999999999999999999999999') }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
before do
@@ -556,11 +554,12 @@ describe API::MergeRequests, api: true do
original_count = merge_request.notes.size
post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), note: "My comment"
+
expect(response).to have_http_status(201)
expect(json_response['note']).to eq('My comment')
expect(json_response['author']['name']).to eq(user.name)
expect(json_response['author']['username']).to eq(user.username)
- expect(merge_request.notes.size).to eq(original_count + 1)
+ expect(merge_request.reload.notes.size).to eq(original_count + 1)
end
it "returns 400 if note is missing" do
@@ -576,6 +575,9 @@ describe API::MergeRequests, api: true do
end
describe "GET :id/merge_requests/:merge_request_id/comments" do
+ let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
+ let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
+
it "returns merge_request comments ordered by created_at" do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
expect(response).to have_http_status(200)
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 9a01f7fa1c4..b7a0b5a9e13 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -5,7 +5,7 @@ describe API::Pipelines, api: true do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
- let(:project) { create(:project, creator_id: user.id) }
+ let(:project) { create(:project, :repository, creator: user) }
let!(:pipeline) do
create(:ci_empty_pipeline, project: project, sha: project.commit.id,
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index cc5c532de83..a1db81ce18c 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -17,6 +17,7 @@ describe API::Projects, api: true do
let(:project3) do
create(:project,
:private,
+ :repository,
name: 'second_project',
path: 'second_project',
creator_id: user.id,
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 0b19fa38c55..c61208e395c 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -8,7 +8,7 @@ describe API::Repositories, api: true do
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } }
- let!(:project) { create(:project, creator_id: user.id) }
+ let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
describe "GET /projects/:id/repository/tree" do
@@ -74,7 +74,7 @@ describe API::Repositories, api: true do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository tree' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -144,7 +144,7 @@ describe API::Repositories, api: true do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository blob' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -198,7 +198,7 @@ describe API::Repositories, api: true do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository raw blob' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -273,7 +273,7 @@ describe API::Repositories, api: true do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository archive' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -347,7 +347,7 @@ describe API::Repositories, api: true do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository compare' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -394,7 +394,7 @@ describe API::Repositories, api: true do
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository contributors' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index a1c32ae65ba..898d2b27e5c 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -7,7 +7,7 @@ describe API::Tags, api: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
- let!(:project) { create(:project, creator_id: user.id) }
+ let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:guest) { create(:project_member, :guest, user: user2, project: project) }
@@ -29,7 +29,7 @@ describe API::Tags, api: true do
context 'when unauthenticated' do
it_behaves_like 'repository tags' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
@@ -88,7 +88,7 @@ describe API::Tags, api: true do
context 'when unauthenticated' do
it_behaves_like 'repository tag' do
- let(:project) { create(:project, :public) }
+ let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index cd01283b655..84104aa66ee 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -7,7 +7,7 @@ describe API::Triggers do
let(:user2) { create(:user) }
let!(:trigger_token) { 'secure_token' }
let!(:trigger_token_2) { 'secure_token_2' }
- let!(:project) { create(:project, creator_id: user.id) }
+ let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) }
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 270c23e3f19..8dbe5f0b025 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -91,6 +91,20 @@ describe Ci::API::Builds do
expect { register_builds }.to change { runner.reload.contacted_at }
end
+ context 'when concurrently updating build' do
+ before do
+ expect_any_instance_of(Ci::Build).to receive(:run!).
+ and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
+ end
+
+ it 'returns a conflict' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(409)
+ expect(response.headers).not_to have_key('X-GitLab-Last-Update')
+ end
+ end
+
context 'registry credentials' do
let(:registry_credentials) do
{ 'type' => 'registry',
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index 2d434ab5dd8..a30be767119 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -5,9 +5,9 @@ describe Ci::API::Triggers do
describe 'POST /projects/:project_id/refs/:ref/trigger' do
let!(:trigger_token) { 'secure token' }
- let!(:project) { FactoryGirl.create(:project, ci_id: 10) }
- let!(:project2) { FactoryGirl.create(:empty_project, ci_id: 11) }
- let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
+ let!(:project) { create(:project, :repository, ci_id: 10) }
+ let!(:project2) { create(:empty_project, ci_id: 11) }
+ let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) }
let(:options) do
{
token: trigger_token
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 5abda28e26f..4a16824de04 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -12,7 +12,7 @@ describe 'Git HTTP requests', lib: true do
describe "User with no identities" do
let(:user) { create(:user) }
- let(:project) { create(:project, path: 'project.git-project') }
+ let(:project) { create(:project, :repository, path: 'project.git-project') }
context "when the project doesn't exist" do
context "when no authentication is provided" do
@@ -55,6 +55,28 @@ describe 'Git HTTP requests', lib: true do
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
end
+
+ context 'but the repo is disabled' do
+ let(:project) { create(:project, repository_access_level: ProjectFeature::DISABLED, wiki_access_level: ProjectFeature::ENABLED) }
+ let(:wiki) { ProjectWiki.new(project) }
+ let(:path) { "/#{wiki.repository.path_with_namespace}.git" }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ it 'allows clones' do
+ download(path, user: user.username, password: user.password) do |response|
+ expect(response).to have_http_status(200)
+ end
+ end
+
+ it 'allows pushes' do
+ upload(path, user: user.username, password: user.password) do |response|
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
end
context "when the project exists" do
diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb
index e02f0eacc93..d20866c0d44 100644
--- a/spec/requests/projects/artifacts_controller_spec.rb
+++ b/spec/requests/projects/artifacts_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::ArtifactsController do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline,
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index 72978846e93..0edbffbcd3b 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -4,7 +4,7 @@ describe 'cycle analytics events' do
include ApiHelpers
let(:user) { create(:user) }
- let(:project) { create(:project, public_builds: false) }
+ let(:project) { create(:project, :repository, public_builds: false) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
describe 'GET /:namespace/:project/cycle_analytics/events/issues' do
@@ -13,7 +13,12 @@ describe 'cycle analytics events' do
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
- 3.times { create_cycle }
+ 3.times do |count|
+ Timecop.freeze(Time.now + count.days) do
+ create_cycle
+ end
+ end
+
deploy_master
login_as(user)
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index a3fc23ba177..d9f774a1095 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -2,7 +2,6 @@ require 'spec_helper'
module Ci
describe RegisterBuildService, services: true do
- let!(:service) { RegisterBuildService.new }
let!(:project) { FactoryGirl.create :empty_project, shared_runners_enabled: false }
let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
let!(:pending_build) { FactoryGirl.create :ci_build, pipeline: pipeline }
@@ -19,29 +18,29 @@ module Ci
pending_build.tag_list = ["linux"]
pending_build.save
specific_runner.tag_list = ["linux"]
- expect(service.execute(specific_runner)).to eq(pending_build)
+ expect(execute(specific_runner)).to eq(pending_build)
end
it "does not pick build with different tag" do
pending_build.tag_list = ["linux"]
pending_build.save
specific_runner.tag_list = ["win32"]
- expect(service.execute(specific_runner)).to be_falsey
+ expect(execute(specific_runner)).to be_falsey
end
it "picks build without tag" do
- expect(service.execute(specific_runner)).to eq(pending_build)
+ expect(execute(specific_runner)).to eq(pending_build)
end
it "does not pick build with tag" do
pending_build.tag_list = ["linux"]
pending_build.save
- expect(service.execute(specific_runner)).to be_falsey
+ expect(execute(specific_runner)).to be_falsey
end
it "pick build without tag" do
specific_runner.tag_list = ["win32"]
- expect(service.execute(specific_runner)).to eq(pending_build)
+ expect(execute(specific_runner)).to eq(pending_build)
end
end
@@ -56,13 +55,13 @@ module Ci
end
it 'does not pick a build' do
- expect(service.execute(shared_runner)).to be_nil
+ expect(execute(shared_runner)).to be_nil
end
end
context 'for specific runner' do
it 'does not pick a build' do
- expect(service.execute(specific_runner)).to be_nil
+ expect(execute(specific_runner)).to be_nil
end
end
end
@@ -86,34 +85,34 @@ module Ci
it 'prefers projects without builds first' do
# it gets for one build from each of the projects
- expect(service.execute(shared_runner)).to eq(build1_project1)
- expect(service.execute(shared_runner)).to eq(build1_project2)
- expect(service.execute(shared_runner)).to eq(build1_project3)
+ expect(execute(shared_runner)).to eq(build1_project1)
+ expect(execute(shared_runner)).to eq(build1_project2)
+ expect(execute(shared_runner)).to eq(build1_project3)
# then it gets a second build from each of the projects
- expect(service.execute(shared_runner)).to eq(build2_project1)
- expect(service.execute(shared_runner)).to eq(build2_project2)
+ expect(execute(shared_runner)).to eq(build2_project1)
+ expect(execute(shared_runner)).to eq(build2_project2)
# in the end the third build
- expect(service.execute(shared_runner)).to eq(build3_project1)
+ expect(execute(shared_runner)).to eq(build3_project1)
end
it 'equalises number of running builds' do
# after finishing the first build for project 1, get a second build from the same project
- expect(service.execute(shared_runner)).to eq(build1_project1)
+ expect(execute(shared_runner)).to eq(build1_project1)
build1_project1.reload.success
- expect(service.execute(shared_runner)).to eq(build2_project1)
+ expect(execute(shared_runner)).to eq(build2_project1)
- expect(service.execute(shared_runner)).to eq(build1_project2)
+ expect(execute(shared_runner)).to eq(build1_project2)
build1_project2.reload.success
- expect(service.execute(shared_runner)).to eq(build2_project2)
- expect(service.execute(shared_runner)).to eq(build1_project3)
- expect(service.execute(shared_runner)).to eq(build3_project1)
+ expect(execute(shared_runner)).to eq(build2_project2)
+ expect(execute(shared_runner)).to eq(build1_project3)
+ expect(execute(shared_runner)).to eq(build3_project1)
end
end
context 'shared runner' do
- let(:build) { service.execute(shared_runner) }
+ let(:build) { execute(shared_runner) }
it { expect(build).to be_kind_of(Build) }
it { expect(build).to be_valid }
@@ -122,7 +121,7 @@ module Ci
end
context 'specific runner' do
- let(:build) { service.execute(specific_runner) }
+ let(:build) { execute(specific_runner) }
it { expect(build).to be_kind_of(Build) }
it { expect(build).to be_valid }
@@ -137,13 +136,13 @@ module Ci
end
context 'shared runner' do
- let(:build) { service.execute(shared_runner) }
+ let(:build) { execute(shared_runner) }
it { expect(build).to be_nil }
end
context 'specific runner' do
- let(:build) { service.execute(specific_runner) }
+ let(:build) { execute(specific_runner) }
it { expect(build).to be_kind_of(Build) }
it { expect(build).to be_valid }
@@ -159,17 +158,21 @@ module Ci
end
context 'and uses shared runner' do
- let(:build) { service.execute(shared_runner) }
+ let(:build) { execute(shared_runner) }
it { expect(build).to be_nil }
end
context 'and uses specific runner' do
- let(:build) { service.execute(specific_runner) }
+ let(:build) { execute(specific_runner) }
it { expect(build).to be_nil }
end
end
+
+ def execute(runner)
+ described_class.new(runner).execute.build
+ end
end
end
end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index b0cc3ce5f5a..9c92a5080c6 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -15,39 +15,45 @@ describe Notes::CreateService, services: true do
context "valid params" do
it 'returns a valid note' do
- note = Notes::CreateService.new(project, user, opts).execute
+ note = described_class.new(project, user, opts).execute
expect(note).to be_valid
end
it 'returns a persisted note' do
- note = Notes::CreateService.new(project, user, opts).execute
+ note = described_class.new(project, user, opts).execute
expect(note).to be_persisted
end
it 'note has valid content' do
- note = Notes::CreateService.new(project, user, opts).execute
+ note = described_class.new(project, user, opts).execute
expect(note.note).to eq(opts[:note])
end
+ it 'note belongs to the correct project' do
+ note = described_class.new(project, user, opts).execute
+
+ expect(note.project).to eq(project)
+ end
+
it 'TodoService#new_note is called' do
- note = build(:note)
- allow(project).to receive_message_chain(:notes, :new).with(opts) { note }
+ note = build(:note, project: project)
+ allow(Note).to receive(:new).with(opts) { note }
expect_any_instance_of(TodoService).to receive(:new_note).with(note, user)
- Notes::CreateService.new(project, user, opts).execute
+ described_class.new(project, user, opts).execute
end
it 'enqueues NewNoteWorker' do
- note = build(:note, id: 999)
- allow(project).to receive_message_chain(:notes, :new).with(opts) { note }
+ note = build(:note, id: 999, project: project)
+ allow(Note).to receive(:new).with(opts) { note }
expect(NewNoteWorker).to receive(:perform_async).with(note.id)
- Notes::CreateService.new(project, user, opts).execute
+ described_class.new(project, user, opts).execute
end
end
@@ -75,6 +81,27 @@ describe Notes::CreateService, services: true do
end
end
end
+
+ describe 'personal snippet note' do
+ subject { described_class.new(nil, user, params).execute }
+
+ let(:snippet) { create(:personal_snippet) }
+ let(:params) do
+ { note: 'comment', noteable_type: 'Snippet', noteable_id: snippet.id }
+ end
+
+ it 'returns a valid note' do
+ expect(subject).to be_valid
+ end
+
+ it 'returns a persisted note' do
+ expect(subject).to be_persisted
+ end
+
+ it 'note has valid content' do
+ expect(subject.note).to eq(params[:note])
+ end
+ end
end
describe "award emoji" do
@@ -88,7 +115,7 @@ describe Notes::CreateService, services: true do
noteable_type: 'Issue',
noteable_id: issue.id
}
- note = Notes::CreateService.new(project, user, opts).execute
+ note = described_class.new(project, user, opts).execute
expect(note).to be_valid
expect(note.name).to eq('smile')
@@ -100,7 +127,7 @@ describe Notes::CreateService, services: true do
noteable_type: 'Issue',
noteable_id: issue.id
}
- note = Notes::CreateService.new(project, user, opts).execute
+ note = described_class.new(project, user, opts).execute
expect(note).to be_valid
expect(note.note).to eq(opts[:note])
@@ -115,7 +142,7 @@ describe Notes::CreateService, services: true do
expect_any_instance_of(TodoService).to receive(:new_award_emoji).with(issue, user)
- Notes::CreateService.new(project, user, opts).execute
+ described_class.new(project, user, opts).execute
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index f3e80ac22a0..309985a5d90 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -33,6 +33,49 @@ describe NotificationService, services: true do
end
end
+ # Next shared examples are intended to test notifications of "participants"
+ #
+ # they take the following parameters:
+ # * issuable
+ # * notification trigger
+ # * participant
+ #
+ shared_examples 'participating by note notification' do
+ it 'emails the participant' do
+ create(:note_on_issue, noteable: issuable, project_id: project.id, note: 'anything', author: participant)
+
+ notification_trigger
+
+ should_email(participant)
+ end
+ end
+
+ shared_examples 'participating by assignee notification' do
+ it 'emails the participant' do
+ issuable.update_attribute(:assignee, participant)
+
+ notification_trigger
+
+ should_email(participant)
+ end
+ end
+
+ shared_examples 'participating by author notification' do
+ it 'emails the participant' do
+ issuable.author = participant
+
+ notification_trigger
+
+ should_email(participant)
+ end
+ end
+
+ shared_examples_for 'participating notifications' do
+ it_should_behave_like 'participating by note notification'
+ it_should_behave_like 'participating by author notification'
+ it_should_behave_like 'participating by assignee notification'
+ end
+
describe 'Keys' do
describe '#new_key' do
let!(:key) { create(:personal_key) }
@@ -269,6 +312,55 @@ describe NotificationService, services: true do
end
end
+ context 'personal snippet note' do
+ let(:snippet) { create(:personal_snippet, :public, author: @u_snippet_author) }
+ let(:note) { create(:note_on_personal_snippet, noteable: snippet, note: '@mentioned note', author: @u_note_author) }
+
+ before do
+ @u_watcher = create_global_setting_for(create(:user), :watch)
+ @u_participant = create_global_setting_for(create(:user), :participating)
+ @u_disabled = create_global_setting_for(create(:user), :disabled)
+ @u_mentioned = create_global_setting_for(create(:user, username: 'mentioned'), :mention)
+ @u_mentioned_level = create_global_setting_for(create(:user, username: 'participator'), :mention)
+ @u_note_author = create(:user, username: 'note_author')
+ @u_snippet_author = create(:user, username: 'snippet_author')
+ @u_not_mentioned = create_global_setting_for(create(:user, username: 'regular'), :participating)
+
+ reset_delivered_emails!
+ end
+
+ let!(:notes) do
+ [
+ create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_watcher),
+ create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_participant),
+ create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_mentioned),
+ create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_disabled),
+ create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_note_author),
+ ]
+ end
+
+ describe '#new_note' do
+ it 'notifies the participants' do
+ notification.new_note(note)
+
+ # it emails participants
+ should_email(@u_watcher)
+ should_email(@u_participant)
+ should_email(@u_watcher)
+ should_email(@u_snippet_author)
+
+ # it emails mentioned users
+ should_email(@u_mentioned)
+
+ # it does not email participants with mention notification level
+ should_not_email(@u_mentioned_level)
+
+ # it does not email note author
+ should_not_email(@u_note_author)
+ end
+ end
+ end
+
context 'commit note' do
let(:project) { create(:project, :public) }
let(:note) { create(:note_on_commit, project: project) }
@@ -539,32 +631,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- issue.update_attribute(:assignee, @u_lazy_participant)
- notification.reassigned_issue(issue, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.reassigned_issue(issue, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- issue.author = @u_lazy_participant
- notification.reassigned_issue(issue, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { issue }
+ let(:notification_trigger) { notification.reassigned_issue(issue, @u_disabled) }
end
end
@@ -671,32 +741,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- issue.update_attribute(:assignee, @u_lazy_participant)
- notification.close_issue(issue, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.close_issue(issue, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- issue.author = @u_lazy_participant
- notification.close_issue(issue, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { issue }
+ let(:notification_trigger) { notification.close_issue(issue, @u_disabled) }
end
end
@@ -723,32 +771,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- issue.update_attribute(:assignee, @u_lazy_participant)
- notification.reopen_issue(issue, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.reopen_issue(issue, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- issue.author = @u_lazy_participant
- notification.reopen_issue(issue, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { issue }
+ let(:notification_trigger) { notification.reopen_issue(issue, @u_disabled) }
end
end
end
@@ -809,31 +835,28 @@ describe NotificationService, services: true do
end
context 'participating' do
- context 'by assignee' do
- before do
- merge_request.update_attribute(:assignee, @u_lazy_participant)
- notification.new_merge_request(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
+ it_should_behave_like 'participating by assignee notification' do
+ let(:participant) { create(:user, username: 'user-participant')}
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.new_merge_request(merge_request, @u_disabled) }
end
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.new_merge_request(merge_request, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
+ it_should_behave_like 'participating by note notification' do
+ let(:participant) { create(:user, username: 'user-participant')}
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.new_merge_request(merge_request, @u_disabled) }
end
context 'by author' do
+ let(:participant) { create(:user, username: 'user-participant')}
+
before do
- merge_request.author = @u_lazy_participant
+ merge_request.author = participant
merge_request.save
notification.new_merge_request(merge_request, @u_disabled)
end
- it { should_not_email(@u_lazy_participant) }
+ it { should_not_email(participant) }
end
end
end
@@ -868,33 +891,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- merge_request.update_attribute(:assignee, @u_lazy_participant)
- notification.reassigned_merge_request(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.reassigned_merge_request(merge_request, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- merge_request.author = @u_lazy_participant
- merge_request.save
- notification.reassigned_merge_request(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.reassigned_merge_request(merge_request, @u_disabled) }
end
end
@@ -965,33 +965,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- merge_request.update_attribute(:assignee, @u_lazy_participant)
- notification.close_mr(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.close_mr(merge_request, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- merge_request.author = @u_lazy_participant
- merge_request.save
- notification.close_mr(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.close_mr(merge_request, @u_disabled) }
end
end
@@ -1032,33 +1009,10 @@ describe NotificationService, services: true do
should_not_email(@u_watcher)
end
- context 'participating' do
- context 'by assignee' do
- before do
- merge_request.update_attribute(:assignee, @u_lazy_participant)
- notification.merge_mr(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.merge_mr(merge_request, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- merge_request.author = @u_lazy_participant
- merge_request.save
- notification.merge_mr(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.merge_mr(merge_request, @u_disabled) }
end
end
@@ -1085,33 +1039,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- merge_request.update_attribute(:assignee, @u_lazy_participant)
- notification.reopen_mr(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.reopen_mr(merge_request, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- merge_request.author = @u_lazy_participant
- merge_request.save
- notification.reopen_mr(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.reopen_mr(merge_request, @u_disabled) }
end
end
@@ -1131,33 +1062,10 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- context 'participating' do
- context 'by assignee' do
- before do
- merge_request.update_attribute(:assignee, @u_lazy_participant)
- notification.resolve_all_discussions(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by note' do
- let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
-
- before { notification.resolve_all_discussions(merge_request, @u_disabled) }
-
- it { should_email(@u_lazy_participant) }
- end
-
- context 'by author' do
- before do
- merge_request.author = @u_lazy_participant
- merge_request.save
- notification.resolve_all_discussions(merge_request, @u_disabled)
- end
-
- it { should_email(@u_lazy_participant) }
- end
+ it_behaves_like 'participating notifications' do
+ let(:participant) { create(:user, username: 'user-participant') }
+ let(:issuable) { merge_request }
+ let(:notification_trigger) { notification.resolve_all_discussions(merge_request, @u_disabled) }
end
end
end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index fef211ded50..db9f1231682 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -12,6 +12,7 @@ describe SystemHooksService, services: true do
it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) }
it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) }
it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
+ it { expect(event_data(project, :update)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
@@ -68,6 +69,7 @@ describe SystemHooksService, services: true do
it { expect(event_name(project, :destroy)).to eq "project_destroy" }
it { expect(event_name(project, :rename)).to eq "project_rename" }
it { expect(event_name(project, :transfer)).to eq "project_transfer" }
+ it { expect(event_name(project, :update)).to eq "project_update" }
it { expect(event_name(project_member, :create)).to eq "user_add_to_team" }
it { expect(event_name(project_member, :destroy)).to eq "user_remove_from_team" }
it { expect(event_name(key, :create)).to eq 'key_create' }
diff --git a/spec/services/user_project_access_changed_service_spec.rb b/spec/services/user_project_access_changed_service_spec.rb
new file mode 100644
index 00000000000..b4efe7de431
--- /dev/null
+++ b/spec/services/user_project_access_changed_service_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe UserProjectAccessChangedService do
+ describe '#execute' do
+ it 'schedules the user IDs' do
+ expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait).
+ with([[1], [2]])
+
+ described_class.new([1, 2]).execute
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 6ee3307512d..ab38dac65c5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -2,13 +2,17 @@ require './spec/simplecov_env'
SimpleCovEnv.start!
ENV["RAILS_ENV"] ||= 'test'
+ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'shoulda/matchers'
-require 'sidekiq/testing/inline'
require 'rspec/retry'
+if ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING']
+ require 'rspec_profiling/rspec'
+end
+
if ENV['CI'] && !ENV['NO_KNAPSACK']
require 'knapsack'
Knapsack::Adapters::RSpecAdapter.bind
diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb
index 35b40d73191..10b90b40ba7 100644
--- a/spec/support/cycle_analytics_helpers/test_generation.rb
+++ b/spec/support/cycle_analytics_helpers/test_generation.rb
@@ -54,7 +54,7 @@ module CycleAnalyticsHelpers
end
context "when the data belongs to another project" do
- let(:other_project) { create(:project) }
+ let(:other_project) { create(:project, :repository) }
it "returns nil" do
# Use a stub to "trick" the data/condition functions
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index f57c82809a6..87936bb4859 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -12,7 +12,7 @@ shared_context 'mentionable context' do
let!(:mentioned_mr) { create(:merge_request, source_project: project) }
let(:mentioned_commit) { project.commit("HEAD~1") }
- let(:ext_proj) { create(:project, :public) }
+ let(:ext_proj) { create(:project, :public, :repository) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let(:ext_mr) { create(:merge_request, :simple, source_project: ext_proj) }
let(:ext_commit) { ext_proj.commit("HEAD~2") }
diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb
new file mode 100644
index 00000000000..575d3451150
--- /dev/null
+++ b/spec/support/sidekiq.rb
@@ -0,0 +1,5 @@
+require 'sidekiq/testing/inline'
+
+Sidekiq::Testing.server_middleware do |chain|
+ chain.add Gitlab::SidekiqStatus::ServerMiddleware
+end
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index 74d9b8c6313..704922b6cf4 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -26,7 +26,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
describe "#execute" do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:username) { 'slack_username' }
let(:channel) { 'slack_channel' }
@@ -196,7 +196,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
describe "Note events" do
let(:user) { create(:user) }
- let(:project) { create(:project, creator_id: user.id) }
+ let(:project) { create(:project, :repository, creator: user) }
before do
allow(chat_service).to receive_messages(
@@ -269,7 +269,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do
describe 'Pipeline events' do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline,
diff --git a/spec/support/taskable_shared_examples.rb b/spec/support/taskable_shared_examples.rb
index 1b6c33248c9..4056ff06b84 100644
--- a/spec/support/taskable_shared_examples.rb
+++ b/spec/support/taskable_shared_examples.rb
@@ -72,6 +72,25 @@ shared_examples 'a Taskable' do
end
end
+ describe 'with tasks that are not formatted correctly' do
+ before do
+ subject.description = <<-EOT.strip_heredoc
+ [ ] task 1
+ [ ] task 2
+
+ - [ ]task 1
+ -[ ] task 2
+ EOT
+ end
+
+ it 'returns the correct task status' do
+ expect(subject.task_status).to match('0 of')
+ expect(subject.task_status).to match('0 tasks completed')
+ expect(subject.task_status_short).to match('0/')
+ expect(subject.task_status_short).to match('0 task')
+ end
+ end
+
describe 'with a complete task' do
before do
subject.description = <<-EOT.strip_heredoc
diff --git a/spec/views/ci/lints/show.html.haml_spec.rb b/spec/views/ci/lints/show.html.haml_spec.rb
index 2dac5ee23c8..3390ae247ff 100644
--- a/spec/views/ci/lints/show.html.haml_spec.rb
+++ b/spec/views/ci/lints/show.html.haml_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'ci/lints/show' do
- include Devise::TestHelpers
+ include Devise::Test::ControllerHelpers
describe 'XSS protection' do
let(:config_processor) { Ci::GitlabCiYamlProcessor.new(YAML.dump(content)) }
diff --git a/spec/views/projects/builds/show.html.haml_spec.rb b/spec/views/projects/builds/show.html.haml_spec.rb
index 745d0c745bd..44870cfcfb3 100644
--- a/spec/views/projects/builds/show.html.haml_spec.rb
+++ b/spec/views/projects/builds/show.html.haml_spec.rb
@@ -15,6 +15,36 @@ describe 'projects/builds/show', :view do
allow(view).to receive(:can?).and_return(true)
end
+ describe 'build information in header' do
+ let(:build) do
+ create(:ci_build, :success, environment: 'staging')
+ end
+
+ before do
+ render
+ end
+
+ it 'shows status name' do
+ expect(rendered).to have_css('.ci-status.ci-success', text: 'passed')
+ end
+
+ it 'does not render a link to the build' do
+ expect(rendered).not_to have_link('passed')
+ end
+
+ it 'shows build id' do
+ expect(rendered).to have_css('.js-build-id', text: build.id)
+ end
+
+ it 'shows a link to the pipeline' do
+ expect(rendered).to have_link(build.pipeline.id)
+ end
+
+ it 'shows a link to the commit' do
+ expect(rendered).to have_link(build.pipeline.short_sha)
+ end
+ end
+
describe 'environment info in build view' do
context 'build with latest deployment' do
let(:build) do
diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb
index b6591f272f6..97c4bfcd248 100644
--- a/spec/workers/authorized_projects_worker_spec.rb
+++ b/spec/workers/authorized_projects_worker_spec.rb
@@ -3,6 +3,18 @@ require 'spec_helper'
describe AuthorizedProjectsWorker do
let(:worker) { described_class.new }
+ describe '.bulk_perform_and_wait' do
+ it 'schedules the ids and waits for the jobs to complete' do
+ project = create(:project)
+
+ project.owner.project_authorizations.delete_all
+
+ described_class.bulk_perform_and_wait([[project.owner.id]])
+
+ expect(project.owner.project_authorizations.count).to eq(1)
+ end
+ end
+
describe '#perform' do
it "refreshes user's authorized projects" do
user = create(:user)