summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-11-30 12:21:33 +0100
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-11-30 12:21:33 +0100
commit00ca7adca2de8ff05cca3df9eb2df8a67f638cfe (patch)
tree86764da0f493a7169b2fea59e5618b669cd64d56
parentadb3f3d4e494e8f8d41c1b9e676e395a49cd96b2 (diff)
parent7e5fa10b665835e3160eee4d333a17fbaef9c113 (diff)
downloadgitlab-ce-00ca7adca2de8ff05cca3df9eb2df8a67f638cfe.tar.gz
Merge branch 'master' into fix/rename-mwbs-to-merge-when-pipeline-succeeds
* master: (110 commits) Rewrite an HTTP link to use HTTPS Edit /spec/features/profiles/preferences_spec.rb to match changes in 084d90ac Add blue back to sub nav active Remove JSX/React eslint plugins. Fix a transient spec failure Adds hoverstates for collapsed Issue/Merge Request sidebar Moved groups above projects Add StackProf to the Gemfile, along with a utility to get a profile for a spec Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1 Add a CHANGELOG entry Alert user when logged in user email is not the same as the invitation Expose timestamp in build entity used by serializer Rename `MergeRequest#pipeline` to `head_pipeline` Remove unnecessary database indexes CE-specific changes gitlab-org/gitlab-ee#1137 Fixing typo & Clarifying Key name fix started_at check fix blob controller spec failure - updated not to use file-path- fix blob controller spec failure Merge branch 'jej-use-issuable-finder-instead-of-access-check' into 'security' ... Conflicts: app/controllers/projects/merge_requests_controller.rb lib/api/merge_requests.rb spec/requests/api/merge_requests_spec.rb
-rw-r--r--.eslintignore1
-rw-r--r--.eslintrc7
-rw-r--r--CHANGELOG.md29
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock11
-rw-r--r--README.md4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.js.es613
-rw-r--r--app/assets/javascripts/dispatcher.js.es63
-rw-r--r--app/assets/javascripts/environments/components/environment.js.es62
-rw-r--r--app/assets/javascripts/extensions/array.js8
-rw-r--r--app/assets/javascripts/extensions/array.js.es624
-rw-r--r--app/assets/javascripts/labels_select.js2
-rw-r--r--app/assets/javascripts/notes.js2
-rw-r--r--app/assets/javascripts/project_variables.js.es643
-rw-r--r--app/assets/stylesheets/framework/avatar.scss1
-rw-r--r--app/assets/stylesheets/framework/buttons.scss26
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss85
-rw-r--r--app/assets/stylesheets/framework/nav.scss51
-rw-r--r--app/assets/stylesheets/framework/variables.scss84
-rw-r--r--app/assets/stylesheets/mailers/highlighted_diff_email.scss (renamed from app/assets/stylesheets/mailers/repository_push_email.scss)0
-rw-r--r--app/assets/stylesheets/pages/issuable.scss16
-rw-r--r--app/assets/stylesheets/pages/projects.scss8
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/concerns/lfs_request.rb (renamed from app/helpers/lfs_helper.rb)76
-rw-r--r--app/controllers/concerns/toggle_award_emoji.rb5
-rw-r--r--app/controllers/concerns/workhorse_request.rb13
-rw-r--r--app/controllers/help_controller.rb6
-rw-r--r--app/controllers/profiles/avatars_controller.rb1
-rw-r--r--app/controllers/projects/avatars_controller.rb1
-rw-r--r--app/controllers/projects/blob_controller.rb20
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb2
-rw-r--r--app/controllers/projects/git_http_client_controller.rb16
-rw-r--r--app/controllers/projects/git_http_controller.rb12
-rw-r--r--app/controllers/projects/lfs_api_controller.rb21
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb7
-rw-r--r--app/controllers/projects/merge_requests_controller.rb18
-rw-r--r--app/controllers/projects/todos_controller.rb8
-rw-r--r--app/finders/issuable_finder.rb12
-rw-r--r--app/finders/notes_finder.rb2
-rw-r--r--app/helpers/dropdowns_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb6
-rw-r--r--app/helpers/issuables_helper.rb20
-rw-r--r--app/mailers/emails/notes.rb2
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/protected_branch_access.rb9
-rw-r--r--app/models/cycle_analytics.rb5
-rw-r--r--app/models/cycle_analytics/summary.rb5
-rw-r--r--app/models/discussion.rb12
-rw-r--r--app/models/event.rb10
-rw-r--r--app/models/issue.rb12
-rw-r--r--app/models/merge_request.rb20
-rw-r--r--app/models/note.rb13
-rw-r--r--app/models/project.rb21
-rw-r--r--app/models/protected_branch/merge_access_level.rb9
-rw-r--r--app/models/protected_branch/push_access_level.rb6
-rw-r--r--app/models/repository.rb14
-rw-r--r--app/models/user.rb50
-rw-r--r--app/serializers/analytics_build_entity.rb2
-rw-r--r--app/serializers/build_entity.rb3
-rw-r--r--app/serializers/entity_date_helper.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb12
-rw-r--r--app/services/ci/process_pipeline_service.rb22
-rw-r--r--app/services/git_hooks_service.rb6
-rw-r--r--app/services/issuable_base_service.rb10
-rw-r--r--app/services/labels/find_or_create_service.rb7
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/merge_requests/build_service.rb2
-rw-r--r--app/services/notes/delete_service.rb1
-rw-r--r--app/services/notes/update_service.rb1
-rw-r--r--app/services/projects/transfer_service.rb3
-rw-r--r--app/uploaders/avatar_uploader.rb6
-rw-r--r--app/views/admin/application_settings/_form.html.haml11
-rw-r--r--app/views/dashboard/_projects_head.html.haml6
-rw-r--r--app/views/dashboard/projects/_zero_authorized_projects.html.haml23
-rw-r--r--app/views/dashboard/todos/index.html.haml4
-rw-r--r--app/views/events/_event.html.haml19
-rw-r--r--app/views/events/event/_push.html.haml6
-rw-r--r--app/views/explore/groups/index.html.haml4
-rw-r--r--app/views/explore/projects/_filter.html.haml8
-rw-r--r--app/views/invites/show.html.haml13
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/_user_styles.html.haml24
-rw-r--r--app/views/layouts/nav/_project.html.haml2
-rw-r--r--app/views/notify/_note_message.text.erb5
-rw-r--r--app/views/notify/_note_mr_or_commit_email.html.haml18
-rw-r--r--app/views/notify/_note_mr_or_commit_email.text.erb8
-rw-r--r--app/views/notify/_simple_diff.text.erb3
-rw-r--r--app/views/notify/note_commit_email.html.haml4
-rw-r--r--app/views/notify/note_commit_email.text.erb11
-rw-r--r--app/views/notify/note_merge_request_email.html.haml9
-rw-r--r--app/views/notify/note_merge_request_email.text.erb11
-rw-r--r--app/views/notify/repository_push_email.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/branches/index.html.haml4
-rw-r--r--app/views/projects/builds/_sidebar.html.haml2
-rw-r--r--app/views/projects/diffs/_file.html.haml2
-rw-r--r--app/views/projects/diffs/_line.html.haml2
-rw-r--r--app/views/projects/forks/index.html.haml4
-rw-r--r--app/views/projects/issues/_merge_requests.html.haml6
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml8
-rw-r--r--app/views/projects/merge_requests/_show.html.haml5
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/_show.html.haml2
-rw-r--r--app/views/projects/tags/index.html.haml4
-rw-r--r--app/views/projects/variables/_table.html.haml4
-rw-r--r--app/views/projects/variables/index.html.haml1
-rw-r--r--app/views/search/_filter.html.haml4
-rw-r--r--app/views/shared/_group_form.html.haml5
-rw-r--r--app/views/shared/_sort_dropdown.html.haml4
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml48
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/form/_description.html.haml15
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml38
-rw-r--r--app/workers/authorized_projects_worker.rb23
-rwxr-xr-xbin/rspec-stackprof16
-rw-r--r--changelogs/unreleased/22373-reduce-queries-in-api-helpers-find_project.yml4
-rw-r--r--changelogs/unreleased/24135-new-project-should-be-below-new-group-on-the-welcome-screen.yml4
-rw-r--r--changelogs/unreleased/24150-consistent-dropdown-styles.yml4
-rw-r--r--changelogs/unreleased/24161-non-intuitive-buttons-for-import-sources-in-administrator-settings-enable-disable.yml4
-rw-r--r--changelogs/unreleased/24266-Afraid-to-press-the-Orange-button-on-Merge-request-screen.yml4
-rw-r--r--changelogs/unreleased/24739-collapsed-build-list-sorting.yml4
-rw-r--r--changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml4
-rw-r--r--changelogs/unreleased/24804-wrong-render-index-should-be-render-show-in-projects-pipelinessettingscontroller-update.yml4
-rw-r--r--changelogs/unreleased/24813-project-members-with-developer-access-can-no-longer-create-tags.yml4
-rw-r--r--changelogs/unreleased/24860-actionview-template-error-undefined-method-size-for-nil-nilclass.yml4
-rw-r--r--changelogs/unreleased/24863-mrs-without-discussions-are-mergeable.yml4
-rw-r--r--changelogs/unreleased/24999-fix-project-avatar-alignment.yml4
-rw-r--r--changelogs/unreleased/25002-sentence-case-dashboard-tabs.yml4
-rw-r--r--changelogs/unreleased/25011-hoverstates-for-collapsed-issue-merge-request-sidebar.yml4
-rw-r--r--changelogs/unreleased/25026-authenticate-user-for-new-snippet.yml4
-rw-r--r--changelogs/unreleased/25055-pipelines-info-missing-from-mr-widget.yml4
-rw-r--r--changelogs/unreleased/7749-add-setting-to-disable-html-emails.yml3
-rw-r--r--changelogs/unreleased/Last-minute-CI-Style-tweaks-for-8-14.yml4
-rw-r--r--changelogs/unreleased/create-dynamic-fixture-for-build_spec.yml4
-rw-r--r--changelogs/unreleased/disable-calendar-deselection.yml4
-rw-r--r--changelogs/unreleased/events-cache-invalidation.yml4
-rw-r--r--changelogs/unreleased/fix-build-without-trace-exceptions.yml4
-rw-r--r--changelogs/unreleased/fix-ca-no-date.yml4
-rw-r--r--changelogs/unreleased/fix-create-pipeline-with-builds-in-transaction.yml4
-rw-r--r--changelogs/unreleased/fix-cycle-analytics-plan-issue.yml4
-rw-r--r--changelogs/unreleased/fix_sidekiq_stats_in_admin_area.yml4
-rw-r--r--changelogs/unreleased/hoopes-gitlab-ce-21027-add-diff-hunks-to-notification-emails.yml4
-rw-r--r--changelogs/unreleased/improve-invite-accept-page.yml4
-rw-r--r--changelogs/unreleased/issuable_filters_present-refactor.yml4
-rw-r--r--changelogs/unreleased/issue-24534.yml4
-rw-r--r--changelogs/unreleased/issue-boards-dragging-fix.yml4
-rw-r--r--changelogs/unreleased/jej-22869.yml4
-rw-r--r--changelogs/unreleased/jej-fix-missing-access-check-on-issues.yml4
-rw-r--r--changelogs/unreleased/jej-use-issuable-finder-instead-of-access-check.yml4
-rw-r--r--changelogs/unreleased/readme-link-fix.yml4
-rw-r--r--changelogs/unreleased/refresh-authorizations-with-lease.yml4
-rw-r--r--changelogs/unreleased/remove-jsx-react-eslint-plugins.yml5
-rw-r--r--changelogs/unreleased/removing_unnecessary_indexes.yml4
-rw-r--r--changelogs/unreleased/sh-update-sidekiq-cron.yml4
-rw-r--r--changelogs/unreleased/timeout-merge-request-for-binary-file.yml4
-rw-r--r--changelogs/unreleased/workhorse-v1-0-1.yml4
-rw-r--r--changelogs/unreleased/zen-mode-fixture.yml4
-rw-r--r--changelogs/unreleased/zj-expose-coverage-pipelines.yml4
-rw-r--r--changelogs/unreleased/zj-fix-label-creation-non-members.yml4
-rw-r--r--changelogs/unreleased/zj-issue-search-slash-command.yml4
-rw-r--r--changelogs/unreleased/zj-upgrade-grape.yml4
-rw-r--r--config/initializers/email_template_interceptor.rb2
-rw-r--r--db/fixtures/development/04_project.rb1
-rw-r--r--db/fixtures/development/06_teams.rb1
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb1
-rw-r--r--db/fixtures/support/serialized_transaction.rb9
-rw-r--r--db/migrate/20130319214458_create_forked_project_links.rb2
-rw-r--r--db/migrate/20130506090604_create_deploy_keys_projects.rb2
-rw-r--r--db/migrate/20130617095603_create_users_groups.rb2
-rw-r--r--db/migrate/20130711063759_create_project_group_links.rb2
-rw-r--r--db/migrate/20131112114325_create_broadcast_messages.rb2
-rw-r--r--db/migrate/20140122112253_create_merge_request_diffs.rb2
-rw-r--r--db/migrate/20140209025651_create_emails.rb2
-rw-r--r--db/migrate/20140625115202_create_users_star_projects.rb2
-rw-r--r--db/migrate/20140729134820_create_labels.rb2
-rw-r--r--db/migrate/20140729140420_create_label_links.rb2
-rw-r--r--db/migrate/20140914113604_add_members_table.rb2
-rw-r--r--db/migrate/20140914173417_remove_old_member_tables.rb4
-rw-r--r--db/migrate/20141118150935_add_audit_event.rb2
-rw-r--r--db/migrate/20141216155758_create_doorkeeper_tables.rb2
-rw-r--r--db/migrate/20150108073740_create_application_settings.rb2
-rw-r--r--db/migrate/20150313012111_create_subscriptions_table.rb2
-rw-r--r--db/migrate/20150806104937_create_abuse_reports.rb2
-rw-r--r--db/migrate/20151103134857_create_lfs_objects.rb2
-rw-r--r--db/migrate/20151103134958_create_lfs_objects_projects.rb2
-rw-r--r--db/migrate/20151105094515_create_releases.rb2
-rw-r--r--db/migrate/20160212123307_create_tasks.rb2
-rw-r--r--db/migrate/20160416180807_add_award_emoji.rb2
-rw-r--r--db/migrate/20160831214002_create_project_features.rb2
-rw-r--r--db/migrate/20161128142110_remove_unnecessary_indexes.rb33
-rw-r--r--db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb29
-rw-r--r--db/schema.rb12
-rw-r--r--doc/administration/auth/ldap.md2
-rw-r--r--doc/api/access_requests.md8
-rw-r--r--doc/api/award_emoji.md4
-rw-r--r--doc/api/boards.md10
-rw-r--r--doc/api/branches.md7
-rw-r--r--doc/api/broadcast_messages.md4
-rw-r--r--doc/api/issues.md31
-rw-r--r--doc/api/labels.md25
-rw-r--r--doc/api/members.md10
-rw-r--r--doc/api/merge_requests.md43
-rw-r--r--doc/api/notes.md9
-rw-r--r--doc/api/pipelines.md18
-rw-r--r--doc/api/projects.md19
-rw-r--r--doc/api/system_hooks.md3
-rw-r--r--doc/api/tags.md21
-rw-r--r--doc/api/users.md8
-rw-r--r--doc/development/doc_styleguide.md71
-rw-r--r--doc/development/limit_ee_conflicts.md111
-rw-r--r--doc/development/performance.md110
-rw-r--r--doc/development/ux_guide/components.md20
-rw-r--r--doc/development/ux_guide/img/components-anchorlinks.pngbin19948 -> 30089 bytes
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/integration/bitbucket.md4
-rw-r--r--doc/update/8.13-to-8.14.md2
-rw-r--r--features/steps/shared/issuable.rb6
-rw-r--r--features/support/capybara.rb10
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/groups.rb8
-rw-r--r--lib/api/helpers.rb40
-rw-r--r--lib/api/helpers/members_helpers.rb2
-rw-r--r--lib/api/issues.rb78
-rw-r--r--lib/api/merge_requests.rb12
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/services.rb2
-rw-r--r--lib/api/triggers.rb2
-rw-r--r--lib/email_template_interceptor.rb13
-rw-r--r--lib/gitlab/chat_commands/base_command.rb4
-rw-r--r--lib/gitlab/chat_commands/command.rb1
-rw-r--r--lib/gitlab/chat_commands/issue_command.rb6
-rw-r--r--lib/gitlab/chat_commands/issue_search.rb17
-rw-r--r--lib/gitlab/chat_commands/issue_show.rb2
-rw-r--r--lib/gitlab/database.rb7
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff.rb6
-rw-r--r--lib/gitlab/search_results.rb2
-rw-r--r--package.json10
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb4
-rw-r--r--spec/controllers/help_controller_spec.rb14
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb49
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb18
-rw-r--r--spec/controllers/projects/todo_controller_spec.rb17
-rw-r--r--spec/controllers/snippets_controller_spec.rb22
-rw-r--r--spec/factories/ci/pipelines.rb28
-rw-r--r--spec/features/help_pages_spec.rb24
-rw-r--r--spec/features/profiles/preferences_spec.rb2
-rw-r--r--spec/features/projects/blobs/edit_spec.rb45
-rw-r--r--spec/features/security/project/internal_access_spec.rb517
-rw-r--r--spec/features/security/project/private_access_spec.rb445
-rw-r--r--spec/features/security/project/public_access_spec.rb515
-rw-r--r--spec/features/snippets/create_snippet_spec.rb20
-rw-r--r--spec/features/variables_spec.rb25
-rw-r--r--spec/helpers/issuables_helper_spec.rb21
-rw-r--r--spec/javascripts/build_spec.js.es6279
-rw-r--r--spec/javascripts/environments/environment_actions_spec.js.es64
-rw-r--r--spec/javascripts/environments/environment_item_spec.js.es620
-rw-r--r--spec/javascripts/environments/environments_store_spec.js.es68
-rw-r--r--spec/javascripts/extensions/array_spec.js23
-rw-r--r--spec/javascripts/extensions/array_spec.js.es646
-rw-r--r--spec/javascripts/fixtures/build.html.haml62
-rw-r--r--spec/javascripts/fixtures/builds.rb33
-rw-r--r--spec/javascripts/fixtures/issues.rb3
-rw-r--r--spec/javascripts/fixtures/zen_mode.html.haml8
-rw-r--r--spec/javascripts/issue_spec.js2
-rw-r--r--spec/javascripts/smart_interval_spec.js.es62
-rw-r--r--spec/javascripts/spec_helper.js5
-rw-r--r--spec/javascripts/vue_common_components/commit_spec.js.es612
-rw-r--r--spec/javascripts/zen_mode_spec.js9
-rw-r--r--spec/lib/gitlab/chat_commands/issue_search_spec.rb46
-rw-r--r--spec/lib/gitlab/chat_commands/issue_show_spec.rb8
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb13
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb9
-rw-r--r--spec/lib/gitlab/search_results_spec.rb51
-rw-r--r--spec/mailers/notify_spec.rb113
-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.rb2
-rw-r--r--spec/models/cycle_analytics/summary_spec.rb2
-rw-r--r--spec/models/cycle_analytics/test_spec.rb2
-rw-r--r--spec/models/discussion_spec.rb19
-rw-r--r--spec/models/event_spec.rb18
-rw-r--r--spec/models/merge_request_spec.rb18
-rw-r--r--spec/models/project_spec.rb16
-rw-r--r--spec/models/repository_spec.rb40
-rw-r--r--spec/models/user_spec.rb27
-rw-r--r--spec/requests/api/issues_spec.rb32
-rw-r--r--spec/requests/api/merge_requests_spec.rb30
-rw-r--r--spec/requests/api/pipelines_spec.rb12
-rw-r--r--spec/requests/api/services_spec.rb2
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb2
-rw-r--r--spec/serializers/analytics_build_entity_spec.rb53
-rw-r--r--spec/serializers/build_entity_spec.rb25
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb137
-rw-r--r--spec/services/labels/transfer_service_spec.rb2
-rw-r--r--spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb12
-rw-r--r--spec/services/merge_requests/build_service_spec.rb12
-rw-r--r--spec/services/merge_requests/merge_request_diff_cache_service_spec.rb1
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb22
-rw-r--r--spec/support/capybara.rb10
-rw-r--r--spec/support/javascript_fixtures_helpers.rb7
-rw-r--r--spec/support/matchers/access_matchers.rb35
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb4
-rw-r--r--spec/views/projects/merge_requests/_new_submit.html.haml_spec.rb31
-rw-r--r--spec/workers/authorized_projects_worker_spec.rb23
311 files changed, 3323 insertions, 2250 deletions
diff --git a/.eslintignore b/.eslintignore
index d9c2233c9d7..93de4b10dfe 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,4 @@
+/coverage/
/coverage-javascript/
/public/
/tmp/
diff --git a/.eslintrc b/.eslintrc
index 788a88487d8..b80dcec9d1d 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,15 +1,14 @@
{
"env": {
+ "jquery": true,
"browser": true,
"es6": true
},
- "extends": "airbnb",
+ "extends": "airbnb-base",
"globals": {
- "$": false,
"_": false,
"gl": false,
- "gon": false,
- "jQuery": false
+ "gon": false
},
"plugins": [
"filenames"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 549336e4dff..12a3e63ed2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,26 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 8.14.1 (2016-11-28)
+
+- Fix deselecting calendar days on contribution graph. !6453 (ClemMakesApps)
+- Update grape entity to 0.6.0. !7491
+- If Build running change accept merge request when build succeeds button from orange to blue. !7577
+- Changed import sources buttons to checkboxes. !7598 (Luke "Jared" Bennett)
+- Last minute CI Style tweaks for 8.14. !7643
+- Fix exceptions when loading build trace. !7658
+- Fix wrong template rendered when CI/CD settings aren't update successfully. !7665
+- fixes last_deployment call environment is nil. !7671
+- Sort builds by name within pipeline graph. !7681
+- Correctly determine mergeability of MR with no discussions.
+- Sidekiq stats in the admin area will now show correctly on different platforms. (blackst0ne)
+- Fixed issue boards dragging card removing random issues.
+- Fix information disclosure in `Projects::BlobController#update`.
+- Fix missing access checks on issue lookup using IssuableFinder.
+- Replace issue access checks with use of IssuableFinder.
+- Non members cannot create labels through the API.
+- Fix cycle analytics plan stage when commits are missing.
+
## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
@@ -202,6 +222,15 @@ entry.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page
+## 8.13.7 (2016-11-28)
+
+- fixes 500 error on project show when user is not logged in and project is still empty. !7376
+- Update grape entity to 0.6.0. !7491
+- Fix information disclosure in `Projects::BlobController#update`.
+- Fix missing access checks on issue lookup using IssuableFinder.
+- Replace issue access checks with use of IssuableFinder.
+- Non members cannot create labels through the API.
+
## 8.13.6 (2016-11-17)
- Omniauth auto link LDAP user falls back to find by DN when user cannot be found by UID. !7002
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 3eefcb9dd5b..7dea76edb3d 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-1.0.0
+1.0.1
diff --git a/Gemfile b/Gemfile
index 1d686199557..83edf420798 100644
--- a/Gemfile
+++ b/Gemfile
@@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs
gem 'sidekiq', '~> 4.2'
-gem 'sidekiq-cron', '~> 0.4.0'
+gem 'sidekiq-cron', '~> 0.4.4'
gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4'
@@ -309,6 +309,8 @@ group :development, :test do
gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2'
+
+ gem 'stackprof', '~> 0.2.10'
end
group :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index bf9702b2562..1db0e466164 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -614,7 +614,8 @@ GEM
rubyntlm (0.5.2)
rubypants (0.2.0)
rubyzip (1.2.0)
- rufus-scheduler (3.1.10)
+ rufus-scheduler (3.3.0)
+ tzinfo
rugged (0.24.0)
safe_yaml (1.0.4)
sanitize (2.1.0)
@@ -650,10 +651,10 @@ GEM
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (~> 1.5)
redis (~> 3.2, >= 3.2.1)
- sidekiq-cron (0.4.0)
+ sidekiq-cron (0.4.4)
redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24)
- sidekiq (>= 4.0.0)
+ sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4)
simplecov (0.12.0)
@@ -691,6 +692,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
+ stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1)
@@ -925,7 +927,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2)
- sidekiq-cron (~> 0.4.0)
+ sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0)
slack-notifier (~> 1.2.0)
@@ -937,6 +939,7 @@ DEPENDENCIES
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.7.0)
sprockets-es6 (~> 0.9.2)
+ stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0)
diff --git a/README.md b/README.md
index f63543ca39d..61204630fd2 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# 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)](http://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
+[![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)
@@ -84,7 +84,7 @@ For more information please see the [architecture documentation](https://docs.gi
## UX design
-Please adhere to the [UX Guide](doc/development/ux_guide/readme.md) when creating designs and implementing code.
+Please adhere to the [UX Guide](doc/development/ux_guide/index.md) when creating designs and implementing code.
## Third-party applications
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
index b9675f50e31..0d85e1a4678 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
@@ -10,10 +10,15 @@
},
template: `
<span class="total-time">
- <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
- <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
- <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
- <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
+ <template v-if="Object.keys(time).length">
+ <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
+ <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
+ <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
+ <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
+ </template>
+ <template v-else>
+ --
+ </template>
</span>
`,
});
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index c2d4670b7e9..16df4b0b005 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -208,6 +208,9 @@
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
break;
+ case 'projects:variables:index':
+ new gl.ProjectVariables();
+ break;
}
switch (path.first()) {
case 'admin':
diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6
index 35e183a9086..84faabf938a 100644
--- a/app/assets/javascripts/environments/components/environment.js.es6
+++ b/app/assets/javascripts/environments/components/environment.js.es6
@@ -181,7 +181,7 @@
<div class="environments-container">
<div class="environments-list-loading text-center" v-if="isLoading">
- <i class="fa fa-spinner spin"></i>
+ <i class="fa fa-spinner fa-spin"></i>
</div>
<div class="blank-state blank-state-no-icon"
diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js
deleted file mode 100644
index fc6c130113d..00000000000
--- a/app/assets/javascripts/extensions/array.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
-Array.prototype.first = function() {
- return this[0];
-}
-
-Array.prototype.last = function() {
- return this[this.length-1];
-}
diff --git a/app/assets/javascripts/extensions/array.js.es6 b/app/assets/javascripts/extensions/array.js.es6
new file mode 100644
index 00000000000..717566a4715
--- /dev/null
+++ b/app/assets/javascripts/extensions/array.js.es6
@@ -0,0 +1,24 @@
+/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
+Array.prototype.first = function() {
+ return this[0];
+}
+
+Array.prototype.last = function() {
+ return this[this.length-1];
+}
+
+Array.prototype.find = Array.prototype.find || function(predicate, ...args) {
+ if (!this) throw new TypeError('Array.prototype.find called on null or undefined');
+ if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
+
+ const list = Object(this);
+ const thisArg = args[1];
+ let value = {};
+
+ for (let i = 0; i < list.length; i += 1) {
+ value = list[i];
+ if (predicate.call(thisArg, value, i, list)) return value;
+ }
+
+ return undefined;
+};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 812d5cde685..f334f35594d 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
+/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 47e7b6f831b..0ca0e255595 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
+/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/*= require autosave */
/*= require autosize */
diff --git a/app/assets/javascripts/project_variables.js.es6 b/app/assets/javascripts/project_variables.js.es6
new file mode 100644
index 00000000000..4ee2e49306d
--- /dev/null
+++ b/app/assets/javascripts/project_variables.js.es6
@@ -0,0 +1,43 @@
+(() => {
+ const HIDDEN_VALUE_TEXT = '******';
+
+ class ProjectVariables {
+ constructor() {
+ this.$revealBtn = $('.js-btn-toggle-reveal-values');
+ this.$revealBtn.on('click', this.toggleRevealState.bind(this));
+ }
+
+ toggleRevealState(e) {
+ e.preventDefault();
+
+ const oldStatus = this.$revealBtn.attr('data-status');
+ let newStatus = 'hidden';
+ let newAction = 'Reveal Values';
+
+ if (oldStatus === 'hidden') {
+ newStatus = 'revealed';
+ newAction = 'Hide Values';
+ }
+
+ this.$revealBtn.attr('data-status', newStatus);
+
+ const $variables = $('.variable-value');
+
+ $variables.each((_, variable) => {
+ const $variable = $(variable);
+ let newText = HIDDEN_VALUE_TEXT;
+
+ if (newStatus === 'revealed') {
+ newText = $variable.attr('data-value');
+ }
+
+ $variable.text(newText);
+ });
+
+ this.$revealBtn.text(newAction);
+ }
+ }
+
+ window.gl = window.gl || {};
+ window.gl.ProjectVariables = ProjectVariables;
+})();
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index ad0d387067f..c0dd1cb3667 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -80,6 +80,7 @@
border-radius: 0;
border: none;
height: auto;
+ width: 100%;
margin: 0;
align-self: center;
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 4a9aa0f8717..36f530af685 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -15,7 +15,7 @@
@include btn-default;
}
-@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
+@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border, $active-background, $active-border) {
background-color: $background;
color: $text;
border-color: $border;
@@ -23,8 +23,14 @@
&:hover,
&:focus {
background-color: $hover-background;
- color: $hover-text;
border-color: $hover-border;
+ color: $hover-text;
+ }
+
+ &:active {
+ background-color: $active-background;
+ border-color: $active-border;
+ color: $hover-text;
}
}
@@ -82,11 +88,11 @@
}
@mixin btn-gray {
- @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark);
+ @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, $gl-gray-dark);
}
@mixin btn-white {
- @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
+ @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $gl-text-color);
}
@mixin btn-with-margin {
@@ -139,11 +145,11 @@
&.btn-new,
&.btn-create,
&.btn-save {
- @include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
+ @include btn-outline($white-light, $border-green-light, $border-green-light, $green-light, $white-light, $border-green-light, $green-normal, $border-green-normal);
}
&.btn-remove {
- @include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
+ @include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
}
}
@@ -165,11 +171,11 @@
}
&.btn-close {
- @include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
+ @include btn-outline($white-light, $border-orange-light, $border-orange-light, $orange-light, $white-light, $border-orange-light, $orange-normal, $border-orange-normal);
}
&.btn-spam {
- @include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
+ @include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
}
&.btn-danger,
@@ -199,7 +205,7 @@
}
.fa-caret-down,
- .fa-caret-up {
+ .fa-chevron-down {
margin-left: 5px;
}
@@ -351,7 +357,7 @@
.btn-inverted {
&-secondary {
- @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
+ @include btn-outline($white-light, $border-blue-light, $border-blue-light, $blue-light, $white-light, $border-blue-light, $blue-normal, $border-blue-normal);
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 583c17e4a83..6d77aadd753 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -8,6 +8,12 @@
}
}
+@mixin chevron-active {
+ .fa-chevron-down {
+ color: $dropdown-toggle-hover-icon-color;
+ }
+}
+
.open {
.dropdown-menu,
.dropdown-menu-nav {
@@ -19,53 +25,27 @@
}
}
+ .dropdown-toggle,
.dropdown-menu-toggle {
+ @include chevron-active;
border-color: $dropdown-toggle-hover-border-color;
-
- .fa {
- color: $dropdown-toggle-hover-icon-color;
- }
}
}
-.dropdown-menu-toggle {
- position: relative;
- width: 160px;
- padding: 6px 20px 6px 10px;
+.dropdown-toggle {
+ padding: 6px 8px 6px 10px;
background-color: $dropdown-toggle-bg;
color: $dropdown-toggle-color;
font-size: 15px;
text-align: left;
border: 1px solid $border-color;
border-radius: $border-radius-base;
- text-overflow: ellipsis;
white-space: nowrap;
- overflow: hidden;
-
- .fa {
- position: absolute;
- top: 10px;
- right: 8px;
- color: $dropdown-toggle-icon-color;
-
- &.fa-spinner {
- font-size: 16px;
- margin-top: -8px;
- }
- }
&.no-outline {
outline: 0;
}
- &:hover, {
- border-color: $dropdown-toggle-hover-border-color;
-
- .fa {
- color: $dropdown-toggle-hover-icon-color;
- }
- }
-
&.large {
width: 200px;
}
@@ -86,6 +66,51 @@
max-width: 100%;
padding-right: 25px;
}
+
+ .fa {
+ color: $dropdown-toggle-icon-color;
+ }
+
+ .fa-chevron-down {
+ font-size: $dropdown-chevron-size;
+ position: relative;
+ top: -3px;
+ margin-left: 5px;
+ }
+
+ &:hover {
+ @include chevron-active;
+ border-color: $dropdown-toggle-hover-border-color;
+ }
+
+ &:focus:active {
+ @include chevron-active;
+ border-color: $dropdown-toggle-active-border-color;
+ }
+}
+
+.dropdown-menu-toggle {
+ @extend .dropdown-toggle;
+ padding-right: 20px;
+ position: relative;
+ width: 160px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ .fa {
+ position: absolute;
+
+ &.fa-spinner {
+ font-size: 16px;
+ margin-top: -8px;
+ }
+ }
+
+ .fa-chevron-down {
+ position: absolute;
+ top: 11px;
+ right: 8px;
+ }
}
.dropdown-menu,
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index ce864c2de5e..1839ffa0976 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -51,19 +51,26 @@
margin-bottom: -1px;
font-size: 15px;
line-height: 28px;
- color: #959494;
+ color: $note-toolbar-color;
border-bottom: 2px solid transparent;
&:hover,
&:active,
&:focus {
text-decoration: none;
+ border-bottom: 2px solid $gray-darkest;
+ color: $black;
+
+ .badge {
+ color: $black;
+ }
}
}
&.active a {
border-bottom: 2px solid $link-underline-blue;
color: $black;
+ font-weight: 600;
}
.badge {
@@ -85,14 +92,20 @@
li {
+ &.active a {
+ border-bottom: none;
+ color: $link-underline-blue;
+ }
+
a {
margin: 0;
padding: 11px 10px 9px;
- }
- &.active a {
- border-bottom: none;
- color: $link-underline-blue;
+ &:hover,
+ &:active,
+ &:focus {
+ border-bottom: none;
+ }
}
}
}
@@ -310,37 +323,9 @@
height: 51px;
li {
-
a {
padding-top: 10px;
}
-
- a,
- i {
- color: $layout-link-gray;
- }
-
- &.active {
-
- a,
- i {
- color: $black;
- }
-
- svg {
- path,
- polygon {
- fill: $black;
- }
- }
- }
-
- &:hover {
- a,
- i {
- color: $black;
- }
- }
}
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 750d99ebabe..8a9c279d124 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -12,67 +12,71 @@ $sidebar-breakpoint: 1024px;
/*
* Color schema
*/
+$darken-normal-factor: 7%;
+$darken-dark-factor: 10%;
+$darken-border-factor: 5%;
+
$white-light: #fff;
-$white-normal: #ededed;
-$white-dark: #ececec;
+$white-normal: darken($white-light, $darken-normal-factor);
+$white-dark: darken($white-light, $darken-dark-factor);
$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
$gray-lighter: #f9f9f9;
-$gray-normal: #f5f5f5;
-$gray-dark: #ededed;
+$gray-normal: darken($gray-light, $darken-normal-factor);
+$gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee;
$gray-darkest: #c9c9c9;
-$green-light: #38ae67;
-$green-normal: #2faa60;
-$green-dark: #2ca05b;
+$green-light: #3cbd70;
+$green-normal: darken($green-light, $darken-normal-factor);
+$green-dark: darken($green-light, $darken-dark-factor);
$blue-light: #2ea8e5;
-$blue-normal: #2d9fd8;
-$blue-dark: #2897ce;
+$blue-normal: darken($blue-light, $darken-normal-factor);
+$blue-dark: darken($blue-light, $darken-dark-factor);
$blue-medium-light: #3498cb;
-$blue-medium: #2f8ebf;
-$blue-medium-dark: #2d86b4;
+$blue-medium: darken($blue-medium-light, $darken-normal-factor);
+$blue-medium-dark: darken($blue-medium-light, $darken-dark-factor);
$blue-light-transparent: rgba(44, 159, 216, 0.05);
$orange-light: #fc8a51;
-$orange-normal: #e75e40;
-$orange-dark: #ce5237;
+$orange-normal: darken($orange-light, $darken-normal-factor);
+$orange-dark: darken($orange-light, $darken-dark-factor);
$red-light: #e52c5a;
-$red-normal: #d22852;
-$red-dark: darken($red-normal, 5%);
+$red-normal: darken($red-light, $darken-normal-factor);
+$red-dark: darken($red-light, $darken-dark-factor);
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
-$border-white-light: #f1f2f4;
-$border-white-normal: #d6dae2;
-$border-white-dark: #c6cacf;
+$border-white-light: darken($white-light, $darken-border-factor);
+$border-white-normal: darken($white-normal, $darken-border-factor);
+$border-white-dark: darken($white-dark, $darken-border-factor);
-$border-gray-light: #dcdcdc;
-$border-gray-normal: #d7d7d7;
-$border-gray-dark: #c6cacf;
+$border-gray-light: darken($gray-light, $darken-border-factor);
+$border-gray-normal: darken($gray-normal, $darken-border-factor);
+$border-gray-dark: darken($gray-dark, $darken-border-factor);
$border-green-extra-light: #9adb84;
-$border-green-light: #2faa60;
-$border-green-normal: #2ca05b;
-$border-green-dark: #279654;
+$border-green-light: darken($green-light, $darken-border-factor);
+$border-green-normal: darken($green-normal, $darken-border-factor);
+$border-green-dark: darken($green-dark, $darken-border-factor);
-$border-blue-light: #2d9fd8;
-$border-blue-normal: #2897ce;
-$border-blue-dark: #258dc1;
+$border-blue-light: darken($blue-light, $darken-border-factor);
+$border-blue-normal: darken($blue-normal, $darken-border-factor);
+$border-blue-dark: darken($blue-dark, $darken-border-factor);
-$border-orange-light: #fc6d26;
-$border-orange-normal: #ce5237;
-$border-orange-dark: #c14e35;
+$border-orange-light: darken($orange-light, $darken-border-factor);
+$border-orange-normal: darken($orange-normal, $darken-border-factor);
+$border-orange-dark: darken($orange-dark, $darken-border-factor);
-$border-red-light: #d22852;
-$border-red-normal: #ca264f;
-$border-red-dark: darken($border-red-normal, 5%);
+$border-red-light: darken($red-light, $darken-border-factor);
+$border-red-normal: darken($red-normal, $darken-border-factor);
+$border-red-dark: darken($red-dark, $darken-border-factor);
$help-well-bg: $gray-light;
$help-well-border: #e5e5e5;
@@ -216,7 +220,7 @@ $dropdown-bg: #fff;
$dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04);
-$dropdown-border-color: rgba(#000, .1);
+$dropdown-border-color: $border-color;
$dropdown-shadow-color: rgba(#000, .1);
$dropdown-divider-color: rgba(#000, .1);
$dropdown-header-color: #959494;
@@ -225,13 +229,15 @@ $dropdown-input-color: #555;
$dropdown-input-focus-border: $focus-border-color;
$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
$dropdown-loading-bg: rgba(#fff, .6);
+$dropdown-chevron-size: 10px;
$dropdown-toggle-bg: #fff;
-$dropdown-toggle-color: #626262;
-$dropdown-toggle-border-color: #eaeaea;
-$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%);
+$dropdown-toggle-color: #5c5c5c;
+$dropdown-toggle-border-color: #e5e5e5;
+$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 13%);
+$dropdown-toggle-active-border-color: darken($dropdown-toggle-border-color, 14%);
$dropdown-toggle-icon-color: #c4c4c4;
-$dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
+$dropdown-toggle-hover-icon-color: darken($dropdown-toggle-icon-color, 7%);
/*
* Buttons
@@ -255,7 +261,7 @@ $search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 220px;
$location-badge-color: #aaa;
-$location-badge-bg: $gray-normal;
+$location-badge-bg: $dark-background-color;
$location-badge-active-bg: #4f91f8;
$location-icon-color: #e7e9ed;
$location-icon-active-color: #807e7e;
diff --git a/app/assets/stylesheets/mailers/repository_push_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index 8d1a6020ca4..8d1a6020ca4 100644
--- a/app/assets/stylesheets/mailers/repository_push_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 773155fe80a..7aad99eee4e 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -132,7 +132,7 @@
display: none;
}
- .btn-clipboard {
+ .btn-clipboard:hover {
color: $gl-gray;
}
}
@@ -235,6 +235,10 @@
padding-bottom: 10px;
color: #999;
+ &:hover {
+ color: $gl-gray;
+ }
+
span {
display: block;
margin-top: 0;
@@ -244,15 +248,17 @@
display: none;
}
+ .avatar:hover {
+ border-color: #999;
+ }
+
.btn-clipboard {
border: none;
+ color: #999;
&:hover {
background: transparent;
- }
-
- i {
- color: #999;
+ color: $gl-gray;
}
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 19a7a97ea0d..0562ee7b178 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -876,3 +876,11 @@ pre.light-well {
pointer-events: none;
}
}
+
+.variables-table {
+ table-layout: fixed;
+
+ .variable-key {
+ width: 30%;
+ }
+}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index b81842e319b..c2bb8464824 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -112,6 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:koding_enabled,
:koding_url,
:email_author_in_body,
+ :html_emails_enabled,
:repository_checks_enabled,
:metrics_packet_size,
:send_user_confirmation_email,
diff --git a/app/helpers/lfs_helper.rb b/app/controllers/concerns/lfs_request.rb
index 2425c3a8bc8..ed22b1e5470 100644
--- a/app/helpers/lfs_helper.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -1,5 +1,21 @@
-module LfsHelper
- include Gitlab::Routing.url_helpers
+# This concern assumes:
+# - a `#project` accessor
+# - a `#user` accessor
+# - a `#authentication_result` accessor
+# - a `#can?(object, action, subject)` method
+# - a `#ci?` method
+# - a `#download_request?` method
+# - a `#upload_request?` method
+# - a `#has_authentication_ability?(ability)` method
+module LfsRequest
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :require_lfs_enabled!
+ before_action :lfs_check_access!
+ end
+
+ private
def require_lfs_enabled!
return if Gitlab.config.lfs.enabled
@@ -17,35 +33,15 @@ module LfsHelper
return if download_request? && lfs_download_access?
return if upload_request? && lfs_upload_access?
- if project.public? || (user && user.can?(:read_project, project))
- render_lfs_forbidden
+ if project.public? || can?(user, :read_project, project)
+ lfs_forbidden!
else
render_lfs_not_found
end
end
- def lfs_download_access?
- return false unless project.lfs_enabled?
-
- ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
- end
-
- def objects
- @objects ||= (params[:objects] || []).to_a
- end
-
- def user_can_download_code?
- has_authentication_ability?(:download_code) && can?(user, :download_code, project)
- end
-
- def build_can_download_code?
- has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
- end
-
- def lfs_upload_access?
- return false unless project.lfs_enabled?
-
- has_authentication_ability?(:push_code) && can?(user, :push_code, project)
+ def lfs_forbidden!
+ render_lfs_forbidden
end
def render_lfs_forbidden
@@ -70,6 +66,30 @@ module LfsHelper
)
end
+ def lfs_download_access?
+ return false unless project.lfs_enabled?
+
+ ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
+ end
+
+ def lfs_upload_access?
+ return false unless project.lfs_enabled?
+
+ has_authentication_ability?(:push_code) && can?(user, :push_code, project)
+ end
+
+ def lfs_deploy_token?
+ authentication_result.lfs_deploy_token?(project)
+ end
+
+ def user_can_download_code?
+ has_authentication_ability?(:download_code) && can?(user, :download_code, project)
+ end
+
+ def build_can_download_code?
+ has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
+ end
+
def storage_project
@storage_project ||= begin
result = project
@@ -82,4 +102,8 @@ module LfsHelper
result
end
end
+
+ def objects
+ @objects ||= (params[:objects] || []).to_a
+ end
end
diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb
index 3717c49f272..fbf9a026b10 100644
--- a/app/controllers/concerns/toggle_award_emoji.rb
+++ b/app/controllers/concerns/toggle_award_emoji.rb
@@ -1,11 +1,8 @@
module ToggleAwardEmoji
extend ActiveSupport::Concern
- included do
- before_action :authenticate_user!, only: [:toggle_award_emoji]
- end
-
def toggle_award_emoji
+ authenticate_user!
name = params.require(:name)
if awardable.user_can_award?(current_user, name)
diff --git a/app/controllers/concerns/workhorse_request.rb b/app/controllers/concerns/workhorse_request.rb
new file mode 100644
index 00000000000..43c0f1b173c
--- /dev/null
+++ b/app/controllers/concerns/workhorse_request.rb
@@ -0,0 +1,13 @@
+module WorkhorseRequest
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :verify_workhorse_api!
+ end
+
+ private
+
+ def verify_workhorse_api!
+ Gitlab::Workhorse.verify_api_request!(request.headers)
+ end
+end
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 4b3c71874be..a10cdcce72b 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -6,9 +6,9 @@ class HelpController < ApplicationController
def index
@help_index = File.read(Rails.root.join('doc', 'README.md'))
- # Prefix Markdown links with `help/` unless they already have been
- # See http://rubular.com/r/ie2MlpdUMq
- @help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
+ # Prefix Markdown links with `help/` unless they are external links
+ # See http://rubular.com/r/MioSrVLK3S
+ @help_index.gsub!(%r{(\]\()(?!.+://)([^\)\(]+\))}, '\1/help/\2')
end
def show
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index f193adb46b4..daa51ae41df 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -4,7 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
@user.remove_avatar!
@user.save
- @user.reset_events_cache
redirect_to profile_path
end
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index ada7db3c552..53788687076 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -20,7 +20,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.remove_avatar!
@project.save
- @project.reset_events_cache
redirect_to edit_project_path(@project)
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 56ced786311..9940263ae24 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -13,7 +13,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action :assign_blob_vars
before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
- before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff
@@ -39,14 +38,6 @@ class Projects::BlobController < Projects::ApplicationController
def update
@path = params[:file_path] if params[:file_path].present?
- after_edit_path =
- if from_merge_request && @target_branch == @ref
- diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
- "##{hexdigest(@path)}"
- else
- namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
- end
-
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
@@ -124,9 +115,14 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
- def from_merge_request
- # If blob edit was initiated from merge request page
- @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
+ def after_edit_path
+ from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
+ if from_merge_request && @target_branch == @ref
+ diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
+ "##{hexdigest(@path)}"
+ else
+ namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
+ end
end
def editor_variables
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 6b9f37983c4..89d84809e3a 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -36,7 +36,7 @@ class Projects::BranchesController < Projects::ApplicationController
execute(branch_name, ref)
if params[:issue_iid]
- issue = @project.issues.find_by(iid: params[:issue_iid])
+ issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid])
SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue
end
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index fd263960b93..ac639ef015b 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -6,7 +6,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
before_action :authorize_read_cycle_analytics!
def show
- @cycle_analytics = ::CycleAnalytics.new(@project, from: start_date(cycle_analytics_params))
+ @cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params))
stats_values, cycle_analytics_json = generate_cycle_analytics_data
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 3f41916e6d3..8714349e27f 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -18,6 +18,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
private
+ def download_request?
+ raise NotImplementedError
+ end
+
+ def upload_request?
+ raise NotImplementedError
+ end
+
def authenticate_user
@authentication_result = Gitlab::Auth::Result.new
@@ -130,10 +138,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
authentication_result.ci?(project)
end
- def lfs_deploy_token?
- authentication_result.lfs_deploy_token?(project)
- end
-
def authentication_has_download_access?
has_authentication_ability?(:download_code) || has_authentication_ability?(:build_download_code)
end
@@ -149,8 +153,4 @@ class Projects::GitHttpClientController < Projects::ApplicationController
def authentication_project
authentication_result.project
end
-
- def verify_workhorse_api!
- Gitlab::Workhorse.verify_api_request!(request.headers)
- end
end
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 13caeb42d40..9184dcccac5 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -1,7 +1,5 @@
-# This file should be identical in GitLab Community Edition and Enterprise Edition
-
class Projects::GitHttpController < Projects::GitHttpClientController
- before_action :verify_workhorse_api!
+ include WorkhorseRequest
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
@@ -67,14 +65,18 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def render_denied
- if user && user.can?(:read_project, project)
- render plain: 'Access denied', status: :forbidden
+ if user && can?(user, :read_project, project)
+ render plain: access_denied_message, status: :forbidden
else
# Do not leak information about project existence
render_not_found
end
end
+ def access_denied_message
+ 'Access denied'
+ end
+
def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
index 2d493276941..440259b643c 100644
--- a/app/controllers/projects/lfs_api_controller.rb
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -1,8 +1,7 @@
class Projects::LfsApiController < Projects::GitHttpClientController
- include LfsHelper
+ include LfsRequest
- before_action :require_lfs_enabled!
- before_action :lfs_check_access!, except: [:deprecated]
+ skip_before_action :lfs_check_access!, only: [:deprecated]
def batch
unless objects.present?
@@ -31,6 +30,14 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private
+ def download_request?
+ params[:operation] == 'download'
+ end
+
+ def upload_request?
+ params[:operation] == 'upload'
+ end
+
def existing_oids
@existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
@@ -79,12 +86,4 @@ class Projects::LfsApiController < Projects::GitHttpClientController
}
}
end
-
- def download_request?
- params[:operation] == 'download'
- end
-
- def upload_request?
- params[:operation] == 'upload'
- end
end
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
index 9005b104e90..32759672b6c 100644
--- a/app/controllers/projects/lfs_storage_controller.rb
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -1,9 +1,8 @@
class Projects::LfsStorageController < Projects::GitHttpClientController
- include LfsHelper
+ include LfsRequest
+ include WorkhorseRequest
- before_action :require_lfs_enabled!
- before_action :lfs_check_access!
- before_action :verify_workhorse_api!, only: [:upload_authorize]
+ skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize]
def download
lfs_object = LfsObject.find_by_oid(oid)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 707cc018a12..c7cde9e6a14 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -329,17 +329,18 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present?
- unless @merge_request.pipeline
+ unless @merge_request.head_pipeline
@status = :failed
return
end
- if @merge_request.pipeline.active?
+ if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenPipelineSucceedsService
.new(@project, current_user, merge_params)
.execute(@merge_request)
+
@status = :merge_when_build_succeeds
- elsif @merge_request.pipeline.success?
+ elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
@@ -403,7 +404,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def ci_status
- pipeline = @merge_request.pipeline
+ pipeline = @merge_request.head_pipeline
+
if pipeline
status = pipeline.status
coverage = pipeline.try(:coverage)
@@ -539,7 +541,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_widget_vars
- @pipeline = @merge_request.pipeline
+ @pipeline = @merge_request.head_pipeline
end
def define_commit_vars
@@ -568,8 +570,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars
@pipelines = @merge_request.all_pipelines
- @pipeline = @merge_request.pipeline
- @statuses = @pipeline.statuses.relevant if @pipeline.present?
+ @pipeline = @merge_request.head_pipeline
+ @statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
end
def define_new_vars
@@ -636,7 +638,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? &&
- @merge_request.pipeline && @merge_request.pipeline.active?
+ @merge_request.head_pipeline && @merge_request.head_pipeline.active?
end
def build_merge_request
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index 5685d0f4e7c..52517381c65 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -16,13 +16,7 @@ class Projects::TodosController < Projects::ApplicationController
@issuable ||= begin
case params[:issuable_type]
when "issue"
- issue = @project.issues.find(params[:issuable_id])
-
- if can?(current_user, :read_issue, issue)
- issue
- else
- render_404
- end
+ IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
when "merge_request"
@project.merge_requests.find(params[:issuable_id])
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 6297b2db369..9a74e36870b 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -16,14 +16,12 @@
# label_name: string
# sort: string
#
-require_relative 'projects_finder'
-
class IssuableFinder
NONE = '0'
attr_accessor :current_user, :params
- def initialize(current_user, params)
+ def initialize(current_user, params = {})
@current_user = current_user
@params = params
end
@@ -43,6 +41,14 @@ class IssuableFinder
sort(items)
end
+ def find(*params)
+ execute.find(*params)
+ end
+
+ def find_by(*params)
+ execute.find_by(*params)
+ end
+
def group
return @group if defined?(@group)
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 0b7832e6583..a653a6d59c6 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -12,7 +12,7 @@ class NotesFinder
when "commit"
project.notes.for_commit_id(target_id).non_diff_notes
when "issue"
- project.issues.visible_to_user(current_user).find(target_id).notes.inc_author
+ IssuesFinder.new(current_user, project_id: project.id).find(target_id).notes.inc_author
when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
when "snippet", "project_snippet"
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index cbab1fd5967..81e0b6bb5ae 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -43,7 +43,7 @@ module DropdownsHelper
default_label = data_attr[:default_label]
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
- output << icon('caret-down')
+ output << icon('chevron-down')
output.html_safe
end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 75cd9eece5c..19ab059aea6 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -8,11 +8,7 @@ module GroupsHelper
group = Group.find_by(path: group)
end
- if group && group.avatar.present?
- group.avatar.url
- else
- image_path('no_group_avatar.png')
- end
+ group.try(:avatar_url) || image_path('no_group_avatar.png')
end
def group_title(group, name = nil, url = nil)
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 8bebda07787..6584aa3edd5 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -143,6 +143,20 @@ module IssuablesHelper
end
end
+ def issuable_filter_params
+ [
+ :search,
+ :author_id,
+ :assignee_id,
+ :milestone_title,
+ :label_name
+ ]
+ end
+
+ def issuable_filter_present?
+ issuable_filter_params.any? { |k| params.key?(k) }
+ end
+
private
def assigned_issuables_count(assignee, issuable_type, state)
@@ -165,13 +179,9 @@ module IssuablesHelper
end
end
- def issuable_filters_present
- params[:search] || params[:author_id] || params[:assignee_id] || params[:milestone_title] || params[:label_name]
- end
-
def issuables_count_for_state(issuable_type, state)
issuables_finder = public_send("#{issuable_type}_finder")
-
+
params = issuables_finder.params.merge(state: state)
finder = issuables_finder.class.new(issuables_finder.current_user, params)
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index 96116e916dd..0d20c9092c4 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -4,6 +4,7 @@ module Emails
setup_note_mail(note_id, recipient_id)
@commit = @note.noteable
+ @discussion = @note.to_discussion if @note.diff_note?
@target_url = namespace_project_commit_url(*note_target_url_options)
mail_answer_thread(@commit,
@@ -24,6 +25,7 @@ module Emails
setup_note_mail(note_id, recipient_id)
@merge_request = @note.noteable
+ @discussion = @note.to_discussion if @note.diff_note?
@target_url = namespace_project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 4294a10e9e3..fabbf97d4db 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -317,7 +317,7 @@ module Ci
def merge_requests
@merge_requests ||= project.merge_requests
.where(source_branch: self.ref)
- .select { |merge_request| merge_request.pipeline.try(:id) == self.id }
+ .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end
private
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index 7fd0905ee81..9dd4d9c6f24 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -2,6 +2,9 @@ module ProtectedBranchAccess
extend ActiveSupport::Concern
included do
+ belongs_to :protected_branch
+ delegate :project, to: :protected_branch
+
scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
end
@@ -9,4 +12,10 @@ module ProtectedBranchAccess
def humanize
self.class.human_access_levels[self.access_level]
end
+
+ def check_access(user)
+ return true if user.is_admin?
+
+ project.team.max_member_access(user.id) >= access_level
+ end
end
diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb
index cb8e088d21d..ba4ee6fcf9d 100644
--- a/app/models/cycle_analytics.rb
+++ b/app/models/cycle_analytics.rb
@@ -1,14 +1,15 @@
class CycleAnalytics
STAGES = %i[issue plan code test review staging production].freeze
- def initialize(project, from:)
+ def initialize(project, current_user, from:)
@project = project
+ @current_user = current_user
@from = from
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil)
end
def summary
- @summary ||= Summary.new(@project, from: @from)
+ @summary ||= Summary.new(@project, @current_user, from: @from)
end
def permissions(user:)
diff --git a/app/models/cycle_analytics/summary.rb b/app/models/cycle_analytics/summary.rb
index b46db449bf3..82f53d17ddd 100644
--- a/app/models/cycle_analytics/summary.rb
+++ b/app/models/cycle_analytics/summary.rb
@@ -1,12 +1,13 @@
class CycleAnalytics
class Summary
- def initialize(project, from:)
+ def initialize(project, current_user, from:)
@project = project
+ @current_user = current_user
@from = from
end
def new_issues
- @project.issues.created_after(@from).count
+ IssuesFinder.new(@current_user, project_id: @project.id).execute.created_after(@from).count
end
def commits
diff --git a/app/models/discussion.rb b/app/models/discussion.rb
index de06c13481a..75a85563235 100644
--- a/app/models/discussion.rb
+++ b/app/models/discussion.rb
@@ -25,7 +25,12 @@ class Discussion
to: :last_resolved_note,
allow_nil: true
- delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true
+ delegate :blob,
+ :highlighted_diff_lines,
+ :diff_lines,
+
+ to: :diff_file,
+ allow_nil: true
def self.for_notes(notes)
notes.group_by(&:discussion_id).values.map { |notes| new(notes) }
@@ -159,10 +164,11 @@ class Discussion
end
# Returns an array of at most 16 highlighted lines above a diff note
- def truncated_diff_lines
+ def truncated_diff_lines(highlight: true)
+ lines = highlight ? highlighted_diff_lines : diff_lines
prev_lines = []
- highlighted_diff_lines.each do |line|
+ lines.each do |line|
if line.meta?
prev_lines.clear
else
diff --git a/app/models/event.rb b/app/models/event.rb
index 21eaca917b8..2662f170765 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -43,12 +43,6 @@ class Event < ActiveRecord::Base
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
class << self
- def reset_event_cache_for(target)
- Event.where(target_id: target.id, target_type: target.class.to_s).
- order('id DESC').limit(100).
- update_all(updated_at: Time.now)
- end
-
# Update Gitlab::ContributionsCalendar#activity_dates if this changes
def contributions
where("action = ? OR (target_type in (?) AND action in (?))",
@@ -353,6 +347,10 @@ class Event < ActiveRecord::Base
update_all(last_activity_at: created_at)
end
+ def authored_by?(user)
+ user ? author_id == user.id : false
+ end
+
private
def recent_update?
diff --git a/app/models/issue.rb b/app/models/issue.rb
index dd0cb75f9a8..fbf07040301 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -182,18 +182,6 @@ class Issue < ActiveRecord::Base
branches_with_iid - branches_with_merge_request
end
- # Reset issue events cache
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when an issue is updated
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.reset_event_cache_for(self)
- end
-
# To allow polymorphism with MergeRequest.
def source_project
project
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index fdf54cc8a7e..64990f8134e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -605,18 +605,6 @@ class MergeRequest < ActiveRecord::Base
self.target_project.repository.branch_names.include?(self.target_branch)
end
- # Reset merge request events cache
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when a merge request is updated
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.reset_event_cache_for(self)
- end
-
def merge_commit_message
message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n"
message << "#{title}\n\n"
@@ -690,7 +678,7 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
- !pipeline || pipeline.success? || pipeline.skipped?
+ !head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end
def environments
@@ -786,14 +774,14 @@ class MergeRequest < ActiveRecord::Base
commits.map(&:sha)
end
- def pipeline
+ def head_pipeline
return unless diff_head_sha && source_project
- @pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
+ @head_pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
end
def all_pipelines
- return unless source_project
+ return Ci::Pipeline.none unless source_project
@all_pipelines ||= source_project.pipelines
.where(sha: all_commits_sha, ref: source_branch)
diff --git a/app/models/note.rb b/app/models/note.rb
index ed4224e3046..5b50ca285c3 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -201,19 +201,6 @@ class Note < ActiveRecord::Base
super(noteable_type.to_s.classify.constantize.base_class.to_s)
end
- # Reset notes events cache
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when a note is updated
- # * when a note is removed
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.reset_event_cache_for(self)
- end
-
def editable?
!system?
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 9256e9ddd95..f01cb613b85 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -687,9 +687,9 @@ class Project < ActiveRecord::Base
self.id
end
- def get_issue(issue_id)
+ def get_issue(issue_id, current_user)
if default_issues_tracker?
- issues.find_by(iid: issue_id)
+ IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id)
else
ExternalIssue.new(issue_id, self)
end
@@ -976,7 +976,6 @@ class Project < ActiveRecord::Base
begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace)
- reset_events_cache
@old_path_with_namespace = old_path_with_namespace
@@ -1043,22 +1042,6 @@ class Project < ActiveRecord::Base
attrs
end
- # Reset events cache related to this project
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when project was moved
- # * when project was renamed
- # * when the project avatar changes
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.where(project_id: self.id).
- order('id DESC').limit(100).
- update_all(updated_at: Time.now)
- end
-
def project_member(user)
project_members.find_by(user_id: user)
end
diff --git a/app/models/protected_branch/merge_access_level.rb b/app/models/protected_branch/merge_access_level.rb
index 806b3ccd275..771e3376613 100644
--- a/app/models/protected_branch/merge_access_level.rb
+++ b/app/models/protected_branch/merge_access_level.rb
@@ -1,9 +1,6 @@
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
- belongs_to :protected_branch
- delegate :project, to: :protected_branch
-
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER] }
@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
Gitlab::Access::DEVELOPER => "Developers + Masters"
}.with_indifferent_access
end
-
- def check_access(user)
- return true if user.is_admin?
-
- project.team.max_member_access(user.id) >= access_level
- end
end
diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb
index 92e9c51d883..14610cb42b7 100644
--- a/app/models/protected_branch/push_access_level.rb
+++ b/app/models/protected_branch/push_access_level.rb
@@ -1,9 +1,6 @@
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
- belongs_to :protected_branch
- delegate :project, to: :protected_branch
-
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS] }
@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
def check_access(user)
return false if access_level == Gitlab::Access::NO_ACCESS
- return true if user.is_admin?
- project.team.max_member_access(user.id) >= access_level
+ super
end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index bf136ccdb6c..e2e7d08abac 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -196,18 +196,12 @@ class Repository
options = { message: message, tagger: user_to_committer(user) } if message
- rugged.tags.create(tag_name, target, options)
- tag = find_tag(tag_name)
-
- GitHooksService.new.execute(user, path_to_repo, oldrev, tag.target, ref) do
- # we already created a tag, because we need tag SHA to pass correct
- # values to hooks
+ GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do |service|
+ raw_tag = rugged.tags.create(tag_name, target, options)
+ service.newrev = raw_tag.target_id
end
- tag
- rescue GitHooksService::PreReceiveError
- rugged.tags.delete(tag_name)
- raise
+ find_tag(tag_name)
end
def rm_branch(user, branch_name)
diff --git a/app/models/user.rb b/app/models/user.rb
index 513a19d81d2..b54ce14f0bf 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -445,27 +445,21 @@ class User < ActiveRecord::Base
end
def refresh_authorized_projects
- loop do
- begin
- Gitlab::Database.serialized_transaction do
- project_authorizations.delete_all
-
- # project_authorizations_union can return multiple records for the same project/user with
- # different access_level so we take row with the maximum access_level
- project_authorizations.connection.execute <<-SQL
- INSERT INTO project_authorizations (user_id, project_id, access_level)
- SELECT user_id, project_id, MAX(access_level) AS access_level
- FROM (#{project_authorizations_union.to_sql}) sub
- GROUP BY user_id, project_id
- SQL
-
- update_column(:authorized_projects_populated, true) unless authorized_projects_populated
- end
-
- break
- # In the event of a concurrent modification Rails raises StatementInvalid.
- # In this case we want to keep retrying until the transaction succeeds
- rescue ActiveRecord::StatementInvalid
+ transaction do
+ project_authorizations.delete_all
+
+ # project_authorizations_union can return multiple records for the same
+ # project/user with different access_level so we take row with the maximum
+ # access_level
+ project_authorizations.connection.execute <<-SQL
+ INSERT INTO project_authorizations (user_id, project_id, access_level)
+ SELECT user_id, project_id, MAX(access_level) AS access_level
+ FROM (#{project_authorizations_union.to_sql}) sub
+ GROUP BY user_id, project_id
+ SQL
+
+ unless authorized_projects_populated
+ update_column(:authorized_projects_populated, true)
end
end
end
@@ -708,20 +702,6 @@ class User < ActiveRecord::Base
project.project_member(self)
end
- # Reset project events cache related to this user
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when the user changes their avatar
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.where(author_id: id).
- order('id DESC').limit(1000).
- update_all(updated_at: Time.now)
- end
-
def full_website_url
return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb
index abefcd5cc02..a0db5b8f0f4 100644
--- a/app/serializers/analytics_build_entity.rb
+++ b/app/serializers/analytics_build_entity.rb
@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
end
expose :duration, as: :total_time do |build|
- distance_of_time_as_hash(build.duration.to_f)
+ build.duration ? distance_of_time_as_hash(build.duration.to_f) : {}
end
expose :branch do
diff --git a/app/serializers/build_entity.rb b/app/serializers/build_entity.rb
index cf1c418a88e..b5384e6462b 100644
--- a/app/serializers/build_entity.rb
+++ b/app/serializers/build_entity.rb
@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity
path_to(:play_namespace_project_build, build)
end
+ expose :created_at
+ expose :updated_at
+
private
def path_to(route, build)
diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb
index 918abba8d99..9607ad55a8b 100644
--- a/app/serializers/entity_date_helper.rb
+++ b/app/serializers/entity_date_helper.rb
@@ -2,6 +2,8 @@ module EntityDateHelper
include ActionView::Helpers::DateHelper
def interval_in_words(diff)
+ return 'Not started' unless diff
+
"#{distance_of_time_in_words(Time.now, diff)} ago"
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index cde856b0186..e3bc9847200 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -45,9 +45,15 @@ module Ci
return error('No builds for this pipeline.')
end
- pipeline.save
- pipeline.process!
- pipeline
+ Ci::Pipeline.transaction do
+ pipeline.save
+
+ Ci::CreatePipelineBuildsService
+ .new(project, current_user)
+ .execute(pipeline)
+ end
+
+ pipeline.tap(&:process!)
end
private
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index 8face432d97..2e028c44d8b 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -5,10 +5,7 @@ module Ci
def execute(pipeline)
@pipeline = pipeline
- # This method will ensure that our pipeline does have all builds for all stages created
- if created_builds.empty?
- create_builds!
- end
+ ensure_created_builds! # TODO, remove me in 9.0
new_builds =
stage_indexes_of_created_builds.map do |index|
@@ -22,10 +19,6 @@ module Ci
private
- def create_builds!
- Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
- end
-
def process_stage(index)
current_status = status_for_prior_stages(index)
@@ -76,5 +69,18 @@ module Ci
def created_builds
pipeline.builds.created
end
+
+ # This method is DEPRECATED and should be removed in 9.0.
+ #
+ # We need it to maintain backwards compatibility with previous versions
+ # when builds were not created within one transaction with the pipeline.
+ #
+ def ensure_created_builds!
+ return if created_builds.any?
+
+ Ci::CreatePipelineBuildsService
+ .new(project, current_user)
+ .execute(pipeline)
+ end
end
end
diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb
index 172bd85dade..6cd3908d43a 100644
--- a/app/services/git_hooks_service.rb
+++ b/app/services/git_hooks_service.rb
@@ -1,6 +1,8 @@
class GitHooksService
PreReceiveError = Class.new(StandardError)
+ attr_accessor :oldrev, :newrev, :ref
+
def execute(user, repo_path, oldrev, newrev, ref)
@repo_path = repo_path
@user = Gitlab::GlId.gl_id(user)
@@ -16,7 +18,7 @@ class GitHooksService
end
end
- yield
+ yield self
run_hook('post-receive')
end
@@ -25,6 +27,6 @@ class GitHooksService
def run_hook(name)
hook = Gitlab::Git::Hook.new(name, @repo_path)
- hook.trigger(@user, @oldrev, @newrev, @ref)
+ hook.trigger(@user, oldrev, newrev, ref)
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 575795788de..ce68e433ab8 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -85,14 +85,15 @@ class IssuableBaseService < BaseService
def find_or_create_label_ids
labels = params.delete(:labels)
+
return unless labels
- params[:label_ids] = labels.split(',').map do |label_name|
+ params[:label_ids] = labels.split(",").map do |label_name|
service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip)
label = service.execute
- label.id
- end
+ label.try(:id)
+ end.compact
end
def process_label_ids(attributes, existing_label_ids: nil)
@@ -140,6 +141,7 @@ class IssuableBaseService < BaseService
params.delete(:state_event)
params[:author] ||= current_user
+
label_ids = process_label_ids(params)
issuable.assign_attributes(params)
@@ -184,8 +186,6 @@ class IssuableBaseService < BaseService
params[:label_ids] = process_label_ids(params, existing_label_ids: issuable.label_ids)
if params.present? && update_issuable(issuable, params)
- issuable.reset_events_cache
-
# We do not touch as it will affect a update on updated_at field
ActiveRecord::Base.no_touching do
handle_common_system_notes(issuable, old_labels: old_labels)
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index d622f9edd33..cf4f7606c94 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -22,9 +22,14 @@ module Labels
).execute(skip_authorization: skip_authorization)
end
+ # Only creates the label if current_user can do so, if the label does not exist
+ # and the user can not create the label, nil is returned
def find_or_create_label
new_label = available_labels.find_by(title: title)
- new_label ||= project.labels.create(params)
+
+ if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project))
+ new_label = project.labels.create(params)
+ end
new_label
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 58f69a41e14..800fd39c424 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -55,7 +55,7 @@ module MergeRequests
def pipeline_merge_requests(pipeline)
merge_requests_for(pipeline.ref).each do |merge_request|
- next unless pipeline == merge_request.pipeline
+ next unless pipeline == merge_request.head_pipeline
yield merge_request
end
@@ -63,7 +63,7 @@ module MergeRequests
def commit_status_merge_requests(commit_status)
merge_requests_for(commit_status.ref).each do |merge_request|
- pipeline = merge_request.pipeline
+ pipeline = merge_request.head_pipeline
next unless pipeline
next unless pipeline.sha == commit_status.sha
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index dd0d738674e..bebfca7537b 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -81,7 +81,7 @@ module MergeRequests
commit = commits.first
merge_request.title = commit.title
merge_request.description ||= commit.description.try(:strip)
- elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?)
+ elsif iid && issue = merge_request.target_project.get_issue(iid, current_user)
case issue
when Issue
merge_request.title = "Resolve \"#{issue.title}\""
diff --git a/app/services/notes/delete_service.rb b/app/services/notes/delete_service.rb
index 7f1b30ec84e..a673e8e9dde 100644
--- a/app/services/notes/delete_service.rb
+++ b/app/services/notes/delete_service.rb
@@ -2,7 +2,6 @@ module Notes
class DeleteService < BaseService
def execute(note)
note.destroy
- note.reset_events_cache
end
end
end
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 1361b1e0300..75a4b3ed826 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -5,7 +5,6 @@ module Notes
note.update_attributes(params.merge(updated_by: current_user))
note.create_new_cross_references!(current_user)
- note.reset_events_cache
if note.previous_changes.include?('note')
TodoService.new.update_note(note, current_user)
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 28470f59807..34ec575e808 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -61,9 +61,6 @@ module Projects
# Move missing group labels to project
Labels::TransferService.new(current_user, old_group, project).execute
- # clear project cached events
- project.reset_events_cache
-
# Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 71ff14a3f20..38683fdf6d7 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -3,16 +3,10 @@ class AvatarUploader < CarrierWave::Uploader::Base
storage :file
- after :store, :reset_events_cache
-
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
- def reset_events_cache(file)
- model.reset_events_cache if model.is_a?(User)
- end
-
def exists?
model.avatar.file && model.avatar.file.exists?
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index ce803f329f9..7accd2529af 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -443,7 +443,16 @@
Some email servers do not support overriding the email sender name.
Enable this option to include the name of the author of the issue,
merge request or comment in the email body instead.
-
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :html_emails_enabled do
+ = f.check_box :html_emails_enabled
+ Enable HTML emails
+ .help-block
+ By default GitLab sends emails in HTML and plain text formats so mail
+ clients can choose what format to use. Disable this option if you only
+ want to send emails in plain text format.
%fieldset
%legend Automatic Git repository housekeeping
.form-group
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index f7abad54286..48b0fd504f4 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -4,13 +4,13 @@
%ul.nav-links
= nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
- Your Projects
+ Your projects
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
- Starred Projects
+ Starred projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
- Explore Projects
+ Explore projects
.nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
index fdea834ff45..4a55aac0df6 100644
--- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
@@ -4,6 +4,18 @@
Welcome to GitLab
%p.blank-state-text
Code, test, and deploy together
+
+- if current_user.can_create_group?
+ .blank-state
+ .blank-state-icon
+ = custom_icon("group", size: 50)
+ %h3.blank-state-title
+ You can create a group for several dependent projects.
+ %p.blank-state-text
+ Groups are the best way to manage projects and members.
+ = link_to new_group_path, class: "btn btn-new" do
+ New group
+
.blank-state
.blank-state-icon
= custom_icon("project", size: 50)
@@ -21,17 +33,6 @@
= link_to new_project_path, class: "btn btn-new" do
New project
-- if current_user.can_create_group?
- .blank-state
- .blank-state-icon
- = custom_icon("group", size: 50)
- %h3.blank-state-title
- You can create a group for several dependent projects.
- %p.blank-state-text
- Groups are the best way to manage projects and members.
- = link_to new_group_path, class: "btn btn-new" do
- New group
-
-if publicish_project_count > 0
.blank-state
.blank-state-icon
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 472d698486b..62f52086be4 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -50,13 +50,13 @@
data: { data: todo_actions_options }})
.pull-right
.dropdown.inline.prepend-left-10
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
%li
= link_to todos_filter_path(sort: sort_value_priority) do
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 5c318cd3b8b..a0bd14df209 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -3,14 +3,13 @@
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
- = cache [event, current_application_settings, "v2.2"] do
- = author_avatar(event, size: 40)
+ = author_avatar(event, size: 40)
- - if event.created_project?
- = render "events/event/created_project", event: event
- - elsif event.push?
- = render "events/event/push", event: event
- - elsif event.commented?
- = render "events/event/note", event: event
- - else
- = render "events/event/common", event: event
+ - if event.created_project?
+ = render "events/event/created_project", event: event
+ - elsif event.push?
+ = render "events/event/push", event: event
+ - elsif event.commented?
+ = render "events/event/note", event: event
+ - else
+ = render "events/event/common", event: event
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 44fff49d99c..64ca3c32e01 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -18,7 +18,7 @@
- few_commits.each do |commit|
= render "events/commit", commit: commit, project: project, event: event
- - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project)
+ - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) && event.authored_by?(current_user)
- if event.commits_count > 1
%li.commits-stat
- if event.commits_count > 2
@@ -35,12 +35,12 @@
Compare #{from_label}...#{truncate_sha(event.commit_to)}
- if create_mr
- %span{"data-user-is" => event.author_id, "data-display" => "inline"}
+ %span
or
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
create a merge request
- elsif create_mr
- %li.commits-stat{"data-user-is" => event.author_id}
+ %li.commits-stat
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
Create Merge Request
- elsif event.rm_ref?
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index a1b39d9e1a0..4e5d965ccbe 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -17,13 +17,13 @@
.pull-right
.dropdown.inline
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to explore_groups_path(sort: sort_value_recently_created) do
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 4cff14b096b..5ea154c36b4 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,13 +1,13 @@
- if current_user
.dropdown
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('globe')
%span.light Visibility:
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
Any
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_projects_path(visibility_level: nil) do
@@ -20,14 +20,14 @@
- if @tags.present?
.dropdown
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('tags')
%span.light Tags:
- if params[:tag].present?
= params[:tag]
- else
Any
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_projects_path(tag: nil) do
diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml
index 2fd4859c1c6..882fdf1317d 100644
--- a/app/views/invites/show.html.haml
+++ b/app/views/invites/show.html.haml
@@ -6,7 +6,7 @@
- if inviter = @member.created_by
by
= link_to inviter.name, user_url(inviter)
- to join
+ to join
- case @member.source
- when Project
- project = @member.source
@@ -20,11 +20,18 @@
= link_to group.name, group_url(group)
as #{@member.human_access}.
-- if @member.source.users.include?(current_user)
+- is_member = @member.source.users.include?(current_user)
+
+- if is_member
%p
However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}.
Sign in using a different account to accept the invitation.
-- else
+
+- if @member.invite_email != current_user.email
+ %p
+ Note that this invitation was sent to #{mail_to @member.invite_email}, but you are signed in as #{link_to current_user.to_reference, user_url(current_user)} with email #{mail_to current_user.email}.
+
+- unless is_member
.actions
= link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success"
= link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10"
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 757de92d6d4..3e488cf73b9 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -56,5 +56,3 @@
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/bootlint' if Rails.env.development?
-
- = render 'layouts/user_styles'
diff --git a/app/views/layouts/_user_styles.html.haml b/app/views/layouts/_user_styles.html.haml
deleted file mode 100644
index b76b3cb5510..00000000000
--- a/app/views/layouts/_user_styles.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-:css
- [data-user-is] {
- display: none !important;
- }
-
- [data-user-is="#{current_user.try(:id)}"] {
- display: block !important;
- }
-
- [data-user-is="#{current_user.try(:id)}"][data-display="inline"] {
- display: inline !important;
- }
-
- [data-user-is-not] {
- display: block !important;
- }
-
- [data-user-is-not][data-display="inline"] {
- display: inline !important;
- }
-
- [data-user-is-not="#{current_user.try(:id)}"] {
- display: none !important;
- }
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 99a58bbb676..701bcd3ab71 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -70,7 +70,7 @@
%span
Issues
- if @project.default_issues_tracker?
- %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count)
+ %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
diff --git a/app/views/notify/_note_message.text.erb b/app/views/notify/_note_message.text.erb
new file mode 100644
index 00000000000..f82cbc9a3fc
--- /dev/null
+++ b/app/views/notify/_note_message.text.erb
@@ -0,0 +1,5 @@
+<% if current_application_settings.email_author_in_body %>
+ <%= @note.author_name %> wrote:
+<% end -%>
+
+<%= @note.note %>
diff --git a/app/views/notify/_note_mr_or_commit_email.html.haml b/app/views/notify/_note_mr_or_commit_email.html.haml
new file mode 100644
index 00000000000..edf8dfe7e9e
--- /dev/null
+++ b/app/views/notify/_note_mr_or_commit_email.html.haml
@@ -0,0 +1,18 @@
+= content_for :head do
+ = stylesheet_link_tag 'mailers/highlighted_diff_email'
+
+New comment
+
+- if @discussion && @discussion.diff_file
+ on
+ = link_to @note.diff_file.file_path, @target_url, class: 'details'
+ \:
+ %table
+ = render partial: "projects/diffs/line",
+ collection: @discussion.truncated_diff_lines,
+ as: :line,
+ locals: { diff_file: @note.diff_file,
+ plain: true,
+ email: true }
+
+= render 'note_message'
diff --git a/app/views/notify/_note_mr_or_commit_email.text.erb b/app/views/notify/_note_mr_or_commit_email.text.erb
new file mode 100644
index 00000000000..b4fcdf6b1e9
--- /dev/null
+++ b/app/views/notify/_note_mr_or_commit_email.text.erb
@@ -0,0 +1,8 @@
+<% if @discussion && @discussion.diff_file -%>
+ on <%= @note.diff_file.file_path -%>
+<% end -%>:
+
+<%= url %>
+
+<%= render 'simple_diff' if @discussion -%>
+<%= render 'note_message' %>
diff --git a/app/views/notify/_simple_diff.text.erb b/app/views/notify/_simple_diff.text.erb
new file mode 100644
index 00000000000..c28d1cc34d3
--- /dev/null
+++ b/app/views/notify/_simple_diff.text.erb
@@ -0,0 +1,3 @@
+<% @discussion.truncated_diff_lines(highlight: false).each do |line| %>
+> <%= line.text %>
+<% end %>
diff --git a/app/views/notify/note_commit_email.html.haml b/app/views/notify/note_commit_email.html.haml
index 1d961e4424c..0a650e3b2ca 100644
--- a/app/views/notify/note_commit_email.html.haml
+++ b/app/views/notify/note_commit_email.html.haml
@@ -1,2 +1,2 @@
-= render 'note_message'
-
+%p.details
+ = render 'note_mr_or_commit_email'
diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb
index aaeaf5fdf73..6aa085a172e 100644
--- a/app/views/notify/note_commit_email.text.erb
+++ b/app/views/notify/note_commit_email.text.erb
@@ -1,9 +1,2 @@
-New comment for Commit <%= @commit.short_id %>
-
-<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
-
-
-Author: <%= @note.author_name %>
-
-<%= @note.note %>
-
+New comment for Commit <%= @commit.short_id -%>
+<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url } %>
diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml
index ea7e3d199fd..0a650e3b2ca 100644
--- a/app/views/notify/note_merge_request_email.html.haml
+++ b/app/views/notify/note_merge_request_email.html.haml
@@ -1,7 +1,2 @@
-- if @note.diff_note? && @note.diff_file
- %p.details
- New comment on diff for
- = link_to @note.diff_file.file_path, @target_url
- \:
-
-= render 'note_message'
+%p.details
+ = render 'note_mr_or_commit_email'
diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb
index 8cdab63829e..2ce64c494cf 100644
--- a/app/views/notify/note_merge_request_email.text.erb
+++ b/app/views/notify/note_merge_request_email.text.erb
@@ -1,9 +1,2 @@
-New comment for Merge Request <%= @merge_request.to_reference %>
-
-<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
-
-
-<%= @note.author_name %>
-
-<%= @note.note %>
-
+New comment for Merge Request <%= @merge_request.to_reference -%>
+<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url }%>
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index 307c5a11206..25883de257c 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -1,5 +1,5 @@
= content_for :head do
- = stylesheet_link_tag 'mailers/repository_push_email'
+ = stylesheet_link_tag 'mailers/highlighted_diff_email'
%h3
#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name}
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 2a0352a71b7..a5dcd93f42e 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -27,5 +27,5 @@
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit_sha', @last_commit_sha
= hidden_field_tag 'content', '', id: "file-content"
- = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
+ = hidden_field_tag 'from_merge_request_iid', params[:from_merge_request_iid]
= render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 2246316b540..5fd664c7a93 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -12,10 +12,10 @@
= 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
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
= projects_sort_options_hash[@sort]
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_branches_path(sort: sort_value_name) do
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index f5562046953..d5004f6a066 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -116,7 +116,7 @@
.title Stage
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.stage-selection More
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu
- @build.pipeline.stages.each do |stage|
%li
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 120ba9ffcd2..6c33d80becd 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -9,7 +9,7 @@
= icon('comment')
\
- if editable_diff?(diff_file)
- - link_opts = @merge_request.id ? { from_merge_request_id: @merge_request.id } : {}
+ - link_opts = @merge_request.persisted? ? { from_merge_request_iid: @merge_request.iid } : {}
= edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
blob: blob, link_opts: link_opts)
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
index a3e4b5b777e..16c96b66714 100644
--- a/app/views/projects/diffs/_line.html.haml
+++ b/app/views/projects/diffs/_line.html.haml
@@ -25,7 +25,7 @@
%a{href: "##{line_code}", data: { linenumber: link_text }}
%td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, diff_file.position(line), type) unless plain) }<
- if email
- %pre= diff_line_content(line.text)
+ %pre= line.text
- else
= diff_line_content(line.text)
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index abf4f697f86..5ee3979c7e7 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -9,13 +9,13 @@
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
- %button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index 747bfa554cb..d48923b422a 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -2,12 +2,12 @@
%h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list.related-merge-requests
- - has_any_ci = @merge_requests.any?(&:pipeline)
+ - has_any_ci = @merge_requests.any?(&:head_pipeline)
- @merge_requests.each do |merge_request|
%li
%span.merge-request-ci-status
- - if merge_request.pipeline
- = render_pipeline_status(merge_request.pipeline)
+ - if merge_request.head_pipeline
+ = render_pipeline_status(merge_request.head_pipeline)
- elsif has_any_ci
= icon('blank fw')
%span.merge-request-id
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 9ffcc48eb80..fa189ae62d8 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -15,9 +15,9 @@
= icon('ban')
CLOSED
- - if merge_request.pipeline
+ - if merge_request.head_pipeline
%li
- = render_pipeline_status(merge_request.pipeline)
+ = render_pipeline_status(merge_request.head_pipeline)
- if merge_request.open? && merge_request.broken?
%li
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 9c6f562f7db..4a08ed045f4 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -34,10 +34,11 @@
= link_to url_for(params), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tab'} do
Pipelines
%span.badge= @pipelines.size
+ - if @pipeline.present?
%li.builds-tab
= link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
Builds
- %span.badge= @statuses.size
+ %span.badge= @statuses_count
%li.diffs-tab
= link_to url_for(params.merge(action: 'new_diffs')), data: {target: 'div#diffs', action: 'new/diffs', toggle: 'tab'} do
Changes
@@ -48,9 +49,10 @@
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
- # This tab is always loaded via AJAX
- - if @pipelines.any?
+ - if @pipeline.present?
#builds.builds.tab-pane
= render "projects/merge_requests/show/builds"
+ - if @pipelines.any?
#pipelines.pipelines.tab-pane
= render "projects/merge_requests/show/pipelines"
@@ -65,5 +67,5 @@
:javascript
var merge_request = new MergeRequest({
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}",
- buildsLoaded: "#{@pipelines.any? ? 'true' : 'false'}"
+ buildsLoaded: "#{@pipeline.present? ? 'true' : 'false'}"
});
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index a497f418c7c..0e2975bd551 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -59,15 +59,16 @@
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
Commits
%span.badge= @commits_count
- - if @pipeline
+ - if @pipelines.any?
%li.pipelines-tab
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
%span.badge= @pipelines.size
+ - if @pipeline.present?
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
Builds
- %span.badge= @statuses.size
+ %span.badge= @statuses_count
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 18c72ed875c..6d9b91ad0e7 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,7 +1,7 @@
- if @pipeline
.mr-widget-heading
- %w[success success_with_warnings skipped canceled failed running pending].each do |status|
- .ci_widget{ class: "ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
+ .ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
= ci_icon_for_status(status)
%span
Pipeline
diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml
index 608fdf1c5f5..a8918c85dde 100644
--- a/app/views/projects/merge_requests/widget/_show.html.haml
+++ b/app/views/projects/merge_requests/widget/_show.html.haml
@@ -14,7 +14,7 @@
ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
ci_environments_status_url: "#{ci_environments_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
- ci_status: "#{@merge_request.pipeline ? @merge_request.pipeline.status : ''}",
+ ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
ci_message: {
normal: "Build {{status}} for \"{{title}}\"",
preparing: "{{status}} build for \"{{title}}\""
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index b43b13de4ca..1d39f3a7534 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -12,10 +12,10 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline
- %button.dropdown-toggle.btn{ type: 'button', data: { toggle: 'dropdown'} }
+ %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= projects_sort_options_hash[@sort]
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_tags_path(sort: sort_value_name) do
diff --git a/app/views/projects/variables/_table.html.haml b/app/views/projects/variables/_table.html.haml
index 07cee86ba4c..c7cebf45160 100644
--- a/app/views/projects/variables/_table.html.haml
+++ b/app/views/projects/variables/_table.html.haml
@@ -12,8 +12,8 @@
- @project.variables.order_key_asc.each do |variable|
- if variable.id?
%tr
- %td= variable.key
- %td= variable.value
+ %td.variable-key= variable.key
+ %td.variable-value{ "data-value" => variable.value }******
%td
= link_to namespace_project_variable_path(@project.namespace, @project, variable), class: "btn btn-transparent btn-variable-edit" do
%span.sr-only
diff --git a/app/views/projects/variables/index.html.haml b/app/views/projects/variables/index.html.haml
index 09bb54600af..39303700131 100644
--- a/app/views/projects/variables/index.html.haml
+++ b/app/views/projects/variables/index.html.haml
@@ -15,3 +15,4 @@
No variables found, add one with the form above.
- else
= render "table"
+ %button.btn.btn-info.js-btn-toggle-reveal-values{"data-status" => 'hidden'} Reveal Values
diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml
index ef1c0296d49..938be20c7cf 100644
--- a/app/views/search/_filter.html.haml
+++ b/app/views/search/_filter.html.haml
@@ -3,7 +3,7 @@
- if params[:project_id].present?
= hidden_field_tag :project_id, params[:project_id]
.dropdown
- %button.dropdown-menu-toggle.btn.js-search-group-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Group:" } }
+ %button.dropdown-menu-toggle.js-search-group-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Group:" } }
%span.dropdown-toggle-text
Group:
- if @group.present?
@@ -18,7 +18,7 @@
= dropdown_loading
.dropdown.project-filter
- %button.dropdown-menu-toggle.btn.js-search-project-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Project:" } }
+ %button.dropdown-menu-toggle.js-search-project-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Project:" } }
%span.dropdown-toggle-text
Project:
- if @project.present?
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index ba25e09d638..0bc851b4256 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -13,8 +13,9 @@
.input-group-addon
= root_url
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
- autofocus: local_assigns[:autofocus] || false, pattern: "[a-zA-Z0-9-_]+",
- required: true, title: 'Please choose a group name with no special characters.'
+ autofocus: local_assigns[:autofocus] || false, required: true,
+ pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE,
+ title: 'Please choose a group name with no special characters.'
- if @group.persisted?
.alert.alert-warning.prepend-top-10
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index 68e05cb72e1..ede3c7090d7 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -1,11 +1,11 @@
.dropdown.inline.prepend-left-10
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', data: {toggle: 'dropdown'}}
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
%li
= link_to page_filter_path(sort: sort_value_priority, label: true) do
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index b7e5e928993..e3503981afe 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -29,9 +29,9 @@
.filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown", selected: finder.labels.select(:title).uniq, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
- - if issuable_filters_present
+ - if issuable_filter_present?
.filter-item.inline.reset-filters
- %a{href: page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search])} Reset filters
+ %a{href: page_filter_path(without: issuable_filter_params)} Reset filters
.pull-right
- if boards_page
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 9b9ad510444..3d515a05d46 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -16,20 +16,9 @@
= render 'shared/issuable/form/template_selector', issuable: issuable
= render 'shared/issuable/form/title', issuable: issuable, form: form
-.form-group.detail-page-description
- = form.label :description, 'Description', class: 'control-label'
- .col-sm-10
+= render 'shared/issuable/form/description', issuable: issuable, form: form
- = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
- = render 'projects/zen', f: form, attr: :description,
- classes: 'note-textarea',
- placeholder: "Write a comment or drag your files here...",
- supports_slash_commands: !issuable.persisted?
- = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
- .clearfix
- .error-alert
-
-- if issuable.is_a?(Issue)
+- if issuable.respond_to?(:confidential)
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
@@ -37,38 +26,7 @@
= form.check_box :confidential
This issue is confidential and should only be visible to team members with at least Reporter access.
-- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- - has_due_date = issuable.has_attribute?(:due_date)
- %hr
- .row
- %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
- .form-group.issue-assignee
- = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
- .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- .issuable-form-select-holder
- - if issuable.assignee_id
- = form.hidden_field :assignee_id
- = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
- placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
- .form-group.issue-milestone
- = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
- .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- .issuable-form-select-holder
- = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
- .form-group
- - has_labels = @labels && @labels.any?
- = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
- = form.hidden_field :label_ids, multiple: true, value: ''
- .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- .issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
- - if has_due_date
- .col-lg-6
- .form-group
- = form.label :due_date, "Due date", class: "control-label"
- .col-sm-10
- .issuable-form-select-holder
- = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
+= render 'shared/issuable/form/metadata', issuable: issuable, form: form
- if issuable.can_move?(current_user)
%hr
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 1d778bc88de..22b5a6aa11b 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -22,7 +22,7 @@
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) }
= multi_label_name(selected, "Labels")
- = icon('caret-down')
+ = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: dropdown_title, show_footer: show_footer, show_create: show_create }
- if show_create && project && can?(current_user, :admin_label, project)
diff --git a/app/views/shared/issuable/form/_description.html.haml b/app/views/shared/issuable/form/_description.html.haml
new file mode 100644
index 00000000000..dbace9ce401
--- /dev/null
+++ b/app/views/shared/issuable/form/_description.html.haml
@@ -0,0 +1,15 @@
+- issuable = local_assigns.fetch(:issuable)
+- form = local_assigns.fetch(:form)
+
+.form-group.detail-page-description
+ = form.label :description, 'Description', class: 'control-label'
+ .col-sm-10
+
+ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
+ = render 'projects/zen', f: form, attr: :description,
+ classes: 'note-textarea',
+ placeholder: "Write a comment or drag your files here...",
+ supports_slash_commands: !issuable.persisted?
+ = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
+ .clearfix
+ .error-alert
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
new file mode 100644
index 00000000000..a47085230b8
--- /dev/null
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -0,0 +1,38 @@
+- issuable = local_assigns.fetch(:issuable)
+
+- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
+
+- has_due_date = issuable.has_attribute?(:due_date)
+- has_labels = @labels && @labels.any?
+- form = local_assigns.fetch(:form)
+
+%hr
+.row
+ %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
+ .form-group.issue-assignee
+ = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
+ .col-sm-10{ class: ("col-lg-8" if has_due_date) }
+ .issuable-form-select-holder
+ - if issuable.assignee_id
+ = form.hidden_field :assignee_id
+ = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
+ placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: issuable.project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
+ .form-group.issue-milestone
+ = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
+ .col-sm-10{ class: ("col-lg-8" if has_due_date) }
+ .issuable-form-select-holder
+ = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
+ .form-group
+ - has_labels = @labels && @labels.any?
+ = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.hidden_field :label_ids, multiple: true, value: ''
+ .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
+ .issuable-form-select-holder
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
+ - if has_due_date
+ .col-lg-6
+ .form-group
+ = form.label :due_date, "Due date", class: "control-label"
+ .col-sm-10
+ .issuable-form-select-holder
+ = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index 331727ba9d8..fccddb70d18 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -2,14 +2,33 @@ class AuthorizedProjectsWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ LEASE_TIMEOUT = 1.minute.to_i
+
def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'args' => args_list)
end
def perform(user_id)
user = User.find_by(id: user_id)
- return unless user
- user.refresh_authorized_projects
+ refresh(user) if user
+ end
+
+ def refresh(user)
+ lease_key = "refresh_authorized_projects:#{user.id}"
+ lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT)
+
+ until uuid = lease.try_obtain
+ # Keep trying until we obtain the lease. If we don't do so we may end up
+ # not updating the list of authorized projects properly. To prevent
+ # hammering Redis too much we'll wait for a bit between retries.
+ sleep(1)
+ end
+
+ begin
+ user.refresh_authorized_projects
+ ensure
+ Gitlab::ExclusiveLease.cancel(lease_key, uuid)
+ end
end
end
diff --git a/bin/rspec-stackprof b/bin/rspec-stackprof
new file mode 100755
index 00000000000..df79feb201d
--- /dev/null
+++ b/bin/rspec-stackprof
@@ -0,0 +1,16 @@
+#!/usr/bin/env ruby
+
+require 'stackprof'
+$:.unshift 'spec'
+require 'rails_helper'
+
+filename = ARGV[0].split('/').last
+interval = ENV.fetch('INTERVAL', 1000).to_i
+limit = ENV.fetch('LIMIT', 20)
+output_file = "tmp/#{filename}.dump"
+
+StackProf.run(mode: :wall, out: output_file, interval: interval) do
+ RSpec::Core::Runner.run(ARGV, $stderr, $stdout)
+end
+
+system("stackprof #{output_file} --text --limit #{limit}")
diff --git a/changelogs/unreleased/22373-reduce-queries-in-api-helpers-find_project.yml b/changelogs/unreleased/22373-reduce-queries-in-api-helpers-find_project.yml
new file mode 100644
index 00000000000..7f1d40e7c21
--- /dev/null
+++ b/changelogs/unreleased/22373-reduce-queries-in-api-helpers-find_project.yml
@@ -0,0 +1,4 @@
+---
+title: 'Make API::Helpers find a project with only one query'
+merge_request: 7714
+author:
diff --git a/changelogs/unreleased/24135-new-project-should-be-below-new-group-on-the-welcome-screen.yml b/changelogs/unreleased/24135-new-project-should-be-below-new-group-on-the-welcome-screen.yml
new file mode 100644
index 00000000000..855e4e1ba1d
--- /dev/null
+++ b/changelogs/unreleased/24135-new-project-should-be-below-new-group-on-the-welcome-screen.yml
@@ -0,0 +1,4 @@
+---
+title: Moved new projects button below new group button on the welcome screen
+merge_request: 7770
+author:
diff --git a/changelogs/unreleased/24150-consistent-dropdown-styles.yml b/changelogs/unreleased/24150-consistent-dropdown-styles.yml
new file mode 100644
index 00000000000..a328d796c43
--- /dev/null
+++ b/changelogs/unreleased/24150-consistent-dropdown-styles.yml
@@ -0,0 +1,4 @@
+---
+title: Homogenize filter and sort dropdown look'n'feel
+merge_request: 7583
+author: David Wagner
diff --git a/changelogs/unreleased/24161-non-intuitive-buttons-for-import-sources-in-administrator-settings-enable-disable.yml b/changelogs/unreleased/24161-non-intuitive-buttons-for-import-sources-in-administrator-settings-enable-disable.yml
deleted file mode 100644
index 1404748e83e..00000000000
--- a/changelogs/unreleased/24161-non-intuitive-buttons-for-import-sources-in-administrator-settings-enable-disable.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Changed import sources buttons to checkboxes
-merge_request: 7598
-author: Luke "Jared" Bennett
diff --git a/changelogs/unreleased/24266-Afraid-to-press-the-Orange-button-on-Merge-request-screen.yml b/changelogs/unreleased/24266-Afraid-to-press-the-Orange-button-on-Merge-request-screen.yml
deleted file mode 100644
index 28ca20c7dcc..00000000000
--- a/changelogs/unreleased/24266-Afraid-to-press-the-Orange-button-on-Merge-request-screen.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: If Build running change accept merge request when build succeeds button from orange to blue
-merge_request: 7577
-author:
diff --git a/changelogs/unreleased/24739-collapsed-build-list-sorting.yml b/changelogs/unreleased/24739-collapsed-build-list-sorting.yml
deleted file mode 100644
index 036e606318f..00000000000
--- a/changelogs/unreleased/24739-collapsed-build-list-sorting.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Sort builds by name within pipeline graph
-merge_request: 7681
-author:
diff --git a/changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml b/changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml
deleted file mode 100644
index 5e7580fb8f2..00000000000
--- a/changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: fixes last_deployment call environment is nil
-merge_request: 7671
-author:
diff --git a/changelogs/unreleased/24804-wrong-render-index-should-be-render-show-in-projects-pipelinessettingscontroller-update.yml b/changelogs/unreleased/24804-wrong-render-index-should-be-render-show-in-projects-pipelinessettingscontroller-update.yml
deleted file mode 100644
index 92dbbe3d164..00000000000
--- a/changelogs/unreleased/24804-wrong-render-index-should-be-render-show-in-projects-pipelinessettingscontroller-update.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix wrong template rendered when CI/CD settings aren't update successfully
-merge_request: 7665
-author:
diff --git a/changelogs/unreleased/24813-project-members-with-developer-access-can-no-longer-create-tags.yml b/changelogs/unreleased/24813-project-members-with-developer-access-can-no-longer-create-tags.yml
new file mode 100644
index 00000000000..9254db40742
--- /dev/null
+++ b/changelogs/unreleased/24813-project-members-with-developer-access-can-no-longer-create-tags.yml
@@ -0,0 +1,4 @@
+---
+title: Pass tag SHA to post-receive hook when tag is created via UI
+merge_request: 7700
+author:
diff --git a/changelogs/unreleased/24860-actionview-template-error-undefined-method-size-for-nil-nilclass.yml b/changelogs/unreleased/24860-actionview-template-error-undefined-method-size-for-nil-nilclass.yml
new file mode 100644
index 00000000000..4b4aea79380
--- /dev/null
+++ b/changelogs/unreleased/24860-actionview-template-error-undefined-method-size-for-nil-nilclass.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent error when submitting a merge request and pipeline is not defined
+merge_request: 7707
+author:
diff --git a/changelogs/unreleased/24863-mrs-without-discussions-are-mergeable.yml b/changelogs/unreleased/24863-mrs-without-discussions-are-mergeable.yml
deleted file mode 100644
index 9bdb9411135..00000000000
--- a/changelogs/unreleased/24863-mrs-without-discussions-are-mergeable.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Correctly determine mergeability of MR with no discussions
-merge_request:
-author:
diff --git a/changelogs/unreleased/24999-fix-project-avatar-alignment.yml b/changelogs/unreleased/24999-fix-project-avatar-alignment.yml
new file mode 100644
index 00000000000..7af812e7359
--- /dev/null
+++ b/changelogs/unreleased/24999-fix-project-avatar-alignment.yml
@@ -0,0 +1,4 @@
+---
+title: Adjust the width of project avatars to fix alignment within their container
+merge_request:
+author: Ryan Harris
diff --git a/changelogs/unreleased/25002-sentence-case-dashboard-tabs.yml b/changelogs/unreleased/25002-sentence-case-dashboard-tabs.yml
new file mode 100644
index 00000000000..cc8b0e28277
--- /dev/null
+++ b/changelogs/unreleased/25002-sentence-case-dashboard-tabs.yml
@@ -0,0 +1,4 @@
+---
+title: Sentence cased the nav tab headers on the project dashboard page
+merge_request:
+author: Ryan Harris
diff --git a/changelogs/unreleased/25011-hoverstates-for-collapsed-issue-merge-request-sidebar.yml b/changelogs/unreleased/25011-hoverstates-for-collapsed-issue-merge-request-sidebar.yml
new file mode 100644
index 00000000000..2c3ba1dfe44
--- /dev/null
+++ b/changelogs/unreleased/25011-hoverstates-for-collapsed-issue-merge-request-sidebar.yml
@@ -0,0 +1,4 @@
+---
+title: Adds hoverstates for collapsed Issue/Merge Request sidebar
+merge_request: !7777
+author:
diff --git a/changelogs/unreleased/25026-authenticate-user-for-new-snippet.yml b/changelogs/unreleased/25026-authenticate-user-for-new-snippet.yml
new file mode 100644
index 00000000000..a7b5810f1bf
--- /dev/null
+++ b/changelogs/unreleased/25026-authenticate-user-for-new-snippet.yml
@@ -0,0 +1,4 @@
+---
+title: Redirect to sign-in page when unauthenticated user tries to create a snippet
+merge_request: 7786
+author:
diff --git a/changelogs/unreleased/25055-pipelines-info-missing-from-mr-widget.yml b/changelogs/unreleased/25055-pipelines-info-missing-from-mr-widget.yml
new file mode 100644
index 00000000000..dad9db0ffef
--- /dev/null
+++ b/changelogs/unreleased/25055-pipelines-info-missing-from-mr-widget.yml
@@ -0,0 +1,4 @@
+---
+title: Fix pipelines info being hidden in merge request widget
+merge_request: 7808
+author:
diff --git a/changelogs/unreleased/7749-add-setting-to-disable-html-emails.yml b/changelogs/unreleased/7749-add-setting-to-disable-html-emails.yml
new file mode 100644
index 00000000000..9dd04d3f089
--- /dev/null
+++ b/changelogs/unreleased/7749-add-setting-to-disable-html-emails.yml
@@ -0,0 +1,3 @@
+title: Add setting to enable/disable HTML emails
+merge_request: 7749
+author:
diff --git a/changelogs/unreleased/Last-minute-CI-Style-tweaks-for-8-14.yml b/changelogs/unreleased/Last-minute-CI-Style-tweaks-for-8-14.yml
deleted file mode 100644
index 7d49c639a43..00000000000
--- a/changelogs/unreleased/Last-minute-CI-Style-tweaks-for-8-14.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Last minute CI Style tweaks for 8.14
-merge_request: 7643
-author:
diff --git a/changelogs/unreleased/create-dynamic-fixture-for-build_spec.yml b/changelogs/unreleased/create-dynamic-fixture-for-build_spec.yml
new file mode 100644
index 00000000000..f0d9ff0c34f
--- /dev/null
+++ b/changelogs/unreleased/create-dynamic-fixture-for-build_spec.yml
@@ -0,0 +1,4 @@
+---
+title: Create dynamic fixture for build_spec
+merge_request: 7589
+author: winniehell
diff --git a/changelogs/unreleased/disable-calendar-deselection.yml b/changelogs/unreleased/disable-calendar-deselection.yml
deleted file mode 100644
index 060797bba34..00000000000
--- a/changelogs/unreleased/disable-calendar-deselection.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix deselecting calendar days on contribution graph
-merge_request: 6453
-author: ClemMakesApps
diff --git a/changelogs/unreleased/events-cache-invalidation.yml b/changelogs/unreleased/events-cache-invalidation.yml
new file mode 100644
index 00000000000..2b30f4dcbce
--- /dev/null
+++ b/changelogs/unreleased/events-cache-invalidation.yml
@@ -0,0 +1,4 @@
+---
+title: Remove caching of events data
+merge_request: 6578
+author:
diff --git a/changelogs/unreleased/fix-build-without-trace-exceptions.yml b/changelogs/unreleased/fix-build-without-trace-exceptions.yml
deleted file mode 100644
index 3b95e96e212..00000000000
--- a/changelogs/unreleased/fix-build-without-trace-exceptions.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix exceptions when loading build trace
-merge_request: 7658
-author:
diff --git a/changelogs/unreleased/fix-ca-no-date.yml b/changelogs/unreleased/fix-ca-no-date.yml
new file mode 100644
index 00000000000..6de4a56ac0d
--- /dev/null
+++ b/changelogs/unreleased/fix-ca-no-date.yml
@@ -0,0 +1,4 @@
+---
+title: Fix for error thrown in cycle analytics events if build has not started
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-create-pipeline-with-builds-in-transaction.yml b/changelogs/unreleased/fix-create-pipeline-with-builds-in-transaction.yml
new file mode 100644
index 00000000000..e37841e80c3
--- /dev/null
+++ b/changelogs/unreleased/fix-create-pipeline-with-builds-in-transaction.yml
@@ -0,0 +1,4 @@
+---
+title: Create builds in transaction to avoid empty pipelines
+merge_request: 7742
+author:
diff --git a/changelogs/unreleased/fix-cycle-analytics-plan-issue.yml b/changelogs/unreleased/fix-cycle-analytics-plan-issue.yml
deleted file mode 100644
index 6ed16c6d722..00000000000
--- a/changelogs/unreleased/fix-cycle-analytics-plan-issue.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix cycle analytics plan stage when commits are missing
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix_sidekiq_stats_in_admin_area.yml b/changelogs/unreleased/fix_sidekiq_stats_in_admin_area.yml
deleted file mode 100644
index 4f007be8624..00000000000
--- a/changelogs/unreleased/fix_sidekiq_stats_in_admin_area.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Sidekiq stats in the admin area will now show correctly on different platforms
-merge_request:
-author: blackst0ne
diff --git a/changelogs/unreleased/hoopes-gitlab-ce-21027-add-diff-hunks-to-notification-emails.yml b/changelogs/unreleased/hoopes-gitlab-ce-21027-add-diff-hunks-to-notification-emails.yml
new file mode 100644
index 00000000000..73d8a52e001
--- /dev/null
+++ b/changelogs/unreleased/hoopes-gitlab-ce-21027-add-diff-hunks-to-notification-emails.yml
@@ -0,0 +1,4 @@
+---
+title: Add git diff context to notifications of new notes on merge requests
+merge_request:
+author: Heidi Hoopes
diff --git a/changelogs/unreleased/improve-invite-accept-page.yml b/changelogs/unreleased/improve-invite-accept-page.yml
new file mode 100644
index 00000000000..8a09a5ae42f
--- /dev/null
+++ b/changelogs/unreleased/improve-invite-accept-page.yml
@@ -0,0 +1,4 @@
+---
+title: Add note to the invite page when the logged in user email is not the same as the invitation
+merge_request:
+author:
diff --git a/changelogs/unreleased/issuable_filters_present-refactor.yml b/changelogs/unreleased/issuable_filters_present-refactor.yml
new file mode 100644
index 00000000000..c131f9cb68e
--- /dev/null
+++ b/changelogs/unreleased/issuable_filters_present-refactor.yml
@@ -0,0 +1,4 @@
+---
+title: Refactor issuable_filters_present to reduce duplications
+merge_request: 7776
+author: Semyon Pupkov
diff --git a/changelogs/unreleased/issue-24534.yml b/changelogs/unreleased/issue-24534.yml
new file mode 100644
index 00000000000..14d6730d3f6
--- /dev/null
+++ b/changelogs/unreleased/issue-24534.yml
@@ -0,0 +1,4 @@
+---
+title: Remove unnecessary sentences for status codes in the API documentation
+merge_request:
+author: Luis Alonso Chavez Armendariz
diff --git a/changelogs/unreleased/issue-boards-dragging-fix.yml b/changelogs/unreleased/issue-boards-dragging-fix.yml
deleted file mode 100644
index 565e09b930b..00000000000
--- a/changelogs/unreleased/issue-boards-dragging-fix.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed issue boards dragging card removing random issues
-merge_request:
-author:
diff --git a/changelogs/unreleased/jej-22869.yml b/changelogs/unreleased/jej-22869.yml
new file mode 100644
index 00000000000..9d2edcfee42
--- /dev/null
+++ b/changelogs/unreleased/jej-22869.yml
@@ -0,0 +1,4 @@
+---
+title: Fix information disclosure in `Projects::BlobController#update`
+merge_request:
+author:
diff --git a/changelogs/unreleased/jej-fix-missing-access-check-on-issues.yml b/changelogs/unreleased/jej-fix-missing-access-check-on-issues.yml
new file mode 100644
index 00000000000..844fba9a107
--- /dev/null
+++ b/changelogs/unreleased/jej-fix-missing-access-check-on-issues.yml
@@ -0,0 +1,4 @@
+---
+title: Fix missing access checks on issue lookup using IssuableFinder
+merge_request:
+author:
diff --git a/changelogs/unreleased/jej-use-issuable-finder-instead-of-access-check.yml b/changelogs/unreleased/jej-use-issuable-finder-instead-of-access-check.yml
new file mode 100644
index 00000000000..c0b6f50052c
--- /dev/null
+++ b/changelogs/unreleased/jej-use-issuable-finder-instead-of-access-check.yml
@@ -0,0 +1,4 @@
+---
+title: Replace issue access checks with use of IssuableFinder
+merge_request:
+author:
diff --git a/changelogs/unreleased/readme-link-fix.yml b/changelogs/unreleased/readme-link-fix.yml
new file mode 100644
index 00000000000..211d3b80c3a
--- /dev/null
+++ b/changelogs/unreleased/readme-link-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Fix broken README.md UX guide link.
+merge_request:
+author:
diff --git a/changelogs/unreleased/refresh-authorizations-with-lease.yml b/changelogs/unreleased/refresh-authorizations-with-lease.yml
new file mode 100644
index 00000000000..bb9b77018e3
--- /dev/null
+++ b/changelogs/unreleased/refresh-authorizations-with-lease.yml
@@ -0,0 +1,4 @@
+---
+title: Use a Redis lease for updating authorized projects
+merge_request: 7733
+author:
diff --git a/changelogs/unreleased/remove-jsx-react-eslint-plugins.yml b/changelogs/unreleased/remove-jsx-react-eslint-plugins.yml
new file mode 100644
index 00000000000..6e02998b3a8
--- /dev/null
+++ b/changelogs/unreleased/remove-jsx-react-eslint-plugins.yml
@@ -0,0 +1,5 @@
+---
+title: Changed eslint airbnb config to the base airbnb config and corrected eslintrc
+ plugins and envs
+merge_request: 7470
+author: Luke "Jared" Bennett
diff --git a/changelogs/unreleased/removing_unnecessary_indexes.yml b/changelogs/unreleased/removing_unnecessary_indexes.yml
new file mode 100644
index 00000000000..01314ab5585
--- /dev/null
+++ b/changelogs/unreleased/removing_unnecessary_indexes.yml
@@ -0,0 +1,4 @@
+---
+title: Remove unnecessary database indices
+merge_request:
+author:
diff --git a/changelogs/unreleased/sh-update-sidekiq-cron.yml b/changelogs/unreleased/sh-update-sidekiq-cron.yml
new file mode 100644
index 00000000000..d79ba817a18
--- /dev/null
+++ b/changelogs/unreleased/sh-update-sidekiq-cron.yml
@@ -0,0 +1,4 @@
+---
+title: Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1
+merge_request:
+author:
diff --git a/changelogs/unreleased/timeout-merge-request-for-binary-file.yml b/changelogs/unreleased/timeout-merge-request-for-binary-file.yml
new file mode 100644
index 00000000000..5161265d1bd
--- /dev/null
+++ b/changelogs/unreleased/timeout-merge-request-for-binary-file.yml
@@ -0,0 +1,4 @@
+---
+title: Timeout creating and viewing merge request for binary file
+merge_request:
+author:
diff --git a/changelogs/unreleased/workhorse-v1-0-1.yml b/changelogs/unreleased/workhorse-v1-0-1.yml
new file mode 100644
index 00000000000..c26c2d45b1d
--- /dev/null
+++ b/changelogs/unreleased/workhorse-v1-0-1.yml
@@ -0,0 +1,4 @@
+---
+title: Update GitLab Workhorse to v1.0.1
+merge_request: 7759
+author:
diff --git a/changelogs/unreleased/zen-mode-fixture.yml b/changelogs/unreleased/zen-mode-fixture.yml
new file mode 100644
index 00000000000..bec6f6e6dba
--- /dev/null
+++ b/changelogs/unreleased/zen-mode-fixture.yml
@@ -0,0 +1,4 @@
+---
+title: Replace static fixture for zen_mode_spec
+merge_request: 7686
+author: winniehell
diff --git a/changelogs/unreleased/zj-expose-coverage-pipelines.yml b/changelogs/unreleased/zj-expose-coverage-pipelines.yml
new file mode 100644
index 00000000000..34e4926e58a
--- /dev/null
+++ b/changelogs/unreleased/zj-expose-coverage-pipelines.yml
@@ -0,0 +1,4 @@
+---
+title: 'API: expose pipeline coverage'
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-fix-label-creation-non-members.yml b/changelogs/unreleased/zj-fix-label-creation-non-members.yml
new file mode 100644
index 00000000000..ae4824f82fa
--- /dev/null
+++ b/changelogs/unreleased/zj-fix-label-creation-non-members.yml
@@ -0,0 +1,4 @@
+---
+title: Non members cannot create labels through the API
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-issue-search-slash-command.yml b/changelogs/unreleased/zj-issue-search-slash-command.yml
new file mode 100644
index 00000000000..de41c39d545
--- /dev/null
+++ b/changelogs/unreleased/zj-issue-search-slash-command.yml
@@ -0,0 +1,4 @@
+---
+title: Add issue search slash command
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-upgrade-grape.yml b/changelogs/unreleased/zj-upgrade-grape.yml
deleted file mode 100644
index 1df42d98733..00000000000
--- a/changelogs/unreleased/zj-upgrade-grape.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update grape entity to 0.6.0
-merge_request: 7491
-author:
diff --git a/config/initializers/email_template_interceptor.rb b/config/initializers/email_template_interceptor.rb
new file mode 100644
index 00000000000..f195ca9bcd6
--- /dev/null
+++ b/config/initializers/email_template_interceptor.rb
@@ -0,0 +1,2 @@
+# Interceptor in lib/email_template_interceptor.rb
+ActionMailer::Base.register_interceptor(EmailTemplateInterceptor)
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
index 18a2df7c059..a984eda5ab5 100644
--- a/db/fixtures/development/04_project.rb
+++ b/db/fixtures/development/04_project.rb
@@ -1,5 +1,4 @@
require 'sidekiq/testing'
-require './db/fixtures/support/serialized_transaction'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/06_teams.rb b/db/fixtures/development/06_teams.rb
index 04c3690e152..5c2a03fec3f 100644
--- a/db/fixtures/development/06_teams.rb
+++ b/db/fixtures/development/06_teams.rb
@@ -1,5 +1,4 @@
require 'sidekiq/testing'
-require './db/fixtures/support/serialized_transaction'
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 7b3908fae98..916ee8dbac8 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -1,6 +1,5 @@
require 'sidekiq/testing'
require './spec/support/test_env'
-require './db/fixtures/support/serialized_transaction'
class Gitlab::Seeder::CycleAnalytics
def initialize(project, perf: false)
diff --git a/db/fixtures/support/serialized_transaction.rb b/db/fixtures/support/serialized_transaction.rb
deleted file mode 100644
index d3305b661e5..00000000000
--- a/db/fixtures/support/serialized_transaction.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'gitlab/database'
-
-module Gitlab
- module Database
- def self.serialized_transaction
- connection.transaction { yield }
- end
- end
-end
diff --git a/db/migrate/20130319214458_create_forked_project_links.rb b/db/migrate/20130319214458_create_forked_project_links.rb
index 66eb11a4b2b..41b0b700a6f 100644
--- a/db/migrate/20130319214458_create_forked_project_links.rb
+++ b/db/migrate/20130319214458_create_forked_project_links.rb
@@ -5,7 +5,7 @@ class CreateForkedProjectLinks < ActiveRecord::Migration
t.integer :forked_to_project_id, null: false
t.integer :forked_from_project_id, null: false
- t.timestamps
+ t.timestamps null: true
end
add_index :forked_project_links, :forked_to_project_id, unique: true
end
diff --git a/db/migrate/20130506090604_create_deploy_keys_projects.rb b/db/migrate/20130506090604_create_deploy_keys_projects.rb
index 7d6662d358a..f2e416d3b6f 100644
--- a/db/migrate/20130506090604_create_deploy_keys_projects.rb
+++ b/db/migrate/20130506090604_create_deploy_keys_projects.rb
@@ -5,7 +5,7 @@ class CreateDeployKeysProjects < ActiveRecord::Migration
t.integer :deploy_key_id, null: false
t.integer :project_id, null: false
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20130617095603_create_users_groups.rb b/db/migrate/20130617095603_create_users_groups.rb
index 45cff93fe4a..cb098aa9bf9 100644
--- a/db/migrate/20130617095603_create_users_groups.rb
+++ b/db/migrate/20130617095603_create_users_groups.rb
@@ -6,7 +6,7 @@ class CreateUsersGroups < ActiveRecord::Migration
t.integer :group_id, null: false
t.integer :user_id, null: false
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20130711063759_create_project_group_links.rb b/db/migrate/20130711063759_create_project_group_links.rb
index bd9d40a50db..8da7ff6f4cd 100644
--- a/db/migrate/20130711063759_create_project_group_links.rb
+++ b/db/migrate/20130711063759_create_project_group_links.rb
@@ -5,7 +5,7 @@ class CreateProjectGroupLinks < ActiveRecord::Migration
t.integer :project_id, null: false
t.integer :group_id, null: false
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20131112114325_create_broadcast_messages.rb b/db/migrate/20131112114325_create_broadcast_messages.rb
index ce37a8e2708..4ada40f1b66 100644
--- a/db/migrate/20131112114325_create_broadcast_messages.rb
+++ b/db/migrate/20131112114325_create_broadcast_messages.rb
@@ -7,7 +7,7 @@ class CreateBroadcastMessages < ActiveRecord::Migration
t.datetime :ends_at
t.integer :alert_type
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20140122112253_create_merge_request_diffs.rb b/db/migrate/20140122112253_create_merge_request_diffs.rb
index 395c3edfc79..68448c91529 100644
--- a/db/migrate/20140122112253_create_merge_request_diffs.rb
+++ b/db/migrate/20140122112253_create_merge_request_diffs.rb
@@ -7,7 +7,7 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration
t.text :st_diffs, null: true
t.integer :merge_request_id, null: false
- t.timestamps
+ t.timestamps null: true
end
if ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/migrate/20140209025651_create_emails.rb b/db/migrate/20140209025651_create_emails.rb
index 571beb19cdd..48d14682628 100644
--- a/db/migrate/20140209025651_create_emails.rb
+++ b/db/migrate/20140209025651_create_emails.rb
@@ -5,7 +5,7 @@ class CreateEmails < ActiveRecord::Migration
t.integer :user_id, null: false
t.string :email, null: false
- t.timestamps
+ t.timestamps null: true
end
add_index :emails, :user_id
diff --git a/db/migrate/20140625115202_create_users_star_projects.rb b/db/migrate/20140625115202_create_users_star_projects.rb
index 32dd99e83be..c50bc4bd614 100644
--- a/db/migrate/20140625115202_create_users_star_projects.rb
+++ b/db/migrate/20140625115202_create_users_star_projects.rb
@@ -4,7 +4,7 @@ class CreateUsersStarProjects < ActiveRecord::Migration
create_table :users_star_projects do |t|
t.integer :project_id, null: false
t.integer :user_id, null: false
- t.timestamps
+ t.timestamps null: true
end
add_index :users_star_projects, :user_id
add_index :users_star_projects, :project_id
diff --git a/db/migrate/20140729134820_create_labels.rb b/db/migrate/20140729134820_create_labels.rb
index df0f8cb9f03..589aced0d76 100644
--- a/db/migrate/20140729134820_create_labels.rb
+++ b/db/migrate/20140729134820_create_labels.rb
@@ -6,7 +6,7 @@ class CreateLabels < ActiveRecord::Migration
t.string :color
t.integer :project_id
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20140729140420_create_label_links.rb b/db/migrate/20140729140420_create_label_links.rb
index fa5992605f8..abdbaa1bd1a 100644
--- a/db/migrate/20140729140420_create_label_links.rb
+++ b/db/migrate/20140729140420_create_label_links.rb
@@ -6,7 +6,7 @@ class CreateLabelLinks < ActiveRecord::Migration
t.integer :target_id
t.string :target_type
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20140914113604_add_members_table.rb b/db/migrate/20140914113604_add_members_table.rb
index bc3c1bb61e4..fb0876dd520 100644
--- a/db/migrate/20140914113604_add_members_table.rb
+++ b/db/migrate/20140914113604_add_members_table.rb
@@ -9,7 +9,7 @@ class AddMembersTable < ActiveRecord::Migration
t.integer :notification_level, null: false
t.string :type
- t.timestamps
+ t.timestamps null: true
end
add_index :members, :type
diff --git a/db/migrate/20140914173417_remove_old_member_tables.rb b/db/migrate/20140914173417_remove_old_member_tables.rb
index aff8e94e5be..067dc21ccf1 100644
--- a/db/migrate/20140914173417_remove_old_member_tables.rb
+++ b/db/migrate/20140914173417_remove_old_member_tables.rb
@@ -12,7 +12,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration
t.integer :user_id, null: false
t.integer :notification_level, null: false, default: 3
- t.timestamps
+ t.timestamps null: true
end
create_table :users_projects do |t|
@@ -21,7 +21,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration
t.integer :user_id, null: false
t.integer :notification_level, null: false, default: 3
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20141118150935_add_audit_event.rb b/db/migrate/20141118150935_add_audit_event.rb
index 3884228456f..f0452f46459 100644
--- a/db/migrate/20141118150935_add_audit_event.rb
+++ b/db/migrate/20141118150935_add_audit_event.rb
@@ -13,7 +13,7 @@ class AddAuditEvent < ActiveRecord::Migration
# Details for the event
t.text :details
- t.timestamps
+ t.timestamps null: true
end
add_index :audit_events, :author_id
diff --git a/db/migrate/20141216155758_create_doorkeeper_tables.rb b/db/migrate/20141216155758_create_doorkeeper_tables.rb
index b323ffe96f5..1c4d32e133c 100644
--- a/db/migrate/20141216155758_create_doorkeeper_tables.rb
+++ b/db/migrate/20141216155758_create_doorkeeper_tables.rb
@@ -7,7 +7,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
t.string :secret, null: false
t.text :redirect_uri, null: false
t.string :scopes, null: false, default: ''
- t.timestamps
+ t.timestamps null: true
end
add_index :oauth_applications, :uid, unique: true
diff --git a/db/migrate/20150108073740_create_application_settings.rb b/db/migrate/20150108073740_create_application_settings.rb
index dfa2f765357..c26a7c39574 100644
--- a/db/migrate/20150108073740_create_application_settings.rb
+++ b/db/migrate/20150108073740_create_application_settings.rb
@@ -8,7 +8,7 @@ class CreateApplicationSettings < ActiveRecord::Migration
t.boolean :gravatar_enabled
t.text :sign_in_text
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20150313012111_create_subscriptions_table.rb b/db/migrate/20150313012111_create_subscriptions_table.rb
index 8adb193b27f..0977c9adfec 100644
--- a/db/migrate/20150313012111_create_subscriptions_table.rb
+++ b/db/migrate/20150313012111_create_subscriptions_table.rb
@@ -6,7 +6,7 @@ class CreateSubscriptionsTable < ActiveRecord::Migration
t.references :subscribable, polymorphic: true
t.boolean :subscribed
- t.timestamps
+ t.timestamps null: true
end
add_index :subscriptions,
diff --git a/db/migrate/20150806104937_create_abuse_reports.rb b/db/migrate/20150806104937_create_abuse_reports.rb
index 3c749b5d9a9..9f1512db862 100644
--- a/db/migrate/20150806104937_create_abuse_reports.rb
+++ b/db/migrate/20150806104937_create_abuse_reports.rb
@@ -6,7 +6,7 @@ class CreateAbuseReports < ActiveRecord::Migration
t.integer :user_id
t.text :message
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20151103134857_create_lfs_objects.rb b/db/migrate/20151103134857_create_lfs_objects.rb
index 745b52e2b24..fadaf637cec 100644
--- a/db/migrate/20151103134857_create_lfs_objects.rb
+++ b/db/migrate/20151103134857_create_lfs_objects.rb
@@ -5,7 +5,7 @@ class CreateLfsObjects < ActiveRecord::Migration
t.string :oid, null: false, unique: true
t.integer :size, null: false
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20151103134958_create_lfs_objects_projects.rb b/db/migrate/20151103134958_create_lfs_objects_projects.rb
index 3178e85b899..e830cbe4656 100644
--- a/db/migrate/20151103134958_create_lfs_objects_projects.rb
+++ b/db/migrate/20151103134958_create_lfs_objects_projects.rb
@@ -5,7 +5,7 @@ class CreateLfsObjectsProjects < ActiveRecord::Migration
t.integer :lfs_object_id, null: false
t.integer :project_id, null: false
- t.timestamps
+ t.timestamps null: true
end
add_index :lfs_objects_projects, :project_id
diff --git a/db/migrate/20151105094515_create_releases.rb b/db/migrate/20151105094515_create_releases.rb
index 145b8db1486..87f692c64d0 100644
--- a/db/migrate/20151105094515_create_releases.rb
+++ b/db/migrate/20151105094515_create_releases.rb
@@ -6,7 +6,7 @@ class CreateReleases < ActiveRecord::Migration
t.text :description
t.integer :project_id
- t.timestamps
+ t.timestamps null: true
end
add_index :releases, :project_id
diff --git a/db/migrate/20160212123307_create_tasks.rb b/db/migrate/20160212123307_create_tasks.rb
index 20573b01351..8b5b1dd694d 100644
--- a/db/migrate/20160212123307_create_tasks.rb
+++ b/db/migrate/20160212123307_create_tasks.rb
@@ -9,7 +9,7 @@ class CreateTasks < ActiveRecord::Migration
t.integer :action, null: false
t.string :state, null: false, index: true
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20160416180807_add_award_emoji.rb b/db/migrate/20160416180807_add_award_emoji.rb
index a3bee9b1bc6..c0957f028a8 100644
--- a/db/migrate/20160416180807_add_award_emoji.rb
+++ b/db/migrate/20160416180807_add_award_emoji.rb
@@ -6,7 +6,7 @@ class AddAwardEmoji < ActiveRecord::Migration
t.references :user
t.references :awardable, polymorphic: true
- t.timestamps
+ t.timestamps null: true
end
add_index :award_emoji, :user_id
diff --git a/db/migrate/20160831214002_create_project_features.rb b/db/migrate/20160831214002_create_project_features.rb
index 2d76a015a08..343953826f0 100644
--- a/db/migrate/20160831214002_create_project_features.rb
+++ b/db/migrate/20160831214002_create_project_features.rb
@@ -10,7 +10,7 @@ class CreateProjectFeatures < ActiveRecord::Migration
t.integer :snippets_access_level
t.integer :builds_access_level
- t.timestamps
+ t.timestamps null: true
end
end
end
diff --git a/db/migrate/20161128142110_remove_unnecessary_indexes.rb b/db/migrate/20161128142110_remove_unnecessary_indexes.rb
new file mode 100644
index 00000000000..9deab19782e
--- /dev/null
+++ b/db/migrate/20161128142110_remove_unnecessary_indexes.rb
@@ -0,0 +1,33 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveUnnecessaryIndexes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ remove_index :labels, column: :group_id if index_exists?(:labels, :group_id)
+ remove_index :award_emoji, column: :user_id if index_exists?(:award_emoji, :user_id)
+ remove_index :ci_builds, column: :commit_id if index_exists?(:ci_builds, :commit_id)
+ remove_index :deployments, column: :project_id if index_exists?(:deployments, :project_id)
+ remove_index :deployments, column: ["project_id", "environment_id"] if index_exists?(:deployments, ["project_id", "environment_id"])
+ remove_index :lists, column: :board_id if index_exists?(:lists, :board_id)
+ remove_index :milestones, column: :project_id if index_exists?(:milestones, :project_id)
+ remove_index :notes, column: :project_id if index_exists?(:notes, :project_id)
+ remove_index :users_star_projects, column: :user_id if index_exists?(:users_star_projects, :user_id)
+ end
+
+ def down
+ add_concurrent_index :labels, :group_id
+ add_concurrent_index :award_emoji, :user_id
+ add_concurrent_index :ci_builds, :commit_id
+ add_concurrent_index :deployments, :project_id
+ add_concurrent_index :deployments, ["project_id", "environment_id"]
+ add_concurrent_index :lists, :board_id
+ add_concurrent_index :milestones, :project_id
+ add_concurrent_index :notes, :project_id
+ add_concurrent_index :users_star_projects, :user_id
+ end
+end
diff --git a/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb b/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb
new file mode 100644
index 00000000000..1c59241d0fe
--- /dev/null
+++ b/db/migrate/20161128161412_add_html_emails_enabled_to_application_settings.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddHtmlEmailsEnabledToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ # When a migration requires downtime you **must** uncomment the following
+ # constant and define a short and easy to understand explanation as to why the
+ # migration requires downtime.
+ # DOWNTIME_REASON = ''
+
+ # When using the methods "add_concurrent_index" or "add_column_with_default"
+ # you must disable the use of transactions as these methods can not run in an
+ # existing transaction. When using "add_concurrent_index" make sure that this
+ # method is the _only_ method called in the migration, any other changes
+ # should go in a separate migration. This ensures that upon failure _only_ the
+ # index creation fails and can be retried or reverted easily.
+ #
+ # To disable transactions uncomment the following line and remove these
+ # comments:
+ # disable_ddl_transaction!
+
+ def change
+ add_column :application_settings, :html_emails_enabled, :boolean, default: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b3c49b52597..0d510c8a269 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20161118183841) do
+ActiveRecord::Schema.define(version: 20161128161412) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -106,6 +106,7 @@ ActiveRecord::Schema.define(version: 20161118183841) do
t.integer "housekeeping_incremental_repack_period", default: 10, null: false
t.integer "housekeeping_full_repack_period", default: 50, null: false
t.integer "housekeeping_gc_period", default: 200, null: false
+ t.boolean "html_emails_enabled", default: true
end
create_table "audit_events", force: :cascade do |t|
@@ -131,7 +132,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
add_index "award_emoji", ["awardable_type", "awardable_id"], name: "index_award_emoji_on_awardable_type_and_awardable_id", using: :btree
add_index "award_emoji", ["user_id", "name"], name: "index_award_emoji_on_user_id_and_name", using: :btree
- add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree
create_table "boards", force: :cascade do |t|
t.integer "project_id", null: false
@@ -219,7 +219,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
- add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree
add_index "ci_builds", ["gl_project_id"], name: "index_ci_builds_on_gl_project_id", using: :btree
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
@@ -409,9 +408,7 @@ ActiveRecord::Schema.define(version: 20161118183841) do
end
add_index "deployments", ["project_id", "environment_id", "iid"], name: "index_deployments_on_project_id_and_environment_id_and_iid", using: :btree
- add_index "deployments", ["project_id", "environment_id"], name: "index_deployments_on_project_id_and_environment_id", using: :btree
add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree
- add_index "deployments", ["project_id"], name: "index_deployments_on_project_id", using: :btree
create_table "emails", force: :cascade do |t|
t.integer "user_id", null: false
@@ -569,7 +566,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
end
add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree
- add_index "labels", ["group_id"], name: "index_labels_on_group_id", using: :btree
create_table "lfs_objects", force: :cascade do |t|
t.string "oid", null: false
@@ -600,7 +596,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
end
add_index "lists", ["board_id", "label_id"], name: "index_lists_on_board_id_and_label_id", unique: true, using: :btree
- add_index "lists", ["board_id"], name: "index_lists_on_board_id", using: :btree
add_index "lists", ["label_id"], name: "index_lists_on_label_id", using: :btree
create_table "members", force: :cascade do |t|
@@ -726,7 +721,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
add_index "milestones", ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
- add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree
add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
@@ -789,7 +783,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
- add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
create_table "notification_settings", force: :cascade do |t|
@@ -1242,7 +1235,6 @@ ActiveRecord::Schema.define(version: 20161118183841) do
add_index "users_star_projects", ["project_id"], name: "index_users_star_projects_on_project_id", using: :btree
add_index "users_star_projects", ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree
- add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree
create_table "web_hooks", force: :cascade do |t|
t.string "url", limit: 2000
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index d3f216fb3bf..b8b63df091e 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -221,7 +221,7 @@ Tip: If you want to limit access to the nested members of an Active Directory
group you can use the following syntax:
```
-(memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com)
+(memberOf=CN=My Group,DC=Example,DC=com)
```
Please note that GitLab does not support the custom filter syntax used by
diff --git a/doc/api/access_requests.md b/doc/api/access_requests.md
index ea308b54d62..dee3e384080 100644
--- a/doc/api/access_requests.md
+++ b/doc/api/access_requests.md
@@ -18,8 +18,6 @@
Gets a list of access requests viewable by the authenticated user.
-Returns `200` if the request succeeds.
-
```
GET /groups/:id/access_requests
GET /projects/:id/access_requests
@@ -61,8 +59,6 @@ Example response:
Requests access for the authenticated user to a group or project.
-Returns `201` if the request succeeds.
-
```
POST /groups/:id/access_requests
POST /projects/:id/access_requests
@@ -94,8 +90,6 @@ Example response:
Approves an access request for the given user.
-Returns `201` if the request succeeds.
-
```
PUT /groups/:id/access_requests/:user_id/approve
PUT /projects/:id/access_requests/:user_id/approve
@@ -129,8 +123,6 @@ Example response:
Denies an access request for the given user.
-Returns `200` if the request succeeds.
-
```
DELETE /groups/:id/access_requests/:user_id
DELETE /projects/:id/access_requests/:user_id
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index 06111f4ab67..58092bdd400 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -158,7 +158,7 @@ Example Response:
### Delete an award emoji
Sometimes its just not meant to be, and you'll have to remove your award. Only available to
-admins or the author of the award. Status code 200 on success, 401 if unauthorized.
+admins or the author of the award.
```
DELETE /projects/:id/issues/:issue_id/award_emoji/:award_id
@@ -331,7 +331,7 @@ Example Response:
### Delete an award emoji
Sometimes its just not meant to be, and you'll have to remove your award. Only available to
-admins or the author of the award. Status code 200 on success, 401 if unauthorized.
+admins or the author of the award.
```
DELETE /projects/:id/issues/:issue_id/notes/:note_id/award_emoji/:award_id
diff --git a/doc/api/boards.md b/doc/api/boards.md
index 28681719f43..c83db6df80c 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -148,10 +148,6 @@ Example response:
Creates a new Issue Board list.
-If the operation is successful, a status code of `200` and the newly-created
-list is returned. If an error occurs, an error number and a message explaining
-the reason is returned.
-
```
POST /projects/:id/boards/:board_id/lists
```
@@ -184,10 +180,6 @@ Example response:
Updates an existing Issue Board list. This call is used to change list position.
-If the operation is successful, a code of `200` and the updated board list is
-returned. If an error occurs, an error number and a message explaining the
-reason is returned.
-
```
PUT /projects/:id/boards/:board_id/lists/:list_id
```
@@ -220,8 +212,6 @@ Example response:
## Delete a board list
Only for admins and project owners. Soft deletes the board list in question.
-If the operation is successful, a status code `200` is returned. In case you cannot
-destroy this board list, or it is not present, code `404` is given.
```
DELETE /projects/:id/boards/:board_id/lists/:list_id
diff --git a/doc/api/branches.md b/doc/api/branches.md
index f68eeb9f86b..07dfa5d4d7f 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -212,9 +212,6 @@ Example response:
}
```
-It returns `200` if it succeeds or `400` if failed with an error message
-explaining the reason.
-
## Delete repository branch
```
@@ -226,8 +223,7 @@ DELETE /projects/:id/repository/branches/:branch
| `id` | integer | yes | The ID of a project |
| `branch` | string | yes | The name of the branch |
-It returns `200` if it succeeds, `404` if the branch to be deleted does not exist
-or `400` for other reasons. In case of an error, an explaining message is provided.
+In case of an error, an explaining message is provided.
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches/newbranch"
@@ -253,7 +249,6 @@ DELETE /projects/:id/repository/merged_branches
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
-It returns `200` to indicate deletion of all merged branches was started.
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/merged_branches"
diff --git a/doc/api/broadcast_messages.md b/doc/api/broadcast_messages.md
index c3a9207a3ae..a3e9c01f335 100644
--- a/doc/api/broadcast_messages.md
+++ b/doc/api/broadcast_messages.md
@@ -62,10 +62,6 @@ Example response:
## Create a broadcast message
-Responds with `400 Bad request` when the `message` parameter is missing or the
-`color` or `font` values are invalid, and `201 Created` when the broadcast
-message was successfully created.
-
```
POST /broadcast_messages
```
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 134263d27b4..16f8e32c82a 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -315,10 +315,6 @@ Example response:
Creates a new project issue.
-If the operation is successful, a status code of `200` and the newly-created
-issue is returned. If an error occurs, an error number and a message explaining
-the reason is returned.
-
```
POST /projects/:id/issues
```
@@ -377,10 +373,6 @@ Example response:
Updates an existing project issue. This call is also used to mark an issue as
closed.
-If the operation is successful, a code of `200` and the updated issue is
-returned. If an error occurs, an error number and a message explaining the
-reason is returned.
-
```
PUT /projects/:id/issues/:issue_id
```
@@ -439,8 +431,6 @@ Example response:
## Delete an issue
Only for admins and project owners. Soft deletes the issue in question.
-If the operation is successful, a status code `200` is returned. In case you cannot
-destroy this issue, or it is not present, code `404` is given.
```
DELETE /projects/:id/issues/:issue_id
@@ -457,9 +447,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://git
## Move an issue
-Moves an issue to a different project. If the operation is successful, a status
-code `201` together with moved issue is returned. If the project, issue, or
-target project is not found, error `404` is returned. If the target project
+Moves an issue to a different project. If the target project
equals the source project or the user has insufficient permissions to move an
issue, error `400` together with an explaining error message is returned.
@@ -518,11 +506,9 @@ Example response:
## Subscribe to an issue
-Subscribes the authenticated user to an issue to receive notifications. If the
-operation is successful, status code `201` together with the updated issue is
-returned. If the user is already subscribed to the issue, the status code `304`
-is returned. If the project or issue is not found, status code `404` is
-returned.
+Subscribes the authenticated user to an issue to receive notifications.
+If the user is already subscribed to the issue, the status code `304`
+is returned.
```
POST /projects/:id/issues/:issue_id/subscription
@@ -576,10 +562,8 @@ Example response:
## Unsubscribe from an issue
Unsubscribes the authenticated user from the issue to not receive notifications
-from it. If the operation is successful, status code `200` together with the
-updated issue is returned. If the user is not subscribed to the issue, the
-status code `304` is returned. If the project or issue is not found, status code
-`404` is returned.
+from it. If the user is not subscribed to the issue, the
+status code `304` is returned.
```
DELETE /projects/:id/issues/:issue_id/subscription
@@ -633,8 +617,7 @@ Example response:
## Create a todo
-Manually creates a todo for the current user on an issue. If the request is
-successful, status code `200` together with the created todo is returned. If
+Manually creates a todo for the current user on an issue. If
there already exists a todo for the user on that issue, status code `304` is
returned.
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 78686fdcad4..863b28c23b7 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -82,9 +82,6 @@ Example response:
Creates a new label for the given repository with the given name and color.
-It returns 200 if the label was successfully created, 400 for wrong parameters
-and 409 if the label already exists.
-
```
POST /projects/:id/labels
```
@@ -121,10 +118,6 @@ Example response:
Deletes a label with a given name.
-It returns 200 if the label was successfully deleted, 400 for wrong parameters
-and 404 if the label does not exist.
-In case of an error, an additional error message is returned.
-
```
DELETE /projects/:id/labels
```
@@ -159,10 +152,6 @@ Example response:
Updates an existing label with new name or new color. At least one parameter
is required, to update the label.
-It returns 200 if the label was successfully deleted, 400 for wrong parameters
-and 404 if the label does not exist.
-In case of an error, an additional error message is returned.
-
```
PUT /projects/:id/labels
```
@@ -199,11 +188,9 @@ Example response:
## Subscribe to a label
-Subscribes the authenticated user to a label to receive notifications. If the
-operation is successful, status code `201` together with the updated label is
-returned. If the user is already subscribed to the label, the status code `304`
-is returned. If the project or label is not found, status code `404` is
-returned.
+Subscribes the authenticated user to a label to receive notifications.
+If the user is already subscribed to the label, the status code `304`
+is returned.
```
POST /projects/:id/labels/:label_id/subscription
@@ -237,10 +224,8 @@ Example response:
## Unsubscribe from a label
Unsubscribes the authenticated user from a label to not receive notifications
-from it. If the operation is successful, status code `200` together with the
-updated label is returned. If the user is not subscribed to the label, the
-status code `304` is returned. If the project or label is not found, status code
-`404` is returned.
+from it. If the user is not subscribed to the label, the
+status code `304` is returned.
```
DELETE /projects/:id/labels/:label_id/subscription
diff --git a/doc/api/members.md b/doc/api/members.md
index 6535e9a7801..5dcb2a5f60a 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -16,8 +16,6 @@ The access levels are defined in the `Gitlab::Access` module. Currently, these l
Gets a list of group or project members viewable by the authenticated user.
-Returns `200` if the request succeeds.
-
```
GET /groups/:id/members
GET /projects/:id/members
@@ -60,8 +58,6 @@ Example response:
Gets a member of a group or project.
-Returns `200` if the request succeeds.
-
```
GET /groups/:id/members/:user_id
GET /projects/:id/members/:user_id
@@ -95,8 +91,6 @@ Example response:
Adds a member to a group or project.
-Returns `201` if the request succeeds.
-
```
POST /groups/:id/members
POST /projects/:id/members
@@ -131,8 +125,6 @@ Example response:
Updates a member of a group or project.
-Returns `200` if the request succeeds.
-
```
PUT /groups/:id/members/:user_id
PUT /projects/:id/members/:user_id
@@ -167,8 +159,6 @@ Example response:
Removes a user from a group or project.
-Returns `200` if the request succeeds.
-
```
DELETE /groups/:id/members/:user_id
DELETE /projects/:id/members/:user_id
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 1d1d646138c..9460b3f73b1 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -338,9 +338,6 @@ Parameters:
}
```
-If the operation is successful, 200 and the newly created merge request is returned.
-If an error occurs, an error number and a message explaining the reason is returned.
-
## Update MR
Updates an existing merge request. You can change the target branch, title, or even close the MR.
@@ -415,14 +412,9 @@ Parameters:
}
```
-If the operation is successful, 200 and the updated merge request is returned.
-If an error occurs, an error number and a message explaining the reason is returned.
-
## Delete a merge request
Only for admins and project owners. Soft deletes the merge request in question.
-If the operation is successful, a status code `200` is returned. In case you cannot
-destroy this merge request, or it is not present, code `404` is given.
```
DELETE /projects/:id/merge_requests/:merge_request_id
@@ -441,15 +433,14 @@ curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://git
Merge changes submitted with MR using this API.
-If the merge succeeds you'll get a `200 OK`.
-If it has some conflicts and can not be merged - you'll get a 405 and the error message 'Branch cannot be merged'
+If it has some conflicts and can not be merged - you'll get a `405` and the error message 'Branch cannot be merged'
-If merge request is already merged or closed - you'll get a 406 and the error message 'Method Not Allowed'
+If merge request is already merged or closed - you'll get a `406` and the error message 'Method Not Allowed'
-If the `sha` parameter is passed and does not match the HEAD of the source - you'll get a 409 and the error message 'SHA does not match HEAD of source branch'
+If the `sha` parameter is passed and does not match the HEAD of the source - you'll get a `409` and the error message 'SHA does not match HEAD of source branch'
-If you don't have permissions to accept this merge request - you'll get a 401
+If you don't have permissions to accept this merge request - you'll get a `401`
```
PUT /projects/:id/merge_requests/:merge_request_id/merge
@@ -521,13 +512,11 @@ Parameters:
## Cancel Merge When Pipeline Succeeds
-If successful you'll get `200 OK`.
+If you don't have permissions to accept this merge request - you'll get a `401`
-If you don't have permissions to accept this merge request - you'll get a 401
+If the merge request is already merged or closed - you get `405` and error message 'Method Not Allowed'
-If the merge request is already merged or closed - you get 405 and error message 'Method Not Allowed'
-
-In case the merge request is not set to be merged when the build succeeds, you'll also get a 406 error.
+In case the merge request is not set to be merged when the build succeeds, you'll also get a `406` error.
```
PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds
```
@@ -671,11 +660,8 @@ Example response when an external issue tracker (e.g. JIRA) is used:
## Subscribe to a merge request
-Subscribes the authenticated user to a merge request to receive notification. If
-the operation is successful, status code `201` together with the updated merge
-request is returned. If the user is already subscribed to the merge request, the
-status code `304` is returned. If the project or merge request is not found,
-status code `404` is returned.
+Subscribes the authenticated user to a merge request to receive notification. If the user is already subscribed to the merge request, the
+status code `304` is returned.
```
POST /projects/:id/merge_requests/:merge_request_id/subscription
@@ -748,10 +734,8 @@ Example response:
## Unsubscribe from a merge request
Unsubscribes the authenticated user from a merge request to not receive
-notifications from that merge request. If the operation is successful, status
-code `200` together with the updated merge request is returned. If the user is
-not subscribed to the merge request, the status code `304` is returned. If the
-project or merge request is not found, status code `404` is returned.
+notifications from that merge request. If the user is
+not subscribed to the merge request, the status code `304` is returned.
```
DELETE /projects/:id/merge_requests/:merge_request_id/subscription
@@ -823,9 +807,8 @@ Example response:
## Create a todo
-Manually creates a todo for the current user on a merge request. If the
-request is successful, status code `200` together with the created todo is
-returned. If there already exists a todo for the user on that merge request,
+Manually creates a todo for the current user on a merge request.
+If there already exists a todo for the user on that merge request,
status code `304` is returned.
```
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 9971806be56..214dfa4068d 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -109,8 +109,7 @@ Parameters:
### Delete an issue note
-Deletes an existing note of an issue. On success, this API method returns 200
-and the deleted note. If the note does not exist, the API returns 404.
+Deletes an existing note of an issue.
```
DELETE /projects/:id/issues/:issue_id/notes/:note_id
@@ -234,8 +233,7 @@ Parameters:
### Delete a snippet note
-Deletes an existing note of a snippet. On success, this API method returns 200
-and the deleted note. If the note does not exist, the API returns 404.
+Deletes an existing note of a snippet.
```
DELETE /projects/:id/snippets/:snippet_id/notes/:note_id
@@ -364,8 +362,7 @@ Parameters:
### Delete a merge request note
-Deletes an existing note of a merge request. On success, this API method returns
-200 and the deleted note. If the note does not exist, the API returns 404.
+Deletes an existing note of a merge request.
```
DELETE /projects/:id/merge_requests/:merge_request_id/notes/:note_id
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 6455c333faf..82351ae688f 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -41,7 +41,8 @@ Example of response
"started_at": null,
"finished_at": null,
"committed_at": null,
- "duration": null
+ "duration": null,
+ "coverage": "30.0"
},
{
"id": 48,
@@ -64,7 +65,8 @@ Example of response
"started_at": null,
"finished_at": null,
"committed_at": null,
- "duration": null
+ "duration": null,
+ "coverage": null
}
]
```
@@ -110,7 +112,8 @@ Example of response
"started_at": null,
"finished_at": "2016-08-11T11:32:35.145Z",
"committed_at": null,
- "duration": null
+ "duration": null,
+ "coverage": "30.0"
}
```
@@ -155,7 +158,8 @@ Example of response
"started_at": null,
"finished_at": null,
"committed_at": null,
- "duration": null
+ "duration": null,
+ "coverage": null
}
```
@@ -200,7 +204,8 @@ Response:
"started_at": null,
"finished_at": "2016-08-11T11:32:35.145Z",
"committed_at": null,
- "duration": null
+ "duration": null,
+ "coverage": null
}
```
@@ -245,7 +250,8 @@ Response:
"started_at": null,
"finished_at": "2016-08-11T11:32:35.145Z",
"committed_at": null,
- "duration": null
+ "duration": null,
+ "coverage": null
}
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index bd27a0a6fae..de57f91bb8e 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -645,7 +645,7 @@ Parameters:
### Edit project
-Updates an existing project
+Updates an existing project.
```
PUT /projects/:id
@@ -676,9 +676,6 @@ Parameters:
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
-On success, method returns 200 with the updated project. If parameters are
-invalid, 400 is returned.
-
### Fork project
Forks a project into the user namespace of the authenticated user or the one provided.
@@ -696,8 +693,7 @@ Parameters:
### Star a project
-Stars a given project. Returns status code `201` and the project on success and
-`304` if the project is already starred.
+Stars a given project. Returns status code `304` if the project is already starred.
```
POST /projects/:id/star
@@ -767,8 +763,7 @@ Example response:
### Unstar a project
-Unstars a given project. Returns status code `200` and the project on success
-and `304` if the project is not starred.
+Unstars a given project. Returns status code `304` if the project is not starred.
```
DELETE /projects/:id/star
@@ -839,10 +834,6 @@ Example response:
Archives the project if the user is either admin or the project owner of this project. This action is
idempotent, thus archiving an already archived project will not change the project.
-Status code 201 with the project as body is given when successful, in case the user doesn't
-have the proper access rights, code 403 is returned. Status 404 is returned if the project
-doesn't exist, or is hidden to the user.
-
```
POST /projects/:id/archive
```
@@ -928,10 +919,6 @@ Example response:
Unarchives the project if the user is either admin or the project owner of this project. This action is
idempotent, thus unarchiving an non-archived project will not change the project.
-Status code 201 with the project as body is given when successful, in case the user doesn't
-have the proper access rights, code 403 is returned. Status 404 is returned if the project
-doesn't exist, or is hidden to the user.
-
```
POST /projects/:id/unarchive
```
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index efd23d514bc..3fb8b73be6d 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -108,8 +108,7 @@ Example response:
## Delete system hook
-Deletes a system hook. It returns `200 OK` if the hooks is deleted and
-`404 Not Found` if the hook is not found.
+Deletes a system hook.
---
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 398b080e3f6..14573d48fe4 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -40,9 +40,7 @@ Parameters:
## Get a single repository tag
-Get a specific repository tag determined by its name. It returns `200` together
-with the tag information if the tag exists. It returns `404` if the tag does not
-exist.
+Get a specific repository tag determined by its name.
```
GET /projects/:id/repository/tags/:tag_name
@@ -124,14 +122,12 @@ Parameters:
The message will be `nil` when creating a lightweight tag otherwise
it will contain the annotation.
-It returns 201 if the operation succeed. In case of an error,
-405 with an explaining error message is returned.
+In case of an error,
+status code `405` with an explaining error message is returned.
## Delete a tag
-Deletes a tag of a repository with given name. On success, this API method
-returns 200 with the name of the deleted tag. If the tag does not exist, the
-API returns 404.
+Deletes a tag of a repository with given name.
```
DELETE /projects/:id/repository/tags/:tag_name
@@ -150,9 +146,8 @@ Parameters:
## Create a new release
-Add release notes to the existing git tag. It returns 201 if the release is
-created successfully. If the tag does not exist, 404 is returned. If there
-already exists a release for the given tag, 409 is returned.
+Add release notes to the existing git tag. If there
+already exists a release for the given tag, status code `409` is returned.
```
POST /projects/:id/repository/tags/:tag_name/release
@@ -173,9 +168,7 @@ Parameters:
## Update a release
-Updates the release notes of a given release. It returns 200 if the release is
-successfully updated. If the tag or the release does not exist, it returns 404
-with a proper error message.
+Updates the release notes of a given release.
```
PUT /projects/:id/repository/tags/:tag_name/release
diff --git a/doc/api/users.md b/doc/api/users.md
index b38c335490a..52a6b691610 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -271,8 +271,8 @@ Parameters:
- `can_create_group` (optional) - User can create groups - true or false
- `external` (optional) - Flags the user as external - true or false(default)
-Note, at the moment this method does only return a 404 error,
-even in cases where a 409 (Conflict) would be more appropriate,
+Note, at the moment this method does only return a `404` error,
+even in cases where a `409` (Conflict) would be more appropriate,
e.g. when renaming the email address to some existing one.
## User deletion
@@ -449,8 +449,6 @@ Parameters:
- `title` (required) - new SSH Key's title
- `key` (required) - new SSH key
-Will return created key with status `201 Created` on success, or `404 Not found` on fail.
-
## Delete SSH key for current user
Deletes key owned by currently authenticated user.
@@ -581,8 +579,6 @@ Parameters:
- `id` (required) - id of specified user
- `email` (required) - email address
-Will return created email with status `201 Created` on success, or `404 Not found` on fail.
-
## Delete email for current user
Deletes email owned by currently authenticated user.
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index b137e6ae82e..fc948a7a116 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -113,6 +113,77 @@ merge request.
add an alternative text: `[identifier]: https://example.com "Alternative text"`
that appears when hovering your mouse on a link
+### Linking to inline docs
+
+Sometimes it's needed to link to the built-in documentation that GitLab provides
+under `/help`. This is normally done in files inside the `app/views/` directory
+with the help of the `help_page_path` helper method.
+
+In its simplest form, the HAML code to generate a link to the `/help` page is:
+
+```haml
+= link_to 'Help page', help_page_path('user/permissions')
+```
+
+The `help_page_path` contains the path to the document you want to link to with
+the following conventions:
+
+- it is relative to the `doc/` directory in the GitLab repository
+- the `.md` extension must be omitted
+- it must not end with a slash (`/`)
+
+Below are some special cases where should be used depending on the context.
+You can combine one or more of the following:
+
+1. **Linking to an anchor link.** Use `anchor` as part of the `help_page_path`
+ method:
+
+ ```haml
+ = link_to 'Help page', help_page_path('user/permissions', anchor: 'anchor-link')
+ ```
+
+1. **Opening links in a new tab.** This should be the default behavior:
+
+ ```haml
+ = link_to 'Help page', help_page_path('user/permissions'), target: '_blank'
+ ```
+
+1. **Linking to a circle icon.** Usually used in settings where a long
+ description cannot be used, like near checkboxes. You can basically use
+ any font awesome icon, but prefer the `question-circle`:
+
+ ```haml
+ = link_to icon('question-circle'), help_page_path('user/permissions')
+ ```
+
+1. **Using a button link.** Useful in places where text would be out of context
+ with the rest of the page layout:
+
+ ```haml
+ = link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info'
+ ```
+
+1. **Underlining a link.**
+
+ ```haml
+ = link_to 'Help page', help_page_path('user/permissions'), class: 'underlined-link'
+ ```
+
+1. **Using links inline of some text.**
+
+ ```haml
+ Description to #{link_to 'Help page', help_page_path('user/permissions')}.
+ ```
+
+1. **Adding a period at the end of the sentence.** Useful when you don't want
+ the period to be part of the link:
+
+ ```haml
+ = succeed '.' do
+ Learn more in the
+ = link_to 'Help page', help_page_path('user/permissions')
+ ```
+
## Images
- Place images in a separate directory named `img/` in the same directory where
diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md
index b7e6387838e..568dedf1669 100644
--- a/doc/development/limit_ee_conflicts.md
+++ b/doc/development/limit_ee_conflicts.md
@@ -143,109 +143,162 @@ to resolve when you add the indentation to the equation.
For instance this kind of thing:
```haml
+.form-group.detail-page-description
+ = form.label :description, 'Description', class: 'control-label'
+ .col-sm-10
+ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
+ = render 'projects/zen', f: form, attr: :description,
+ classes: 'note-textarea',
+ placeholder: "Write a comment or drag your files here...",
+ supports_slash_commands: !issuable.persisted?
+ = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
+ .clearfix
+ .error-alert
+- if issuable.is_a?(Issue)
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = form.label :confidential do
+ = form.check_box :confidential
+ This issue is confidential and should only be visible to team members with at least Reporter access.
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- has_due_date = issuable.has_attribute?(:due_date)
%hr
.row
%div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
.form-group.issue-assignee
- = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
- if issuable.assignee_id
- = f.hidden_field :assignee_id
+ = form.hidden_field :assignee_id
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
.form-group.issue-milestone
- = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group
- has_labels = @labels && @labels.any?
- = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
- = f.hidden_field :label_ids, multiple: true, value: ''
+ = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label"
-
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
- if issuable.respond_to?(:weight)
+ - weight_options = Issue.weight_options
+ - weight_options.delete(Issue::WEIGHT_ALL)
+ - weight_options.delete(Issue::WEIGHT_ANY)
.form-group
- = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
+ = form.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
Weight
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
- = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true },
- { class: 'select2 js-select2', data: { placeholder: "Select weight" }}
-
+ .issuable-form-select-holder
+ - if issuable.weight
+ = form.hidden_field :weight
+ = dropdown_tag(issuable.weight || "Weight", options: { title: "Select weight", toggle_class: 'js-weight-select js-issuable-form-weight', dropdown_class: "dropdown-menu-selectable dropdown-menu-weight",
+ placeholder: "Search weight", data: { field_name: "#{issuable.class.model_name.param_key}[weight]" , default_label: "Weight" } }) do
+ %ul
+ - weight_options.each do |weight|
+ %li
+ %a{href: "#", data: { id: weight, none: weight === Issue::WEIGHT_NONE }, class: ("is-active" if issuable.weight == weight)}
+ = weight
- if has_due_date
.col-lg-6
.form-group
- = f.label :due_date, "Due date", class: "control-label"
+ = form.label :due_date, "Due date", class: "control-label"
.col-sm-10
.issuable-form-select-holder
- = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
+ = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
```
could be simplified by using partials:
```haml
-= render 'metadata_form', issuable: issuable
+= render 'shared/issuable/form/description', issuable: issuable, form: form
+
+- if issuable.respond_to?(:confidential)
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = form.label :confidential do
+ = form.check_box :confidential
+ This issue is confidential and should only be visible to team members with at least Reporter access.
+
+= render 'shared/issuable/form/metadata', issuable: issuable, form: form
```
-and then the `_metadata_form.html.haml` could be as follows:
+and then the `app/views/shared/issuable/form/_metadata.html.haml` could be as follows:
```haml
+- issuable = local_assigns.fetch(:issuable)
+
- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- has_due_date = issuable.has_attribute?(:due_date)
+- has_labels = @labels && @labels.any?
+- form = local_assigns.fetch(:form)
+
%hr
.row
%div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
.form-group.issue-assignee
- = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
- if issuable.assignee_id
- = f.hidden_field :assignee_id
+ = form.hidden_field :assignee_id
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
- placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
+ placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: issuable.project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
.form-group.issue-milestone
- = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group
- has_labels = @labels && @labels.any?
- = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
- = f.hidden_field :label_ids, multiple: true, value: ''
+ = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label"
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
- = render 'weight_form', issuable: issuable, has_due_date: has_due_date
+ = render "shared/issuable/form/weight", issuable: issuable, form: form
- if has_due_date
.col-lg-6
.form-group
- = f.label :due_date, "Due date", class: "control-label"
+ = form.label :due_date, "Due date", class: "control-label"
.col-sm-10
.issuable-form-select-holder
- = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
+ = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
```
-and then the `_weight_form.html.haml` could be as follows:
+and then the `app/views/shared/issuable/form/_weight.html.haml` could be as follows:
```haml
+- issuable = local_assigns.fetch(:issuable)
+
- return unless issuable.respond_to?(:weight)
- has_due_date = issuable.has_attribute?(:due_date)
+- form = local_assigns.fetch(:form)
.form-group
- = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
+ = form.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
Weight
.col-sm-10{ class: ("col-lg-8" if has_due_date) }
- = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true },
- { class: 'select2 js-select2', data: { placeholder: "Select weight" }}
+ .issuable-form-select-holder
+ - if issuable.weight
+ = form.hidden_field :weight
+
+ = weight_dropdown_tag(issuable, toggle_class: 'js-issuable-form-weight') do
+ %ul
+ - Issue.weight_options.each do |weight|
+ %li
+ %a{ href: '#', data: { id: weight, none: weight === Issue::WEIGHT_NONE }, class: ("is-active" if issuable.weight == weight) }
+ = weight
```
Note:
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 8337c2d9cb3..5c43ae7b79a 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -101,6 +101,116 @@ In short:
5. If you must write a benchmark use the benchmark-ips Gem instead of Ruby's
`Benchmark` module.
+## Profiling
+
+By collecting snapshots of process state at regular intervals, profiling allows
+you to see where time is spent in a process. The [StackProf](https://github.com/tmm1/stackprof)
+gem is included in GitLab's development environment, allowing you to investigate
+the behaviour of suspect code in detail.
+
+It's important to note that profiling an application *alters its performance*,
+and will generally be done *in an unrepresentative environment*. In particular,
+a method is not necessarily troublesome just because it is executed many times,
+or takes a long time to execute. Profiles are tools you can use to better
+understand what is happening in an application - using that information wisely
+is up to you!
+
+Keeping that in mind, to create a profile, identify (or create) a spec that
+exercises the troublesome code path, then run it using the `bin/rspec-stackprof`
+helper, e.g.:
+
+```
+$ LIMIT=10 bin/rspec-stackprof spec/policies/project_policy_spec.rb
+8/8 |====== 100 ======>| Time: 00:00:18
+
+Finished in 18.19 seconds (files took 4.8 seconds to load)
+8 examples, 0 failures
+
+==================================
+ Mode: wall(1000)
+ Samples: 17033 (5.59% miss rate)
+ GC: 1901 (11.16%)
+==================================
+ TOTAL (pct) SAMPLES (pct) FRAME
+ 6000 (35.2%) 2566 (15.1%) Sprockets::Cache::FileStore#get
+ 2018 (11.8%) 888 (5.2%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_no_cache
+ 1338 (7.9%) 640 (3.8%) ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#execute
+ 3125 (18.3%) 394 (2.3%) Sprockets::Cache::FileStore#safe_open
+ 913 (5.4%) 301 (1.8%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_cache
+ 288 (1.7%) 288 (1.7%) ActiveRecord::Attribute#initialize
+ 246 (1.4%) 246 (1.4%) Sprockets::Cache::FileStore#safe_stat
+ 295 (1.7%) 193 (1.1%) block (2 levels) in class_attribute
+ 187 (1.1%) 187 (1.1%) block (4 levels) in class_attribute
+```
+
+You can limit the specs that are run by passing any arguments `rspec` would
+normally take.
+
+The output is sorted by the `Samples` column by default. This is the number of
+samples taken where the method is the one currently being executed. The `Total`
+column shows the number of samples taken where the method, or any of the methods
+it calls, were being executed.
+
+To create a graphical view of the call stack:
+
+```shell
+$ stackprof tmp/project_policy_spec.rb.dump --graphviz > project_policy_spec.dot
+$ dot -Tsvg project_policy_spec.dot > project_policy_spec.svg
+```
+
+To load the profile in [kcachegrind](https://kcachegrind.github.io/):
+
+```
+$ stackprof tmp/project_policy_spec.dump --callgrind > project_policy_spec.callgrind
+$ kcachegrind project_policy_spec.callgrind # Linux
+$ qcachegrind project_policy_spec.callgrind # Mac
+```
+
+It may be useful to zoom in on a specific method, e.g.:
+
+```
+$ stackprof tmp/project_policy_spec.rb.dump --method warm_asset_cache
+TestEnv#warm_asset_cache (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development-kit/gitlab/spec/support/test_env.rb:164)
+ samples: 0 self (0.0%) / 6288 total (36.9%)
+ callers:
+ 6288 ( 100.0%) block (2 levels) in <top (required)>
+ callees (6288 total):
+ 6288 ( 100.0%) Capybara::RackTest::Driver#visit
+ code:
+ | 164 | def warm_asset_cache
+ | 165 | return if warm_asset_cache?
+ | 166 | return unless defined?(Capybara)
+ | 167 |
+ 6288 (36.9%) | 168 | Capybara.current_session.driver.visit '/'
+ | 169 | end
+$ stackprof tmp/project_policy_spec.rb.dump --method BasePolicy#abilities
+BasePolicy#abilities (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development-kit/gitlab/app/policies/base_policy.rb:79)
+ samples: 0 self (0.0%) / 50 total (0.3%)
+ callers:
+ 25 ( 50.0%) BasePolicy.abilities
+ 25 ( 50.0%) BasePolicy#collect_rules
+ callees (50 total):
+ 25 ( 50.0%) ProjectPolicy#rules
+ 25 ( 50.0%) BasePolicy#collect_rules
+ code:
+ | 79 | def abilities
+ | 80 | return RuleSet.empty if @user && @user.blocked?
+ | 81 | return anonymous_abilities if @user.nil?
+ 50 (0.3%) | 82 | collect_rules { rules }
+ | 83 | end
+```
+
+Since the profile includes the work done by the test suite as well as the
+application code, these profiles can be used to investigate slow tests as well.
+However, for smaller runs (like this example), this means that the cost of
+setting up the test suite will tend to dominate.
+
+It's also possible to modify the application code in-place to output profiles
+whenever a particular code path is triggered without going through the test
+suite first. See the
+[StackProf documentation](https://github.com/tmm1/stackprof/blob/master/README.md)
+for details.
+
## Importance of Changes
When working on performance improvements, it's important to always ask yourself
diff --git a/doc/development/ux_guide/components.md b/doc/development/ux_guide/components.md
index 764c3355714..8e51edd23ef 100644
--- a/doc/development/ux_guide/components.md
+++ b/doc/development/ux_guide/components.md
@@ -43,7 +43,7 @@ Primary links are blue in their rest state. Secondary links (such as the time st
#### Hover
-An underline should always be added on hover. A gray link becomes blue on hover.
+On hover, an underline should be added and the color should change. Both the primary and secondary link should become the darker blue color on hover.
#### Focus
@@ -72,9 +72,7 @@ Secondary buttons are for alternative commands. They should be conveyed by a bu
### Icon and text treatment
Text should be in sentence case, where only the first word is capitalized. "Create issue" is correct, not "Create Issue". Buttons should only contain an icon or a text, not both.
->>>
-TODO: Rationalize this. Ensure that we still believe this.
->>>
+> TODO: Rationalize this. Ensure that we still believe this.
### Colors
Follow the color guidance on the [basics](basics.md#color) page. The default color treatment is the white/grey button.
@@ -85,13 +83,13 @@ Follow the color guidance on the [basics](basics.md#color) page. The default col
Dropdowns are used to allow users to choose one (or many) options from a list of options. If this list of options is more 20, there should generally be a way to search through and filter the options (see the complex filter dropdowns below.)
->>>
-TODO: Will update this section when the new filters UI is implemented.
->>>
+> TODO: Will update this section when the new filters UI is implemented.
![Dropdown states](img/components-dropdown.png)
+### Max size
+The max height for dropdowns should target **10-15 items**. If the height of the dropdown is too large, the list becomes very hard to parse and it is easy to visually lose track of the item you are looking for. Usability also suffers as more mouse movement is required, and you have a larger area in which you hijack the scroll away from the page level. While it may initially seem counterintuitive to not show as many items as you can, it is actually quicker and easier to process the information when it is cropped at a reasonable height.
---
@@ -164,9 +162,7 @@ Cover blocks are generally used to create a heading element for a page, such as
## Panels
->>>
-TODO: Catalog how we are currently using panels and rationalize how they relate to alerts
->>>
+> TODO: Catalog how we are currently using panels and rationalize how they relate to alerts
![Panels](img/components-panels.png)
@@ -174,9 +170,7 @@ TODO: Catalog how we are currently using panels and rationalize how they relate
## Alerts
->>>
-TODO: Catalog how we are currently using alerts
->>>
+> TODO: Catalog how we are currently using alerts
![Alerts](img/components-alerts.png)
diff --git a/doc/development/ux_guide/img/components-anchorlinks.png b/doc/development/ux_guide/img/components-anchorlinks.png
index 7dd6a8a3876..4a9c730566c 100644
--- a/doc/development/ux_guide/img/components-anchorlinks.png
+++ b/doc/development/ux_guide/img/components-anchorlinks.png
Binary files differ
diff --git a/doc/install/installation.md b/doc/install/installation.md
index dabd69d7466..ee02d6024d9 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -403,7 +403,7 @@ If you are not using Linux you may have to run `gmake` instead of
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
- sudo -u git -H git checkout v1.0.0
+ sudo -u git -H git checkout v1.0.1
sudo -u git -H make
### Initialize Database and Activate Advanced Features
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index 556d71b8b76..9122dc62e39 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -123,7 +123,7 @@ To be able to access repositories on Bitbucket, GitLab will automatically
register your public key with Bitbucket as a deploy key for the repositories to
be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa` which
translates to `/var/opt/gitlab/.ssh/bitbucket_rsa` for Omnibus packages and to
-`/home/git/.ssh/bitbucket_rsa.pub` for installations from source.
+`/home/git/.ssh/bitbucket_rsa` for installations from source.
---
@@ -199,7 +199,7 @@ Your GitLab server is now able to connect to Bitbucket over SSH. You should be
able to see the "Import projects from Bitbucket" option on the New Project page
enabled.
-## Acknowledgemts
+## Acknowledgements
Special thanks to the writer behind the following article:
diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md
index 46ea19d11d0..a0e895773ce 100644
--- a/doc/update/8.13-to-8.14.md
+++ b/doc/update/8.13-to-8.14.md
@@ -84,7 +84,7 @@ GitLab 8.1.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all
-sudo -u git -H git checkout v1.0.0
+sudo -u git -H git checkout v1.0.1
sudo -u git -H make
```
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
index aa666a954bc..79dde620265 100644
--- a/features/steps/shared/issuable.rb
+++ b/features/steps/shared/issuable.rb
@@ -110,14 +110,14 @@ module SharedIssuable
end
step 'I sort the list by "Oldest updated"' do
- find('button.dropdown-toggle.btn').click
+ find('button.dropdown-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
click_link "Oldest updated"
end
end
step 'I sort the list by "Least popular"' do
- find('button.dropdown-toggle.btn').click
+ find('button.dropdown-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
click_link 'Least popular'
@@ -125,7 +125,7 @@ module SharedIssuable
end
step 'I sort the list by "Most popular"' do
- find('button.dropdown-toggle.btn').click
+ find('button.dropdown-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
click_link 'Most popular'
diff --git a/features/support/capybara.rb b/features/support/capybara.rb
index dae0d0f918c..47372df152d 100644
--- a/features/support/capybara.rb
+++ b/features/support/capybara.rb
@@ -6,7 +6,15 @@ timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 15
Capybara.javascript_driver = :poltergeist
Capybara.register_driver :poltergeist do |app|
- Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: timeout, window_size: [1366, 768])
+ Capybara::Poltergeist::Driver.new(
+ app,
+ js_errors: true,
+ timeout: timeout,
+ window_size: [1366, 768],
+ phantomjs_options: [
+ '--load-images=no'
+ ]
+ )
end
Capybara.default_max_wait_time = timeout
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 7a724487e02..fdb19558c1c 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -607,6 +607,7 @@ module API
expose :user, with: Entities::UserBasic
expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
expose :duration
+ expose :coverage
end
class EnvironmentBasic < Grape::Entity
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index fc39fdf4b67..5315c22e1e4 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -82,7 +82,7 @@ module API
:lfs_enabled, :request_access_enabled
end
put ':id' do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
authorize! :admin_group, group
if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
@@ -96,13 +96,13 @@ module API
success Entities::GroupDetail
end
get ":id" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
present group, with: Entities::GroupDetail
end
desc 'Remove a group.'
delete ":id" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
authorize! :admin_group, group
DestroyGroupService.new(group, current_user).execute
end
@@ -111,7 +111,7 @@ module API
success Entities::Project
end
get ":id/projects" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
projects = GroupProjectsFinder.new(group).execute(current_user)
projects = paginate projects
present projects, with: Entities::Project, user: current_user
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 2c593dbb4ea..34d9c3c6932 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -68,7 +68,7 @@ module API
end
def user_project
- @project ||= find_project(params[:id])
+ @project ||= find_project!(params[:id])
end
def available_labels
@@ -76,7 +76,15 @@ module API
end
def find_project(id)
- project = Project.find_with_namespace(id) || Project.find_by(id: id)
+ if id =~ /^\d+$/
+ Project.find_by(id: id)
+ else
+ Project.find_with_namespace(id)
+ end
+ end
+
+ def find_project!(id)
+ project = find_project(id)
if can?(current_user, :read_project, project)
project
@@ -97,7 +105,15 @@ module API
end
def find_group(id)
- group = Group.find_by(path: id) || Group.find_by(id: id)
+ if id =~ /^\d+$/
+ Group.find_by(id: id)
+ else
+ Group.find_by(path: id)
+ end
+ end
+
+ def find_group!(id)
+ group = find_group(id)
if can?(current_user, :read_group, group)
group
@@ -112,9 +128,7 @@ module API
end
def find_project_issue(id)
- issue = user_project.issues.find(id)
- not_found! unless can?(current_user, :read_issue, issue)
- issue
+ IssuesFinder.new(current_user, project_id: user_project.id).find(id)
end
def paginate(relation)
@@ -182,20 +196,6 @@ module API
ActionController::Parameters.new(attrs).permit!
end
- # Helper method for validating all labels against its names
- def validate_label_params(params)
- errors = {}
-
- params[:labels].to_s.split(',').each do |label_name|
- label = available_labels.find_or_initialize_by(title: label_name.strip)
- next if label.valid?
-
- errors[label.title] = label.errors
- end
-
- errors
- end
-
# Checks the occurrences of datetime attributes, each attribute if present in the params hash must be in ISO 8601
# format (YYYY-MM-DDTHH:MM:SSZ) or a Bad Request error is invoked.
#
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 90114f6f667..d9cae1501f8 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -2,7 +2,7 @@ module API
module Helpers
module MembersHelpers
def find_source(source_type, id)
- public_send("find_#{source_type}", id)
+ public_send("find_#{source_type}!", id)
end
def authorize_admin_source!(source_type, source)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index eea5b91d4f9..049b4fb214c 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -19,6 +19,15 @@ module API
def filter_issues_milestone(issues, milestone)
issues.includes(:milestone).where('milestones.title' => milestone)
end
+
+ def issue_params
+ new_params = declared(params, include_parent_namespace: false, include_missing: false).to_h
+ new_params = new_params.with_indifferent_access
+ new_params.delete(:id)
+ new_params.delete(:issue_id)
+
+ new_params
+ end
end
resource :issues do
@@ -68,7 +77,7 @@ module API
# GET /groups/:id/issues?milestone=1.0.0
# GET /groups/:id/issues?milestone=1.0.0&state=closed
get ":id/issues" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
params[:state] ||= 'opened'
params[:group_id] = group.id
@@ -86,6 +95,10 @@ module API
end
end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
resource :projects do
# Get a list of project issues
#
@@ -109,7 +122,7 @@ module API
# GET /projects/:id/issues?milestone=1.0.0&state=closed
# GET /issues?iid=42
get ":id/issues" do
- issues = user_project.issues.inc_notes_with_associations.visible_to_user(current_user)
+ issues = IssuesFinder.new(current_user, project_id: user_project.id).execute.inc_notes_with_associations
issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
@@ -152,17 +165,10 @@ module API
post ':id/issues' do
required_attributes! [:title]
- keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential]
+ keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels]
keys << :created_at if current_user.admin? || user_project.owner == current_user
attrs = attributes_for_keys(keys)
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- attrs[:labels] = params[:labels] if params[:labels]
-
# Convert and filter out invalid confidential flags
attrs['confidential'] = to_boolean(attrs['confidential'])
attrs.delete('confidential') if attrs['confidential'].nil?
@@ -180,41 +186,35 @@ module API
end
end
- # Update an existing issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # title (optional) - The title of an issue
- # description (optional) - The description of an issue
- # assignee_id (optional) - The ID of a user to assign issue
- # milestone_id (optional) - The ID of a milestone to assign issue
- # labels (optional) - The labels of an issue
- # state_event (optional) - The state event of an issue (close|reopen)
- # updated_at (optional) - Date time string, ISO 8601 formatted
- # due_date (optional) - Date time string in the format YEAR-MONTH-DAY
- # confidential (optional) - Boolean parameter if the issue should be confidential
- # Example Request:
- # PUT /projects/:id/issues/:issue_id
+ desc 'Update an existing issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ requires :issue_id, type: Integer, desc: "The ID of a project issue"
+ optional :title, type: String, desc: 'The new title of the issue'
+ optional :description, type: String, desc: 'The description of an issue'
+ optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
+ optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
+ optional :labels, type: String, desc: 'The labels of an issue'
+ optional :state_event, type: String, values: ['close', 'reopen'], desc: 'The state event of an issue'
+ # TODO 9.0, use the Grape DateTime type here
+ optional :updated_at, type: String, desc: 'Date time string, ISO 8601 formatted'
+ optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
+ # TODO 9.0, use the Grape boolean type here
+ optional :confidential, type: String, desc: 'Boolean parameter if the issue should be confidential'
+ end
put ':id/issues/:issue_id' do
issue = user_project.issues.find(params[:issue_id])
authorize! :update_issue, issue
- keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date, :confidential]
- keys << :updated_at if current_user.admin? || user_project.owner == current_user
- attrs = attributes_for_keys(keys)
-
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- attrs[:labels] = params[:labels] if params[:labels]
# Convert and filter out invalid confidential flags
- attrs['confidential'] = to_boolean(attrs['confidential'])
- attrs.delete('confidential') if attrs['confidential'].nil?
+ params[:confidential] = to_boolean(params[:confidential])
+ params.delete(:confidential) if params[:confidential].nil?
+
+ params.delete(:updated_at) unless current_user.admin? || user_project.owner == current_user
- issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
+ issue = ::Issues::UpdateService.new(user_project, current_user, issue_params).execute(issue)
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index c8cfdb6a90e..ad9b3d8039f 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -77,11 +77,6 @@ module API
mr_params = declared_params
- # Validate label names in advance
- if (errors = validate_label_params(mr_params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
if merge_request.valid?
@@ -157,11 +152,6 @@ module API
mr_params = declared_params(include_missing: false)
- # Validate label names in advance
- if (errors = validate_label_params(mr_params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request)
if merge_request.valid?
@@ -202,7 +192,7 @@ module API
should_remove_source_branch: params[:should_remove_source_branch]
}
- if params[:merge_when_build_succeeds] && merge_request.pipeline && merge_request.pipeline.active?
+ if params[:merge_when_build_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active?
::MergeRequests::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user, merge_params)
.execute(merge_request)
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index ddfde178d30..2ea3c433ae2 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -379,7 +379,7 @@ module API
# POST /projects/:id/fork/:forked_from_id
post ":id/fork/:forked_from_id" do
authenticated_as_admin!
- forked_from_project = find_project(params[:forked_from_id])
+ forked_from_project = find_project!(params[:forked_from_id])
unless forked_from_project.nil?
if user_project.forked_from_project.nil?
user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 4d23499aa39..bc427705777 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -65,7 +65,7 @@ module API
detail 'Added in GitLab 8.13'
end
post ':id/services/:service_slug/trigger' do
- project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
+ project = find_project(params[:id])
# This is not accurate, but done to prevent leakage of the project names
not_found!('Service') unless project
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index 569598fbd2c..bb4de39def1 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -13,7 +13,7 @@ module API
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/builds" do
- project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
+ project = find_project(params[:id])
trigger = Ci::Trigger.find_by_token(params[:token].to_s)
not_found! unless project && trigger
unauthorized! unless trigger.project == project
diff --git a/lib/email_template_interceptor.rb b/lib/email_template_interceptor.rb
new file mode 100644
index 00000000000..fb04a7824b8
--- /dev/null
+++ b/lib/email_template_interceptor.rb
@@ -0,0 +1,13 @@
+# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
+class EmailTemplateInterceptor
+ include Gitlab::CurrentSettings
+
+ def self.delivering_email(message)
+ # Remove HTML part if HTML emails are disabled.
+ unless current_application_settings.html_emails_enabled
+ message.part.delete_if do |part|
+ part.content_type.try(:start_with?, 'text/html')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat_commands/base_command.rb b/lib/gitlab/chat_commands/base_command.rb
index e59d69b72b9..25da8474e95 100644
--- a/lib/gitlab/chat_commands/base_command.rb
+++ b/lib/gitlab/chat_commands/base_command.rb
@@ -40,9 +40,7 @@ module Gitlab
private
def find_by_iid(iid)
- resource = collection.find_by(iid: iid)
-
- readable?(resource) ? resource : nil
+ collection.find_by(iid: iid)
end
end
end
diff --git a/lib/gitlab/chat_commands/command.rb b/lib/gitlab/chat_commands/command.rb
index 0ec358debc7..b0d3fdbc48a 100644
--- a/lib/gitlab/chat_commands/command.rb
+++ b/lib/gitlab/chat_commands/command.rb
@@ -4,6 +4,7 @@ module Gitlab
COMMANDS = [
Gitlab::ChatCommands::IssueShow,
Gitlab::ChatCommands::IssueCreate,
+ Gitlab::ChatCommands::IssueSearch,
Gitlab::ChatCommands::Deploy,
].freeze
diff --git a/lib/gitlab/chat_commands/issue_command.rb b/lib/gitlab/chat_commands/issue_command.rb
index f1bc36239d5..84de3e44c70 100644
--- a/lib/gitlab/chat_commands/issue_command.rb
+++ b/lib/gitlab/chat_commands/issue_command.rb
@@ -6,11 +6,7 @@ module Gitlab
end
def collection
- project.issues
- end
-
- def readable?(issue)
- self.class.can?(current_user, :read_issue, issue)
+ IssuesFinder.new(current_user, project_id: project.id).execute
end
end
end
diff --git a/lib/gitlab/chat_commands/issue_search.rb b/lib/gitlab/chat_commands/issue_search.rb
new file mode 100644
index 00000000000..51bf80c800b
--- /dev/null
+++ b/lib/gitlab/chat_commands/issue_search.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module ChatCommands
+ class IssueSearch < IssueCommand
+ def self.match(text)
+ /\Aissue\s+search\s+(?<query>.*)/.match(text)
+ end
+
+ def self.help_message
+ "issue search <your query>"
+ end
+
+ def execute(match)
+ collection.search(match[:query]).limit(QUERY_LIMIT)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat_commands/issue_show.rb b/lib/gitlab/chat_commands/issue_show.rb
index f5bceb038e5..2a45d49cf6b 100644
--- a/lib/gitlab/chat_commands/issue_show.rb
+++ b/lib/gitlab/chat_commands/issue_show.rb
@@ -2,7 +2,7 @@ module Gitlab
module ChatCommands
class IssueShow < IssueCommand
def self.match(text)
- /\Aissue\s+show\s+(?<iid>\d+)/.match(text)
+ /\Aissue\s+show\s+#{Issue.reference_prefix}?(?<iid>\d+)/.match(text)
end
def self.help_message
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 2d5c9232425..55b8f888d53 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -35,13 +35,6 @@ module Gitlab
order
end
- def self.serialized_transaction
- opts = {}
- opts[:isolation] = :serializable unless Rails.env.test? && connection.transaction_open?
-
- connection.transaction(opts) { yield }
- end
-
def self.random
Gitlab::Database.postgresql? ? "RANDOM()" : "RAND()"
end
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index fe7adb7bed6..26bb0bc16f5 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -20,7 +20,7 @@ module Gitlab
# Extracted method to highlight in the same iteration to the diff_collection.
def decorate_diff!(diff)
diff_file = super
- cache_highlight!(diff_file) if cacheable?
+ cache_highlight!(diff_file) if cacheable?(diff_file)
diff_file
end
@@ -60,8 +60,8 @@ module Gitlab
Rails.cache.write(cache_key, highlight_cache) if @highlight_cache_was_empty
end
- def cacheable?
- @merge_request_diff.present?
+ def cacheable?(diff_file)
+ @merge_request_diff.present? && diff_file.blob.text?
end
def cache_key
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 2690938fe82..47d8599e298 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -50,7 +50,7 @@ module Gitlab
end
def issues
- issues = Issue.visible_to_user(current_user).where(project_id: project_ids_relation)
+ issues = IssuesFinder.new(current_user).execute.where(project_id: project_ids_relation)
if query =~ /#(\d+)\z/
issues = issues.where(iid: $1)
diff --git a/package.json b/package.json
index 350e4cd80c9..961989f8012 100644
--- a/package.json
+++ b/package.json
@@ -6,13 +6,11 @@
"eslint-report": "npm run eslint -- --format html --output-file ./eslint-report.html"
},
"devDependencies": {
- "eslint": "^3.1.1",
- "eslint-config-airbnb": "^12.0.0",
+ "eslint": "^3.10.1",
+ "eslint-config-airbnb-base": "^10.0.1",
"eslint-plugin-filenames": "^1.1.0",
- "eslint-plugin-import": "^1.16.0",
- "eslint-plugin-jasmine": "^1.8.1",
- "eslint-plugin-jsx-a11y": "^2.2.3",
- "eslint-plugin-react": "^6.4.1",
+ "eslint-plugin-import": "^2.2.0",
+ "eslint-plugin-jasmine": "^2.1.0",
"istanbul": "^0.4.5"
}
}
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index d9a86346c81..0d1545040f1 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -4,7 +4,7 @@ describe AutocompleteController do
let!(:project) { create(:project) }
let!(:user) { create(:user) }
- context 'users and members' do
+ context 'GET users' do
let!(:user2) { create(:user) }
let!(:non_member) { create(:user) }
@@ -180,7 +180,7 @@ describe AutocompleteController do
end
end
- context 'projects' do
+ context 'GET projects' do
let(:authorized_project) { create(:project) }
let(:authorized_search_project) { create(:project, name: 'rugged') }
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 6fc6ea95e13..cffed987f6b 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -16,14 +16,6 @@ describe HelpController do
end
end
- context 'when url prefixed with help/' do
- it 'will be an absolute path' do
- stub_readme("[API](help/api/README.md)")
- get :index
- expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
- end
- end
-
context 'when url prefixed with help' do
it 'will be an absolute path' do
stub_readme("[API](helpful_hints/README.md)")
@@ -32,11 +24,11 @@ describe HelpController do
end
end
- context 'when url prefixed with /help/' do
+ context 'when url is an external link' do
it 'will not be changed' do
- stub_readme("[API](/help/api/README.md)")
+ stub_readme("[external](https://some.external.link)")
get :index
- expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
+ expect(assigns[:help_index]).to eq '[external](https://some.external.link)'
end
end
end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 52d13fb6f9e..3efef757ae2 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -36,4 +36,53 @@ describe Projects::BlobController do
end
end
end
+
+ describe 'PUT update' do
+ let(:default_params) do
+ {
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: 'master/CHANGELOG',
+ target_branch: 'master',
+ content: 'Added changes',
+ commit_message: 'Update CHANGELOG'
+ }
+ end
+
+ def blob_after_edit_path
+ namespace_project_blob_path(project.namespace, project, 'master/CHANGELOG')
+ end
+
+ it 'redirects to blob' do
+ put :update, default_params
+
+ expect(response).to redirect_to(blob_after_edit_path)
+ end
+
+ context '?from_merge_request_iid' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:mr_params) { default_params.merge(from_merge_request_iid: merge_request.iid) }
+
+ it 'redirects to MR diff' do
+ put :update, mr_params
+
+ after_edit_path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
+ file_anchor = "##{Digest::SHA1.hexdigest('CHANGELOG')}"
+ expect(response).to redirect_to(after_edit_path + file_anchor)
+ end
+
+ context "when user doesn't have access" do
+ before do
+ other_project = create(:empty_project)
+ merge_request.update!(source_project: other_project, target_project: other_project)
+ end
+
+ it "it redirect to blob" do
+ put :update, mr_params
+
+ expect(response).to redirect_to(blob_after_edit_path)
+ end
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index f7cf006efd6..b88586b8678 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -94,6 +94,24 @@ describe Projects::BranchesController do
branch_name: branch,
issue_iid: issue.iid
end
+
+ context 'without issue feature access' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ project.team.truncate
+ end
+
+ it "doesn't post a system note" do
+ expect(SystemNoteService).not_to receive(:new_issue_branch)
+
+ post :create,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ branch_name: branch,
+ issue_iid: issue.iid
+ end
+ end
end
end
diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb
index 936320a3709..193a3f6b5a3 100644
--- a/spec/controllers/projects/todo_controller_spec.rb
+++ b/spec/controllers/projects/todo_controller_spec.rb
@@ -4,7 +4,7 @@ describe Projects::TodosController do
include ApiHelpers
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, source_project: project) }
@@ -42,7 +42,7 @@ describe Projects::TodosController do
end
end
- context 'when not authorized' do
+ context 'when not authorized for project' do
it 'does not create todo for issue that user has no access to' do
sign_in(user)
expect do
@@ -60,6 +60,19 @@ describe Projects::TodosController do
expect(response).to have_http_status(302)
end
end
+
+ context 'when not authorized for issue' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ sign_in(user)
+ end
+
+ it "doesn't create todo" do
+ expect{ go }.not_to change { user.todos.count }
+ expect(response).to have_http_status(404)
+ end
+ end
end
end
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 2d762fdaa04..d76fe9f580f 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -3,6 +3,28 @@ require 'spec_helper'
describe SnippetsController do
let(:user) { create(:user) }
+ describe 'GET #new' do
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ end
+
+ it 'responds with status 200' do
+ get :new
+
+ expect(response).to have_http_status(200)
+ end
+ end
+
+ context 'when not signed in' do
+ it 'redirects to the sign in page' do
+ get :new
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+ end
+
describe 'GET #show' do
context 'when the personal snippet is private' do
let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index ac2a1ba5dff..1735791f644 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -7,26 +7,30 @@ FactoryGirl.define do
project factory: :empty_project
factory :ci_pipeline_without_jobs do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) }
+ after(:build) do |pipeline|
+ allow(pipeline).to receive(:ci_yaml_file) { YAML.dump({}) }
end
end
factory :ci_pipeline_with_one_job do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) }
- end
- end
-
- factory :ci_pipeline_with_two_job do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) }
+ after(:build) do |pipeline|
+ allow(pipeline).to receive(:ci_yaml_file) do
+ YAML.dump({ rspec: { script: "ls" } })
+ end
end
end
factory :ci_pipeline do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
+ transient { config nil }
+
+ after(:build) do |pipeline, evaluator|
+ allow(pipeline).to receive(:ci_yaml_file) do
+ if evaluator.config
+ YAML.dump(evaluator.config)
+ else
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
+ end
end
end
end
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index e2101b333e2..73d03837144 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -10,4 +10,28 @@ describe 'Help Pages', feature: true do
expect(page).to have_content("ssh-keygen -t rsa -C \"#{@user.email}\"")
end
end
+
+ describe 'Get the main help page' do
+ shared_examples_for 'help page' do
+ it 'prefixes links correctly' do
+ expect(page).to have_selector('div.documentation-index > ul a[href="/help/api/README.md"]')
+ end
+ end
+
+ context 'without a trailing slash' do
+ before do
+ visit help_path
+ end
+
+ it_behaves_like 'help page'
+ end
+
+ context 'with a trailing slash' do
+ before do
+ visit help_path + '/'
+ end
+
+ it_behaves_like 'help page'
+ end
+ end
end
diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb
index d14a1158b67..a6b841c0210 100644
--- a/spec/features/profiles/preferences_spec.rb
+++ b/spec/features/profiles/preferences_spec.rb
@@ -73,7 +73,7 @@ describe 'Profile > Preferences', feature: true do
expect(page.current_path).to eq starred_dashboard_projects_path
end
- click_link 'Your Projects'
+ click_link 'Your projects'
expect(page).not_to have_content("You don't have starred projects yet")
expect(page.current_path).to eq dashboard_projects_path
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
new file mode 100644
index 00000000000..a820d07ab3b
--- /dev/null
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+feature 'Editing file blob', feature: true, js: true do
+ include WaitForAjax
+
+ given(:user) { create(:user) }
+ given(:role) { :developer }
+ given(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
+ given(:project) { merge_request.target_project }
+
+ background do
+ login_as(user)
+ project.team << [user, role]
+ end
+
+ def edit_and_commit
+ wait_for_ajax
+ first('.file-actions').click_link 'Edit'
+ execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
+ click_button 'Commit Changes'
+ end
+
+ context 'from MR diff' do
+ before do
+ visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
+ edit_and_commit
+ end
+
+ scenario 'returns me to the mr' do
+ expect(page).to have_content(merge_request.title)
+ end
+ end
+
+ context 'from blob file path' do
+ before do
+ visit namespace_project_blob_path(project.namespace, project, '/feature/files/ruby/feature.rb')
+ edit_and_commit
+ end
+
+ scenario 'updates content' do
+ expect(page).to have_content 'successfully committed'
+ expect(page).to have_content 'NextFeature'
+ end
+ end
+end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index b6acc509342..1897c8119d2 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -5,19 +5,6 @@ describe "Internal Project Access", feature: true do
let(:project) { create(:project, :internal) }
- let(:owner) { project.owner }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
-
- before do
- project.team << [master, :master]
- project.team << [developer, :developer]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
- end
-
describe "Project should be internal" do
describe '#internal?' do
subject { project.internal? }
@@ -28,213 +15,213 @@ describe "Internal Project Access", feature: true do
describe "GET /:project_path" do
subject { namespace_project_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/tree/master" do
subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/commits/master" do
subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/commit/:sha" do
subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/compare" do
subject { namespace_project_compare_index_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/project_members" do
subject { namespace_project_project_members_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
end
describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit }
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/edit" do
subject { edit_namespace_project_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/deploy_keys" do
subject { namespace_project_deploy_keys_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/issues" do
subject { namespace_project_issues_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/issues/:id/edit" do
let(:issue) { create(:issue, project: project) }
subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/merge_requests" do
subject { namespace_project_merge_requests_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/branches" do
@@ -245,15 +232,15 @@ describe "Internal Project Access", feature: true do
allow_any_instance_of(Project).to receive(:branches).and_return([])
end
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/tags" do
@@ -264,58 +251,58 @@ describe "Internal Project Access", feature: true do
allow_any_instance_of(Project).to receive(:tags).and_return([])
end
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/pipelines" do
subject { namespace_project_pipelines_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/pipelines/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) }
subject { namespace_project_pipeline_path(project.namespace, project, pipeline) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/builds" do
@@ -324,29 +311,29 @@ describe "Internal Project Access", feature: true do
context "when allowed for public and internal" do
before { project.update(public_builds: true) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
context "when disallowed for public and internal" do
before { project.update(public_builds: false) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
@@ -358,73 +345,73 @@ describe "Internal Project Access", feature: true do
context "when allowed for public and internal" do
before { project.update(public_builds: true) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
context "when disallowed for public and internal" do
before { project.update(public_builds: false) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
describe "GET /:project_path/environments" do
subject { namespace_project_environments_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments/:id" do
let(:environment) { create(:environment, project: project) }
subject { namespace_project_environment_path(project.namespace, project, environment) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments/new" do
subject { new_namespace_project_environment_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/container_registry" do
@@ -435,14 +422,14 @@ describe "Internal Project Access", feature: true do
subject { namespace_project_container_registry_index_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index 79417c769a8..290ddb4c6dd 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -5,19 +5,6 @@ describe "Private Project Access", feature: true do
let(:project) { create(:project, :private) }
- let(:owner) { project.owner }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
-
- before do
- project.team << [master, :master]
- project.team << [developer, :developer]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
- end
-
describe "Project should be private" do
describe '#private?' do
subject { project.private? }
@@ -28,185 +15,185 @@ describe "Private Project Access", feature: true do
describe "GET /:project_path" do
subject { namespace_project_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/tree/master" do
subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/commits/master" do
subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/commit/:sha" do
subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/compare" do
subject { namespace_project_compare_index_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/project_members" do
subject { namespace_project_project_members_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit }
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore'))}
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/edit" do
subject { edit_namespace_project_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/deploy_keys" do
subject { namespace_project_deploy_keys_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/issues" do
subject { namespace_project_issues_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/issues/:id/edit" do
let(:issue) { create(:issue, project: project) }
subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/merge_requests" do
subject { namespace_project_merge_requests_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/branches" do
@@ -217,15 +204,15 @@ describe "Private Project Access", feature: true do
allow_any_instance_of(Project).to receive(:branches).and_return([])
end
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/tags" do
@@ -236,72 +223,72 @@ describe "Private Project Access", feature: true do
allow_any_instance_of(Project).to receive(:tags).and_return([])
end
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/pipelines" do
subject { namespace_project_pipelines_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/pipelines/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) }
subject { namespace_project_pipeline_path(project.namespace, project, pipeline) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/builds" do
subject { namespace_project_builds_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/builds/:id" do
@@ -309,58 +296,58 @@ describe "Private Project Access", feature: true do
let(:build) { create(:ci_build, pipeline: pipeline) }
subject { namespace_project_build_path(project.namespace, project, build.id) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments" do
subject { namespace_project_environments_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments/:id" do
let(:environment) { create(:environment, project: project) }
subject { namespace_project_environment_path(project.namespace, project, environment) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments/new" do
subject { new_namespace_project_environment_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/container_registry" do
@@ -371,14 +358,14 @@ describe "Private Project Access", feature: true do
subject { namespace_project_container_registry_index_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 985663e7c98..bed9e92fcb6 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -5,19 +5,6 @@ describe "Public Project Access", feature: true do
let(:project) { create(:project, :public) }
- let(:owner) { project.owner }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
-
- before do
- project.team << [master, :master]
- project.team << [developer, :developer]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
- end
-
describe "Project should be public" do
describe '#public?' do
subject { project.public? }
@@ -28,114 +15,114 @@ describe "Public Project Access", feature: true do
describe "GET /:project_path" do
subject { namespace_project_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/tree/master" do
subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/commits/master" do
subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/commit/:sha" do
subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/compare" do
subject { namespace_project_compare_index_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/project_members" do
subject { namespace_project_project_members_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :visitor }
- it { is_expected.to be_allowed_for :external }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:visitor) }
+ it { is_expected.to be_allowed_for(:external) }
end
describe "GET /:project_path/pipelines" do
subject { namespace_project_pipelines_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/pipelines/:id" do
let(:pipeline) { create(:ci_pipeline, project: project) }
subject { namespace_project_pipeline_path(project.namespace, project, pipeline) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/builds" do
@@ -144,29 +131,29 @@ describe "Public Project Access", feature: true do
context "when allowed for public" do
before { project.update(public_builds: true) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
context "when disallowed for public" do
before { project.update(public_builds: false) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
@@ -178,73 +165,73 @@ describe "Public Project Access", feature: true do
context "when allowed for public" do
before { project.update(public_builds: true) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
context "when disallowed for public" do
before { project.update(public_builds: false) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
describe "GET /:project_path/environments" do
subject { namespace_project_environments_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments/:id" do
let(:environment) { create(:environment, project: project) }
subject { namespace_project_environment_path(project.namespace, project, environment) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/environments/new" do
subject { new_namespace_project_environment_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/blob" do
@@ -252,127 +239,127 @@ describe "Public Project Access", feature: true do
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/edit" do
subject { edit_namespace_project_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/deploy_keys" do
subject { namespace_project_deploy_keys_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/issues" do
subject { namespace_project_issues_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/issues/:id/edit" do
let(:issue) { create(:issue, project: project) }
subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/merge_requests" do
subject { namespace_project_merge_requests_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/merge_requests/new" do
subject { new_namespace_project_merge_request_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/branches" do
@@ -383,15 +370,15 @@ describe "Public Project Access", feature: true do
allow_any_instance_of(Project).to receive(:branches).and_return([])
end
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/tags" do
@@ -402,29 +389,29 @@ describe "Public Project Access", feature: true do
allow_any_instance_of(Project).to receive(:tags).and_return([])
end
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/container_registry" do
@@ -435,14 +422,14 @@ describe "Public Project Access", feature: true do
subject { namespace_project_container_registry_index_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
end
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb
new file mode 100644
index 00000000000..cb95e7828db
--- /dev/null
+++ b/spec/features/snippets/create_snippet_spec.rb
@@ -0,0 +1,20 @@
+require 'rails_helper'
+
+feature 'Create Snippet', feature: true do
+ before do
+ login_as :user
+ visit new_snippet_path
+ end
+
+ scenario 'Authenticated user creates a snippet' do
+ fill_in 'personal_snippet_title', with: 'My Snippet Title'
+ page.within('.file-editor') do
+ find(:xpath, "//input[@id='personal_snippet_content']").set 'Hello World!'
+ end
+
+ click_button 'Create snippet'
+
+ expect(page).to have_content('My Snippet Title')
+ expect(page).to have_content('Hello World!')
+ end
+end
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index d7880d5778f..ff30ffd7820 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -29,6 +29,31 @@ describe 'Project variables', js: true do
end
end
+ it 'reveals and hides new variable' do
+ fill_in('variable_key', with: 'key')
+ fill_in('variable_value', with: 'key value')
+ click_button('Add new variable')
+
+ page.within('.variables-table') do
+ expect(page).to have_content('key')
+ expect(page).to have_content('******')
+ end
+
+ click_button('Reveal Values')
+
+ page.within('.variables-table') do
+ expect(page).to have_content('key')
+ expect(page).to have_content('key value')
+ end
+
+ click_button('Hide Values')
+
+ page.within('.variables-table') do
+ expect(page).to have_content('key')
+ expect(page).to have_content('******')
+ end
+ end
+
it 'deletes variable' do
page.within('.variables-table') do
find('.btn-variable-delete').click
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 62cc10f579a..a4f08dc4af0 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -114,4 +114,25 @@ describe IssuablesHelper do
end
end
end
+
+ describe '#issuable_filter_present?' do
+ it 'returns true when any key is present' do
+ allow(helper).to receive(:params).and_return(
+ ActionController::Parameters.new(milestone_title: 'Velit consectetur asperiores natus delectus.',
+ project_id: 'gitlabhq',
+ scope: 'all')
+ )
+
+ expect(helper.issuable_filter_present?).to be_truthy
+ end
+
+ it 'returns false when no key is present' do
+ allow(helper).to receive(:params).and_return(
+ ActionController::Parameters.new(project_id: 'gitlabhq',
+ scope: 'all')
+ )
+
+ expect(helper.issuable_filter_present?).to be_falsey
+ end
+ end
end
diff --git a/spec/javascripts/build_spec.js.es6 b/spec/javascripts/build_spec.js.es6
index 4208e076e96..3983cad4c13 100644
--- a/spec/javascripts/build_spec.js.es6
+++ b/spec/javascripts/build_spec.js.es6
@@ -8,184 +8,177 @@
//= require jquery.nicescroll
//= require turbolinks
-(() => {
- describe('Build', () => {
- fixture.preload('build.html');
+describe('Build', () => {
+ const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/builds/1`;
+ // see spec/factories/ci/builds.rb
+ const BUILD_TRACE = 'BUILD TRACE';
+ // see lib/ci/ansi2html.rb
+ const INITIAL_BUILD_TRACE_STATE = window.btoa(JSON.stringify({
+ offset: BUILD_TRACE.length, n_open_tags: 0, fg_color: null, bg_color: null, style_mask: 0,
+ }));
+
+ fixture.preload('builds/build-with-artifacts.html.raw');
+
+ beforeEach(() => {
+ fixture.load('builds/build-with-artifacts.html.raw');
+ spyOn($, 'ajax');
+ });
+
+ describe('constructor', () => {
+ beforeEach(() => {
+ jasmine.clock().install();
+ });
- beforeEach(function () {
- fixture.load('build.html');
- spyOn($, 'ajax');
+ afterEach(() => {
+ jasmine.clock().uninstall();
});
- describe('constructor', () => {
+ describe('setup', () => {
beforeEach(function () {
- jasmine.clock().install();
+ this.build = new Build();
});
- afterEach(() => {
- jasmine.clock().uninstall();
+ it('copies build options', function () {
+ expect(this.build.pageUrl).toBe(BUILD_URL);
+ expect(this.build.buildUrl).toBe(`${BUILD_URL}.json`);
+ expect(this.build.buildStatus).toBe('success');
+ expect(this.build.buildStage).toBe('test');
+ expect(this.build.state).toBe(INITIAL_BUILD_TRACE_STATE);
});
- describe('setup', function () {
- const removeDate = new Date();
- removeDate.setUTCFullYear(removeDate.getUTCFullYear() + 1);
- // give the test three days to run
- removeDate.setTime(removeDate.getTime() + (3 * 24 * 60 * 60 * 1000));
+ it('only shows the jobs matching the current stage', () => {
+ expect($('.build-job[data-stage="build"]').is(':visible')).toBe(false);
+ expect($('.build-job[data-stage="test"]').is(':visible')).toBe(true);
+ expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
+ });
- beforeEach(function () {
- const removeDateElement = document.querySelector('.js-artifacts-remove');
- removeDateElement.innerText = removeDate.toString();
+ it('selects the current stage in the build dropdown menu', () => {
+ expect($('.stage-selection').text()).toBe('test');
+ });
- this.build = new Build();
- });
+ it('updates the jobs when the build dropdown changes', () => {
+ $('.stage-item:contains("build")').click();
- it('copies build options', function () {
- expect(this.build.pageUrl).toBe('http://example.com/root/test-build/builds/2');
- expect(this.build.buildUrl).toBe('http://example.com/root/test-build/builds/2.json');
- expect(this.build.buildStatus).toBe('passed');
- expect(this.build.buildStage).toBe('test');
- expect(this.build.state).toBe('buildstate');
- });
+ expect($('.stage-selection').text()).toBe('build');
+ expect($('.build-job[data-stage="build"]').is(':visible')).toBe(true);
+ expect($('.build-job[data-stage="test"]').is(':visible')).toBe(false);
+ expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
+ });
- it('only shows the jobs matching the current stage', function () {
- expect($('.build-job[data-stage="build"]').is(':visible')).toBe(false);
- expect($('.build-job[data-stage="test"]').is(':visible')).toBe(true);
- expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
- });
+ it('displays the remove date correctly', () => {
+ const removeDateElement = document.querySelector('.js-artifacts-remove');
+ expect(removeDateElement.innerText.trim()).toBe('1 year');
+ });
+ });
- it('selects the current stage in the build dropdown menu', function () {
- expect($('.stage-selection').text()).toBe('test');
- });
+ describe('initial build trace', () => {
+ beforeEach(() => {
+ new Build();
+ });
- it('updates the jobs when the build dropdown changes', function () {
- $('.stage-item:contains("build")').click();
+ it('displays the initial build trace', () => {
+ expect($.ajax.calls.count()).toBe(1);
+ const [{ url, dataType, success, context }] = $.ajax.calls.argsFor(0);
+ expect(url).toBe(`${BUILD_URL}.json`);
+ expect(dataType).toBe('json');
+ expect(success).toEqual(jasmine.any(Function));
- expect($('.stage-selection').text()).toBe('build');
- expect($('.build-job[data-stage="build"]').is(':visible')).toBe(true);
- expect($('.build-job[data-stage="test"]').is(':visible')).toBe(false);
- expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
- });
+ success.call(context, { trace_html: '<span>Example</span>', status: 'running' });
- it('displays the remove date correctly', function () {
- const removeDateElement = document.querySelector('.js-artifacts-remove');
- expect(removeDateElement.innerText.trim()).toBe('1 year');
- });
+ expect($('#build-trace .js-build-output').text()).toMatch(/Example/);
});
- describe('initial build trace', function () {
- beforeEach(function () {
- new Build();
- });
+ it('removes the spinner', () => {
+ const [{ success, context }] = $.ajax.calls.argsFor(0);
+ success.call(context, { trace_html: '<span>Example</span>', status: 'success' });
- it('displays the initial build trace', function () {
- expect($.ajax.calls.count()).toBe(1);
- const [{ url, dataType, success, context }] = $.ajax.calls.argsFor(0);
- expect(url).toBe('http://example.com/root/test-build/builds/2.json');
- expect(dataType).toBe('json');
- expect(success).toEqual(jasmine.any(Function));
+ expect($('.js-build-refresh').length).toBe(0);
+ });
+ });
- success.call(context, { trace_html: '<span>Example</span>', status: 'running' });
+ describe('running build', () => {
+ beforeEach(function () {
+ $('.js-build-options').data('buildStatus', 'running');
+ this.build = new Build();
+ spyOn(this.build, 'location').and.returnValue(BUILD_URL);
+ });
- expect($('#build-trace .js-build-output').text()).toMatch(/Example/);
+ it('updates the build trace on an interval', function () {
+ jasmine.clock().tick(4001);
+
+ expect($.ajax.calls.count()).toBe(2);
+ let [{ url, dataType, success, context }] = $.ajax.calls.argsFor(1);
+ expect(url).toBe(
+ `${BUILD_URL}/trace.json?state=${encodeURIComponent(INITIAL_BUILD_TRACE_STATE)}`,
+ );
+ expect(dataType).toBe('json');
+ expect(success).toEqual(jasmine.any(Function));
+
+ success.call(context, {
+ html: '<span>Update<span>',
+ status: 'running',
+ state: 'newstate',
+ append: true,
});
- it('removes the spinner', function () {
- const [{ success, context }] = $.ajax.calls.argsFor(0);
- success.call(context, { trace_html: '<span>Example</span>', status: 'success' });
+ expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
+ expect(this.build.state).toBe('newstate');
- expect($('.js-build-refresh').length).toBe(0);
+ jasmine.clock().tick(4001);
+
+ expect($.ajax.calls.count()).toBe(3);
+ [{ url, dataType, success, context }] = $.ajax.calls.argsFor(2);
+ expect(url).toBe(`${BUILD_URL}/trace.json?state=newstate`);
+ expect(dataType).toBe('json');
+ expect(success).toEqual(jasmine.any(Function));
+
+ success.call(context, {
+ html: '<span>More</span>',
+ status: 'running',
+ state: 'finalstate',
+ append: true,
});
+
+ expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
+ expect(this.build.state).toBe('finalstate');
});
- describe('running build', function () {
- beforeEach(function () {
- $('.js-build-options').data('buildStatus', 'running');
- this.build = new Build();
- spyOn(this.build, 'location')
- .and.returnValue('http://example.com/root/test-build/builds/2');
+ it('replaces the entire build trace', () => {
+ jasmine.clock().tick(4001);
+ let [{ success, context }] = $.ajax.calls.argsFor(1);
+ success.call(context, {
+ html: '<span>Update</span>',
+ status: 'running',
+ append: true,
});
- it('updates the build trace on an interval', function () {
- jasmine.clock().tick(4001);
-
- expect($.ajax.calls.count()).toBe(2);
- let [{ url, dataType, success, context }] = $.ajax.calls.argsFor(1);
- expect(url).toBe(
- 'http://example.com/root/test-build/builds/2/trace.json?state=buildstate'
- );
- expect(dataType).toBe('json');
- expect(success).toEqual(jasmine.any(Function));
-
- success.call(context, {
- html: '<span>Update<span>',
- status: 'running',
- state: 'newstate',
- append: true,
- });
-
- expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
- expect(this.build.state).toBe('newstate');
-
- jasmine.clock().tick(4001);
-
- expect($.ajax.calls.count()).toBe(3);
- [{ url, dataType, success, context }] = $.ajax.calls.argsFor(2);
- expect(url).toBe(
- 'http://example.com/root/test-build/builds/2/trace.json?state=newstate'
- );
- expect(dataType).toBe('json');
- expect(success).toEqual(jasmine.any(Function));
-
- success.call(context, {
- html: '<span>More</span>',
- status: 'running',
- state: 'finalstate',
- append: true,
- });
-
- expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/);
- expect(this.build.state).toBe('finalstate');
- });
+ expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
- it('replaces the entire build trace', function () {
- jasmine.clock().tick(4001);
- let [{ success, context }] = $.ajax.calls.argsFor(1);
- success.call(context, {
- html: '<span>Update</span>',
- status: 'running',
- append: true,
- });
-
- expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
-
- jasmine.clock().tick(4001);
- [{ success, context }] = $.ajax.calls.argsFor(2);
- success.call(context, {
- html: '<span>Different</span>',
- status: 'running',
- append: false,
- });
-
- expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/);
- expect($('#build-trace .js-build-output').text()).toMatch(/Different/);
+ jasmine.clock().tick(4001);
+ [{ success, context }] = $.ajax.calls.argsFor(2);
+ success.call(context, {
+ html: '<span>Different</span>',
+ status: 'running',
+ append: false,
});
- it('reloads the page when the build is done', function () {
- spyOn(Turbolinks, 'visit');
+ expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/);
+ expect($('#build-trace .js-build-output').text()).toMatch(/Different/);
+ });
- jasmine.clock().tick(4001);
- const [{ success, context }] = $.ajax.calls.argsFor(1);
- success.call(context, {
- html: '<span>Final</span>',
- status: 'passed',
- append: true,
- });
+ it('reloads the page when the build is done', () => {
+ spyOn(Turbolinks, 'visit');
- expect(Turbolinks.visit).toHaveBeenCalledWith(
- 'http://example.com/root/test-build/builds/2'
- );
+ jasmine.clock().tick(4001);
+ const [{ success, context }] = $.ajax.calls.argsFor(1);
+ success.call(context, {
+ html: '<span>Final</span>',
+ status: 'passed',
+ append: true,
});
+
+ expect(Turbolinks.visit).toHaveBeenCalledWith(BUILD_URL);
});
});
});
-})();
+});
diff --git a/spec/javascripts/environments/environment_actions_spec.js.es6 b/spec/javascripts/environments/environment_actions_spec.js.es6
index c9ac7a73fd0..76e81233e89 100644
--- a/spec/javascripts/environments/environment_actions_spec.js.es6
+++ b/spec/javascripts/environments/environment_actions_spec.js.es6
@@ -28,10 +28,10 @@ describe('Actions Component', () => {
});
expect(
- component.$el.querySelectorAll('.dropdown-menu li').length
+ component.$el.querySelectorAll('.dropdown-menu li').length,
).toEqual(actionsMock.length);
expect(
- component.$el.querySelector('.dropdown-menu li a').getAttribute('href')
+ component.$el.querySelector('.dropdown-menu li a').getAttribute('href'),
).toEqual(actionsMock[0].play_path);
});
});
diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6
index 3c15e3b7719..14e90a9dd1b 100644
--- a/spec/javascripts/environments/environment_item_spec.js.es6
+++ b/spec/javascripts/environments/environment_item_spec.js.es6
@@ -141,18 +141,18 @@ describe('Environment item', () => {
describe('With deployment', () => {
it('should render deployment internal id', () => {
expect(
- component.$el.querySelector('.deployment-column span').textContent
+ component.$el.querySelector('.deployment-column span').textContent,
).toContain(environment.last_deployment.iid);
expect(
- component.$el.querySelector('.deployment-column span').textContent
+ component.$el.querySelector('.deployment-column span').textContent,
).toContain('#');
});
describe('With user information', () => {
it('should render user avatar with link to profile', () => {
expect(
- component.$el.querySelector('.js-deploy-user-container').getAttribute('href')
+ component.$el.querySelector('.js-deploy-user-container').getAttribute('href'),
).toEqual(environment.last_deployment.user.web_url);
});
});
@@ -160,13 +160,13 @@ describe('Environment item', () => {
describe('With build url', () => {
it('Should link to build url provided', () => {
expect(
- component.$el.querySelector('.build-link').getAttribute('href')
+ component.$el.querySelector('.build-link').getAttribute('href'),
).toEqual(environment.last_deployment.deployable.build_path);
});
it('Should render deployable name and id', () => {
expect(
- component.$el.querySelector('.build-link').getAttribute('href')
+ component.$el.querySelector('.build-link').getAttribute('href'),
).toEqual(environment.last_deployment.deployable.build_path);
});
});
@@ -174,7 +174,7 @@ describe('Environment item', () => {
describe('With commit information', () => {
it('should render commit component', () => {
expect(
- component.$el.querySelector('.js-commit-component')
+ component.$el.querySelector('.js-commit-component'),
).toBeDefined();
});
});
@@ -183,7 +183,7 @@ describe('Environment item', () => {
describe('With manual actions', () => {
it('Should render actions component', () => {
expect(
- component.$el.querySelector('.js-manual-actions-container')
+ component.$el.querySelector('.js-manual-actions-container'),
).toBeDefined();
});
});
@@ -191,7 +191,7 @@ describe('Environment item', () => {
describe('With external URL', () => {
it('should render external url component', () => {
expect(
- component.$el.querySelector('.js-external-url-container')
+ component.$el.querySelector('.js-external-url-container'),
).toBeDefined();
});
});
@@ -199,7 +199,7 @@ describe('Environment item', () => {
describe('With stop action', () => {
it('Should render stop action component', () => {
expect(
- component.$el.querySelector('.js-stop-component-container')
+ component.$el.querySelector('.js-stop-component-container'),
).toBeDefined();
});
});
@@ -207,7 +207,7 @@ describe('Environment item', () => {
describe('With retry action', () => {
it('Should render rollback component', () => {
expect(
- component.$el.querySelector('.js-rollback-component-container')
+ component.$el.querySelector('.js-rollback-component-container'),
).toBeDefined();
});
});
diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6
index 9b0b3cb1c65..17c00acf63e 100644
--- a/spec/javascripts/environments/environments_store_spec.js.es6
+++ b/spec/javascripts/environments/environments_store_spec.js.es6
@@ -5,11 +5,11 @@
//= require ./mock_data
(() => {
- beforeEach(() => {
- gl.environmentsList.EnvironmentsStore.create();
- });
-
describe('Store', () => {
+ beforeEach(() => {
+ gl.environmentsList.EnvironmentsStore.create();
+ });
+
it('should start with a blank state', () => {
expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(0);
expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(0);
diff --git a/spec/javascripts/extensions/array_spec.js b/spec/javascripts/extensions/array_spec.js
deleted file mode 100644
index c56e6c7789b..00000000000
--- a/spec/javascripts/extensions/array_spec.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable space-before-function-paren, no-var, padded-blocks */
-
-/*= require extensions/array */
-
-(function() {
- describe('Array extensions', function() {
- describe('first', function() {
- return it('returns the first item', function() {
- var arr;
- arr = [0, 1, 2, 3, 4, 5];
- return expect(arr.first()).toBe(0);
- });
- });
- return describe('last', function() {
- return it('returns the last item', function() {
- var arr;
- arr = [0, 1, 2, 3, 4, 5];
- return expect(arr.last()).toBe(5);
- });
- });
- });
-
-}).call(this);
diff --git a/spec/javascripts/extensions/array_spec.js.es6 b/spec/javascripts/extensions/array_spec.js.es6
new file mode 100644
index 00000000000..2ec759c8e80
--- /dev/null
+++ b/spec/javascripts/extensions/array_spec.js.es6
@@ -0,0 +1,46 @@
+/* eslint-disable space-before-function-paren, no-var, padded-blocks */
+
+/*= require extensions/array */
+
+(function() {
+ describe('Array extensions', function() {
+ describe('first', function() {
+ return it('returns the first item', function() {
+ var arr;
+ arr = [0, 1, 2, 3, 4, 5];
+ return expect(arr.first()).toBe(0);
+ });
+ });
+ describe('last', function() {
+ return it('returns the last item', function() {
+ var arr;
+ arr = [0, 1, 2, 3, 4, 5];
+ return expect(arr.last()).toBe(5);
+ });
+ });
+
+ describe('find', function () {
+ beforeEach(() => {
+ this.arr = [0, 1, 2, 3, 4, 5];
+ });
+
+ it('returns the item that first passes the predicate function', () => {
+ expect(this.arr.find(item => item === 2)).toBe(2);
+ });
+
+ it('returns undefined if no items pass the predicate function', () => {
+ expect(this.arr.find(item => item === 6)).not.toBeDefined();
+ });
+
+ it('error when called on undefined or null', () => {
+ expect(Array.prototype.find.bind(undefined, item => item === 1)).toThrow();
+ expect(Array.prototype.find.bind(null, item => item === 1)).toThrow();
+ });
+
+ it('error when predicate is not a function', () => {
+ expect(Array.prototype.find.bind(this.arr, 1)).toThrow();
+ });
+ });
+ });
+
+}).call(this);
diff --git a/spec/javascripts/fixtures/build.html.haml b/spec/javascripts/fixtures/build.html.haml
deleted file mode 100644
index 06b49516e5c..00000000000
--- a/spec/javascripts/fixtures/build.html.haml
+++ /dev/null
@@ -1,62 +0,0 @@
-.build-page
- .prepend-top-default
- .autoscroll-container
- %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
- #js-build-scroll.scroll-controls
- %a.btn{href: '#build-trace'}
- %i.fa.fa-angle-up
- %a.btn{href: '#down-build-trace'}
- %i.fa.fa-angle-down
- %pre.build-trace#build-trace
- %code.bash.js-build-output
- %i.fa.fa-refresh.fa-spin.js-build-refresh
-
-%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
- .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
- Build
- %strong #1
- %a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
- %i.fa.fa-angle-double-right
- .blocks-container
- .dropdown.build-dropdown
- .title Stage
- %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
- %span.stage-selection More
- %i.fa.fa-caret-down
- %ul.dropdown-menu
- %li
- %a.stage-item build
- %li
- %a.stage-item test
- %li
- %a.stage-item deploy
- .builds-container
- .build-job{data: {stage: 'build'}}
- %a{href: 'http://example.com/root/test-build/builds/1'}
- %i.fa.fa-check
- %i.fa.fa-check-circle-o
- %span
- Setup
- .build-job{data: {stage: 'test'}}
- %a{href: 'http://example.com/root/test-build/builds/2'}
- %i.fa.fa-check
- %i.fa.fa-check-circle-o
- %span
- Tests
- .build-job{data: {stage: 'deploy'}}
- %a{href: 'http://example.com/root/test-build/builds/3'}
- %i.fa.fa-check
- %i.fa.fa-check-circle-o
- %span
- Deploy
-
-.js-build-options{ data: { page_url: 'http://example.com/root/test-build/builds/2',
- build_url: 'http://example.com/root/test-build/builds/2.json',
- build_status: 'passed',
- build_stage: 'test',
- log_state: 'buildstate' }}
-
-%p.build-detail-row
- The artifacts will be removed in
- %span.js-artifacts-remove
- 2016-12-19 09:02:12 UTC
diff --git a/spec/javascripts/fixtures/builds.rb b/spec/javascripts/fixtures/builds.rb
new file mode 100644
index 00000000000..978e25a1c32
--- /dev/null
+++ b/spec/javascripts/fixtures/builds.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Projects::BuildsController, '(JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ let(:admin) { create(:admin) }
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:project) { create(:project_empty_repo, namespace: namespace, path: 'builds-project') }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let!(:build_with_artifacts) { create(:ci_build, :success, :artifacts, :trace, pipeline: pipeline, stage: 'test', artifacts_expire_at: Time.now + 18.months) }
+ let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline, stage: 'build') }
+ let!(:pending_build) { create(:ci_build, :pending, pipeline: pipeline, stage: 'deploy') }
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('builds/')
+ end
+
+ before(:each) do
+ sign_in(admin)
+ end
+
+ it 'builds/build-with-artifacts.html.raw' do |example|
+ get :show,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: build_with_artifacts.to_param
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/fixtures/issues.rb b/spec/javascripts/fixtures/issues.rb
index d95eb851421..c10784fe5ae 100644
--- a/spec/javascripts/fixtures/issues.rb
+++ b/spec/javascripts/fixtures/issues.rb
@@ -4,7 +4,8 @@ describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller
include JavaScriptFixturesHelpers
let(:admin) { create(:admin) }
- let(:project) { create(:project_empty_repo) }
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') }
render_views
diff --git a/spec/javascripts/fixtures/zen_mode.html.haml b/spec/javascripts/fixtures/zen_mode.html.haml
deleted file mode 100644
index cb906a7feaa..00000000000
--- a/spec/javascripts/fixtures/zen_mode.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.md-area
- .zen-backdrop
- %textarea#note_note.js-gfm-input.markdown-area
- %a.js-zen-enter(tabindex="-1" href="#")
- %i.fa.fa-expand
- Edit in fullscreen
- %a.js-zen-leave(tabindex="-1" href="#")
- %i.fa.fa-compress
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index beef46122ab..14af6644de1 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -74,7 +74,7 @@
it('submits an ajax request on tasklist:changed', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expect(req.type).toBe('PATCH');
- expect(req.url).toBe('https://fixture.invalid/namespace3/project3/issues/1.json');
+ expect(req.url).toBe(gl.TEST_HOST + '/frontend-fixtures/issues-project/issues/1.json'); // eslint-disable-line prefer-template
expect(req.data.issue.description).not.toBe(null);
});
diff --git a/spec/javascripts/smart_interval_spec.js.es6 b/spec/javascripts/smart_interval_spec.js.es6
index 651d1f0f975..ed6166a25a8 100644
--- a/spec/javascripts/smart_interval_spec.js.es6
+++ b/spec/javascripts/smart_interval_spec.js.es6
@@ -37,7 +37,7 @@
const intervalConfig = this.smartInterval.cfg;
const iterationCount = 4;
const maxIntervalAfterIterations = intervalConfig.startingInterval *
- Math.pow(intervalConfig.incrementByFactorOf, (iterationCount - 1)); // 40
+ (intervalConfig.incrementByFactorOf ** (iterationCount - 1)); // 40
const currentInterval = interval.getCurrentInterval();
// Provide some flexibility for performance of testing environment
diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js
index 8a64de4dd43..831dfada952 100644
--- a/spec/javascripts/spec_helper.js
+++ b/spec/javascripts/spec_helper.js
@@ -41,3 +41,8 @@
}).call(this);
+
+// defined in ActionDispatch::TestRequest
+// see https://github.com/rails/rails/blob/v4.2.7.1/actionpack/lib/action_dispatch/testing/test_request.rb#L7
+window.gl = window.gl || {};
+gl.TEST_HOST = 'http://test.host';
diff --git a/spec/javascripts/vue_common_components/commit_spec.js.es6 b/spec/javascripts/vue_common_components/commit_spec.js.es6
index 0e3b82967c1..b1dbc8bd5fa 100644
--- a/spec/javascripts/vue_common_components/commit_spec.js.es6
+++ b/spec/javascripts/vue_common_components/commit_spec.js.es6
@@ -74,26 +74,26 @@ describe('Commit component', () => {
describe('Given commit title and author props', () => {
it('Should render a link to the author profile', () => {
expect(
- component.$el.querySelector('.commit-title .avatar-image-container').getAttribute('href')
+ component.$el.querySelector('.commit-title .avatar-image-container').getAttribute('href'),
).toEqual(props.author.web_url);
});
it('Should render the author avatar with title and alt attributes', () => {
expect(
- component.$el.querySelector('.commit-title .avatar-image-container img').getAttribute('title')
+ component.$el.querySelector('.commit-title .avatar-image-container img').getAttribute('title'),
).toContain(props.author.username);
expect(
- component.$el.querySelector('.commit-title .avatar-image-container img').getAttribute('alt')
+ component.$el.querySelector('.commit-title .avatar-image-container img').getAttribute('alt'),
).toContain(`${props.author.username}'s avatar`);
});
});
it('should render the commit title', () => {
expect(
- component.$el.querySelector('a.commit-row-message').getAttribute('href')
+ component.$el.querySelector('a.commit-row-message').getAttribute('href'),
).toEqual(props.commit_url);
expect(
- component.$el.querySelector('a.commit-row-message').textContent
+ component.$el.querySelector('a.commit-row-message').textContent,
).toContain(props.title);
});
});
@@ -119,7 +119,7 @@ describe('Commit component', () => {
});
expect(
- component.$el.querySelector('.commit-title span').textContent
+ component.$el.querySelector('.commit-title span').textContent,
).toContain('Cant find HEAD commit for this branch');
});
});
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index a18e8aee9b1..b9acaaa5a0d 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -6,9 +6,10 @@
var enterZen, escapeKeydown, exitZen;
describe('ZenMode', function() {
- fixture.preload('zen_mode.html');
+ var fixtureName = 'issues/open-issue.html.raw';
+ fixture.preload(fixtureName);
beforeEach(function() {
- fixture.load('zen_mode.html');
+ fixture.load(fixtureName);
spyOn(Dropzone, 'forElement').and.callFake(function() {
return {
enable: function() {
@@ -60,11 +61,11 @@
});
enterZen = function() {
- return $('a.js-zen-enter').click();
+ return $('.js-zen-enter').click();
};
exitZen = function() { // Ohmmmmmmm
- return $('a.js-zen-leave').click();
+ return $('.js-zen-leave').click();
};
escapeKeydown = function() {
diff --git a/spec/lib/gitlab/chat_commands/issue_search_spec.rb b/spec/lib/gitlab/chat_commands/issue_search_spec.rb
new file mode 100644
index 00000000000..24c06a967fa
--- /dev/null
+++ b/spec/lib/gitlab/chat_commands/issue_search_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::ChatCommands::IssueSearch, service: true do
+ describe '#execute' do
+ let!(:issue) { create(:issue, title: 'find me') }
+ let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ let(:regex_match) { described_class.match("issue search find") }
+
+ subject do
+ described_class.new(project, user).execute(regex_match)
+ end
+
+ context 'when the user has no access' do
+ it 'only returns the open issues' do
+ expect(subject).not_to include(confidential)
+ end
+ end
+
+ context 'the user has access' do
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'returns all results' do
+ expect(subject).to include(confidential, issue)
+ end
+ end
+
+ context 'without hits on the query' do
+ it 'returns an empty collection' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+
+ describe 'self.match' do
+ let(:query) { "my search keywords" }
+ it 'matches the query' do
+ match = described_class.match("issue search #{query}")
+
+ expect(match[:query]).to eq(query)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_commands/issue_show_spec.rb b/spec/lib/gitlab/chat_commands/issue_show_spec.rb
index 331a4604e9b..2eab73e49e5 100644
--- a/spec/lib/gitlab/chat_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/chat_commands/issue_show_spec.rb
@@ -19,6 +19,14 @@ describe Gitlab::ChatCommands::IssueShow, service: true do
it 'returns the issue' do
expect(subject.iid).to be issue.iid
end
+
+ context 'when its reference is given' do
+ let(:regex_match) { described_class.match("issue show #{issue.to_reference}") }
+
+ it 'shows the issue' do
+ expect(subject.iid).to be issue.iid
+ end
+ end
end
context 'the issue does not exist' do
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
new file mode 100644
index 00000000000..c863a5f04cc
--- /dev/null
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -0,0 +1,13 @@
+require 'spec_helper'
+
+describe Gitlab::Diff::FileCollection::MergeRequestDiff do
+ let(:merge_request) { create :merge_request }
+
+ it 'does not hightlight binary files' do
+ allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => false))
+
+ expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+
+ described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
+ end
+end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index a0fdad87eee..3cd9863ec6a 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -65,6 +65,14 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
end
+ it 'does not list issues on private projects' do
+ issue = create(:issue, project: project)
+
+ results = described_class.new(user, project, issue.title)
+
+ expect(results.objects('issues')).not_to include issue
+ end
+
describe 'confidential issues' do
let(:query) { 'issue' }
let(:author) { create(:user) }
@@ -72,6 +80,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
let(:non_member) { create(:user) }
let(:member) { create(:user) }
let(:admin) { create(:admin) }
+ let(:project) { create(:empty_project, :internal) }
let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignee: assignee) }
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index dfbefad6367..f23e3522625 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -12,35 +12,48 @@ describe Gitlab::SearchResults do
let!(:milestone) { create(:milestone, project: project, title: 'foo') }
let(:results) { described_class.new(user, Project.all, 'foo') }
- describe '#projects_count' do
- it 'returns the total amount of projects' do
- expect(results.projects_count).to eq(1)
+ context 'as a user with access' do
+ before do
+ project.team << [user, :developer]
end
- end
- describe '#issues_count' do
- it 'returns the total amount of issues' do
- expect(results.issues_count).to eq(1)
+ describe '#projects_count' do
+ it 'returns the total amount of projects' do
+ expect(results.projects_count).to eq(1)
+ end
end
- end
- describe '#merge_requests_count' do
- it 'returns the total amount of merge requests' do
- expect(results.merge_requests_count).to eq(1)
+ describe '#issues_count' do
+ it 'returns the total amount of issues' do
+ expect(results.issues_count).to eq(1)
+ end
+ end
+
+ describe '#merge_requests_count' do
+ it 'returns the total amount of merge requests' do
+ expect(results.merge_requests_count).to eq(1)
+ end
end
- end
- describe '#milestones_count' do
- it 'returns the total amount of milestones' do
- expect(results.milestones_count).to eq(1)
+ describe '#milestones_count' do
+ it 'returns the total amount of milestones' do
+ expect(results.milestones_count).to eq(1)
+ end
end
end
+ it 'does not list issues on private projects' do
+ private_project = create(:empty_project, :private)
+ issue = create(:issue, project: private_project, title: 'foo')
+
+ expect(results.objects('issues')).not_to include issue
+ end
+
describe 'confidential issues' do
- let(:project_1) { create(:empty_project) }
- let(:project_2) { create(:empty_project) }
- let(:project_3) { create(:empty_project) }
- let(:project_4) { create(:empty_project) }
+ let(:project_1) { create(:empty_project, :internal) }
+ let(:project_2) { create(:empty_project, :internal) }
+ let(:project_3) { create(:empty_project, :internal) }
+ let(:project_4) { create(:empty_project, :internal) }
let(:query) { 'issue' }
let(:limit_projects) { Project.where(id: [project_1.id, project_2.id, project_3.id]) }
let(:author) { create(:user) }
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 932a5dc4862..b692142713f 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -50,7 +50,7 @@ describe Notify do
context 'when enabled email_author_in_body' do
before do
- allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
+ stub_application_setting(email_author_in_body: true)
end
it 'contains a link to note author' do
@@ -229,7 +229,7 @@ describe Notify do
context 'when enabled email_author_in_body' do
before do
- allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
+ stub_application_setting(email_author_in_body: true)
end
it 'contains a link to note author' do
@@ -607,7 +607,7 @@ describe Notify do
context 'when enabled email_author_in_body' do
before do
- allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
+ stub_application_setting(email_author_in_body: true)
end
it 'contains a link to note author' do
@@ -686,6 +686,79 @@ describe Notify do
end
end
end
+
+ context 'items that are noteable, emails for a note on a diff' do
+ let(:note_author) { create(:user, name: 'author_name') }
+
+ before :each do
+ allow(Note).to receive(:find).with(note.id).and_return(note)
+ end
+
+ shared_examples 'a note email on a diff' do |model|
+ let(:note) { create(model, project: project, author: note_author) }
+
+ it "includes diffs with character-level highlighting" do
+ is_expected.to have_body_text /<span class=\"p\">}<\/span><\/span>/
+ end
+
+ it 'contains a link to the diff file' do
+ is_expected.to have_body_text /#{note.diff_file.file_path}/
+ end
+
+ it_behaves_like 'it should have Gmail Actions links'
+
+ it 'is sent as the author' do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq(note_author.name)
+ expect(sender.address).to eq(gitlab_sender)
+ end
+
+ it 'is sent to the given recipient' do
+ is_expected.to deliver_to recipient.notification_email
+ end
+
+ it 'contains the message from the note' do
+ is_expected.to have_body_text /#{note.note}/
+ end
+
+ it 'does not contain note author' do
+ is_expected.not_to have_body_text /wrote\:/
+ end
+
+ context 'when enabled email_author_in_body' do
+ before do
+ stub_application_setting(email_author_in_body: true)
+ end
+
+ it 'contains a link to note author' do
+ is_expected.to have_body_text note.author_name
+ is_expected.to have_body_text /wrote\:/
+ end
+ end
+ end
+
+ describe 'on a commit' do
+ let(:commit) { project.commit }
+ let(:note) { create(:diff_note_on_commit) }
+
+ subject { Notify.note_commit_email(recipient.id, note.id) }
+
+ it_behaves_like 'a note email on a diff', :diff_note_on_commit
+ it_behaves_like 'it should show Gmail Actions View Commit link'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+ end
+
+ describe 'on a merge request' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:note) { create(:diff_note_on_merge_request) }
+
+ subject { Notify.note_merge_request_email(recipient.id, note.id) }
+
+ it_behaves_like 'a note email on a diff', :diff_note_on_merge_request
+ it_behaves_like 'it should show Gmail Actions View Merge request link'
+ it_behaves_like 'an unsubscribeable thread'
+ end
+ end
end
context 'for a group' do
@@ -1099,4 +1172,38 @@ describe Notify do
is_expected.to have_body_text /#{diff_path}/
end
end
+
+ describe 'HTML emails setting' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:multipart_mail) { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }
+
+ context 'when disabled' do
+ it 'only sends the text template' do
+ stub_application_setting(html_emails_enabled: false)
+
+ EmailTemplateInterceptor.delivering_email(multipart_mail)
+
+ expect(multipart_mail).to have_part_with('text/plain')
+ expect(multipart_mail).not_to have_part_with('text/html')
+ end
+ end
+
+ context 'when enabled' do
+ it 'sends a multipart message' do
+ stub_application_setting(html_emails_enabled: true)
+
+ EmailTemplateInterceptor.delivering_email(multipart_mail)
+
+ expect(multipart_mail).to have_part_with('text/plain')
+ expect(multipart_mail).to have_part_with('text/html')
+ end
+ end
+
+ matcher :have_part_with do |expected|
+ match do |actual|
+ actual.body.parts.any? { |part| part.content_type.try(:match, %r(#{expected})) }
+ end
+ end
+ end
end
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index 7691d690db0..7771785ead3 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#code', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
context 'with deployment' do
generate_cycle_analytics_spec(
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index f649b44d367..5ed3d37f2fb 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#issue', models: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :issue,
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index 2cdefbeef21..baf3e3241a1 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#plan', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :plan,
diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb
index 1f5e5cab92d..21b9c6e7150 100644
--- a/spec/models/cycle_analytics/production_spec.rb
+++ b/spec/models/cycle_analytics/production_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#production', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :production,
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index 0ed080a42b1..158621d59a4 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#review', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :review,
diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb
index af1c4477ddb..dad653964b7 100644
--- a/spec/models/cycle_analytics/staging_spec.rb
+++ b/spec/models/cycle_analytics/staging_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#staging', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :staging,
diff --git a/spec/models/cycle_analytics/summary_spec.rb b/spec/models/cycle_analytics/summary_spec.rb
index 9d67bc82cba..725bc68b25f 100644
--- a/spec/models/cycle_analytics/summary_spec.rb
+++ b/spec/models/cycle_analytics/summary_spec.rb
@@ -4,7 +4,7 @@ describe CycleAnalytics::Summary, models: true do
let(:project) { create(:project) }
let(:from) { Time.now }
let(:user) { create(:user, :admin) }
- subject { described_class.new(project, from: from) }
+ subject { described_class.new(project, user, from: from) }
describe "#new_issues" do
it "finds the number of issues created after the 'from date'" do
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index 02ddfeed9c1..2313724e8f3 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -6,7 +6,7 @@ describe 'CycleAnalytics#test', feature: true do
let(:project) { create(:project) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
- subject { CycleAnalytics.new(project, from: from_date) }
+ subject { CycleAnalytics.new(project, user, from: from_date) }
generate_cycle_analytics_spec(
phase: :test,
diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb
index 0142706d140..2a67c60b978 100644
--- a/spec/models/discussion_spec.rb
+++ b/spec/models/discussion_spec.rb
@@ -590,4 +590,23 @@ describe Discussion, model: true do
end
end
end
+
+ describe "#truncated_diff_lines" do
+ let(:truncated_lines) { subject.truncated_diff_lines }
+
+ context "when diff is greater than allowed number of truncated diff lines " do
+ it "returns fewer lines" do
+ expect(subject.diff_lines.count).to be > described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
+
+ expect(truncated_lines.count).to be <= described_class::NUMBER_OF_TRUNCATED_DIFF_LINES
+ end
+ end
+
+ context "when some diff lines are meta" do
+ it "returns no meta lines" do
+ expect(subject.diff_lines).to include(be_meta)
+ expect(truncated_lines).not_to include(be_meta)
+ end
+ end
+ end
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index b684053cd02..f8660da031d 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -260,6 +260,24 @@ describe Event, models: true do
end
end
+ describe '#authored_by?' do
+ let(:event) { build(:event) }
+
+ it 'returns true when the event author and user are the same' do
+ expect(event.authored_by?(event.author)).to eq(true)
+ end
+
+ it 'returns false when passing nil as an argument' do
+ expect(event.authored_by?(nil)).to eq(false)
+ end
+
+ it 'returns false when the given user is not the author of the event' do
+ user = double(:user, id: -1)
+
+ expect(event.authored_by?(user)).to eq(false)
+ end
+ end
+
def create_event(project, user, attrs = {})
data = {
before: Gitlab::Git::BLANK_SHA,
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 19baf238b8d..30cc91bf265 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -570,7 +570,7 @@ describe MergeRequest, models: true do
end
end
- describe '#pipeline' do
+ describe '#head_pipeline' do
describe 'when the source project exists' do
it 'returns the latest pipeline' do
pipeline = double(:ci_pipeline, ref: 'master')
@@ -581,7 +581,7 @@ describe MergeRequest, models: true do
with('master', '123abc').
and_return(pipeline)
- expect(subject.pipeline).to eq(pipeline)
+ expect(subject.head_pipeline).to eq(pipeline)
end
end
@@ -589,7 +589,7 @@ describe MergeRequest, models: true do
it 'returns nil' do
allow(subject).to receive(:source_project).and_return(nil)
- expect(subject.pipeline).to be_nil
+ expect(subject.head_pipeline).to be_nil
end
end
end
@@ -857,7 +857,7 @@ describe MergeRequest, models: true do
context 'and a failed pipeline is associated' do
before do
pipeline.update(status: 'failed')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_falsey }
@@ -866,7 +866,7 @@ describe MergeRequest, models: true do
context 'and a successful pipeline is associated' do
before do
pipeline.update(status: 'success')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -875,7 +875,7 @@ describe MergeRequest, models: true do
context 'and a skipped pipeline is associated' do
before do
pipeline.update(status: 'skipped')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -883,7 +883,7 @@ describe MergeRequest, models: true do
context 'when no pipeline is associated' do
before do
- allow(subject).to receive(:pipeline) { nil }
+ allow(subject).to receive(:head_pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -896,7 +896,7 @@ describe MergeRequest, models: true do
context 'and a failed pipeline is associated' do
before do
pipeline.statuses << create(:commit_status, status: 'failed', project: project)
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -904,7 +904,7 @@ describe MergeRequest, models: true do
context 'when no pipeline is associated' do
before do
- allow(subject).to receive(:pipeline) { nil }
+ allow(subject).to receive(:head_pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index da38254d1bc..8abcce42ce0 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -361,10 +361,15 @@ describe Project, models: true do
describe '#get_issue' do
let(:project) { create(:empty_project) }
let!(:issue) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :developer]
+ end
context 'with default issues tracker' do
it 'returns an issue' do
- expect(project.get_issue(issue.iid)).to eq issue
+ expect(project.get_issue(issue.iid, user)).to eq issue
end
it 'returns count of open issues' do
@@ -372,7 +377,12 @@ describe Project, models: true do
end
it 'returns nil when no issue found' do
- expect(project.get_issue(999)).to be_nil
+ expect(project.get_issue(999, user)).to be_nil
+ end
+
+ it "returns nil when user doesn't have access" do
+ user = create(:user)
+ expect(project.get_issue(issue.iid, user)).to eq nil
end
end
@@ -382,7 +392,7 @@ describe Project, models: true do
end
it 'returns an ExternalIssue' do
- issue = project.get_issue('FOO-1234')
+ issue = project.get_issue('FOO-1234', user)
expect(issue).to be_kind_of(ExternalIssue)
expect(issue.iid).to eq 'FOO-1234'
expect(issue.project).to eq project
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 04afb8ebc98..b797d19161d 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1303,32 +1303,36 @@ describe Repository, models: true do
repository.add_tag(user, '8.5', 'master', 'foo')
end
- it 'does not create a tag when a pre-hook fails' do
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, ''])
-
- expect do
- repository.add_tag(user, '8.5', 'master', 'foo')
- end.to raise_error(GitHooksService::PreReceiveError)
+ it 'returns a Gitlab::Git::Tag object' do
+ tag = repository.add_tag(user, '8.5', 'master', 'foo')
- repository.expire_tags_cache
- expect(repository.find_tag('8.5')).to be_nil
+ expect(tag).to be_a(Gitlab::Git::Tag)
end
- it 'passes tag SHA to hooks' do
- spy = GitHooksService.new
- allow(GitHooksService).to receive(:new).and_return(spy)
- allow(spy).to receive(:execute).and_call_original
+ it 'passes commit SHA to pre-receive and update hooks,\
+ and tag SHA to post-receive hook' do
+ pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', repository.path_to_repo)
+ update_hook = Gitlab::Git::Hook.new('update', repository.path_to_repo)
+ post_receive_hook = Gitlab::Git::Hook.new('post-receive', repository.path_to_repo)
- tag = repository.add_tag(user, '8.5', 'master', 'foo')
+ allow(Gitlab::Git::Hook).to receive(:new).
+ and_return(pre_receive_hook, update_hook, post_receive_hook)
- expect(spy).to have_received(:execute).
- with(anything, anything, anything, tag.target, anything)
- end
+ allow(pre_receive_hook).to receive(:trigger).and_call_original
+ allow(update_hook).to receive(:trigger).and_call_original
+ allow(post_receive_hook).to receive(:trigger).and_call_original
- it 'returns a Gitlab::Git::Tag object' do
tag = repository.add_tag(user, '8.5', 'master', 'foo')
- expect(tag).to be_a(Gitlab::Git::Tag)
+ commit_sha = repository.commit('master').id
+ tag_sha = tag.target
+
+ expect(pre_receive_hook).to have_received(:trigger).
+ with(anything, anything, commit_sha, anything)
+ expect(update_hook).to have_received(:trigger).
+ with(anything, anything, commit_sha, anything)
+ expect(post_receive_hook).to have_received(:trigger).
+ with(anything, anything, tag_sha, anything)
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 91826e5884d..14c891994d0 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1349,4 +1349,31 @@ describe User, models: true do
expect(projects).to be_empty
end
end
+
+ describe '#refresh_authorized_projects', redis: true do
+ let(:project1) { create(:empty_project) }
+ let(:project2) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ project1.team << [user, :reporter]
+ project2.team << [user, :guest]
+
+ user.project_authorizations.delete_all
+ user.refresh_authorized_projects
+ end
+
+ it 'refreshes the list of authorized projects' do
+ expect(user.project_authorizations.count).to eq(2)
+ end
+
+ it 'sets the authorized_projects_populated column' do
+ expect(user.authorized_projects_populated).to eq(true)
+ end
+
+ it 'stores the correct access levels' do
+ expect(user.project_authorizations.where(access_level: Gitlab::Access::GUEST).exists?).to eq(true)
+ expect(user.project_authorizations.where(access_level: Gitlab::Access::REPORTER).exists?).to eq(true)
+ end
+ end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 7bae055b241..ae7994af981 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -365,6 +365,24 @@ describe API::API, api: true do
let(:base_url) { "/projects/#{project.id}" }
let(:title) { milestone.title }
+ it "returns 404 on private projects for other users" do
+ private_project = create(:empty_project, :private)
+ create(:issue, project: private_project)
+
+ get api("/projects/#{private_project.id}/issues", non_member)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'returns no issues when user has access to project but not issues' do
+ restricted_project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ create(:issue, project: restricted_project)
+
+ get api("/projects/#{restricted_project.id}/issues", non_member)
+
+ expect(json_response).to eq([])
+ end
+
it 'returns project issues without confidential issues for non project members' do
get api("#{base_url}/issues", non_member)
expect(response).to have_http_status(200)
@@ -697,6 +715,14 @@ describe API::API, api: true do
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
end
end
+
+ context 'the user can only read the issue' do
+ it 'cannot create new labels' do
+ expect do
+ post api("/projects/#{project.id}/issues", non_member), title: 'new issue', labels: 'label, label2'
+ end.not_to change { project.labels.count }
+ end
+ end
end
describe 'POST /projects/:id/issues with spam filtering' do
@@ -839,8 +865,8 @@ describe API::API, api: true do
end
it 'removes all labels' do
- put api("/projects/#{project.id}/issues/#{issue.id}", user),
- labels: ''
+ put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: ''
+
expect(response).to have_http_status(200)
expect(json_response['labels']).to eq([])
end
@@ -892,8 +918,8 @@ describe API::API, api: true do
update_time = 2.weeks.ago
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: 'label3', state_event: 'close', updated_at: update_time
- expect(response).to have_http_status(200)
+ expect(response).to have_http_status(200)
expect(json_response['labels']).to include 'label3'
expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time)
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index b93577ef9d4..1e25ae60eef 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -402,14 +402,6 @@ describe API::API, api: true do
end
end
- describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
- it "returns merge_request" do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
- expect(response).to have_http_status(200)
- expect(json_response['state']).to eq('closed')
- end
- end
-
describe "PUT /projects/:id/merge_requests/:merge_request_id/merge" do
let(:pipeline) { create(:ci_pipeline_without_jobs) }
@@ -473,8 +465,9 @@ describe API::API, api: true do
expect(response).to have_http_status(200)
end
+
it "enables merge when pipeline succeeds if the pipeline is active" do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+ allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(pipeline)
allow(pipeline).to receive(:active?).and_return(true)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
@@ -486,6 +479,15 @@ describe API::API, api: true do
end
describe "PUT /projects/:id/merge_requests/:merge_request_id" do
+ context "to close a MR" do
+ it "returns merge_request" do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
+
+ expect(response).to have_http_status(200)
+ expect(json_response['state']).to eq('closed')
+ end
+ end
+
it "updates title and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title"
expect(response).to have_http_status(200)
@@ -511,10 +513,10 @@ describe API::API, api: true do
end
it 'allows special label names' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}",
- user),
- title: 'new issue',
- labels: 'label, label?, label&foo, ?, &'
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
+ title: 'new issue',
+ labels: 'label, label?, label&foo, ?, &'
+
expect(response.status).to eq(200)
expect(json_response['labels']).to include 'label'
expect(json_response['labels']).to include 'label?'
@@ -543,7 +545,7 @@ describe API::API, api: true do
it "returns 404 if note is attached to non existent merge request" do
post api("/projects/#{project.id}/merge_requests/404/comments", user),
- note: 'My comment'
+ note: 'My comment'
expect(response).to have_http_status(404)
end
end
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index d83f7883c78..a7e511aaeda 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -103,6 +103,18 @@ describe API::API, api: true do
expect(json_response['message']).to eq '404 Not found'
expect(json_response['id']).to be nil
end
+
+ context 'with coverage' do
+ before do
+ create(:ci_build, coverage: 30, pipeline: pipeline)
+ end
+
+ it 'exposes the coverage' do
+ get api("/projects/#{project.id}/pipelines/#{pipeline.id}", user)
+
+ expect(json_response["coverage"].to_i).to eq(30)
+ end
+ end
end
context 'unauthorized user' do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index ce9c96ace21..bb0344e5995 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -128,7 +128,7 @@ describe API::API, api: true do
)
end
- it 'retusn status 200' do
+ it 'returns status 200' do
post api("/projects/#{project.id}/services/mattermost_slash_commands/trigger"), params
expect(response).to have_http_status(200)
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index 705dbb7d1c0..5c90fd9bad9 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -40,7 +40,7 @@ describe 'cycle analytics events' do
expect(json_response['events']).not_to be_empty
- first_mr_iid = MergeRequest.order(created_at: :desc).pluck(:iid).first.to_s
+ first_mr_iid = project.merge_requests.order(id: :desc).pluck(:iid).first.to_s
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb
index c0b7e86b17c..6b33fe66a63 100644
--- a/spec/serializers/analytics_build_entity_spec.rb
+++ b/spec/serializers/analytics_build_entity_spec.rb
@@ -7,7 +7,9 @@ describe AnalyticsBuildEntity do
context 'build with an author' do
let(:user) { create(:user) }
- let(:build) { create(:ci_build, author: user, started_at: 2.hours.ago, finished_at: 1.hour.ago) }
+ let(:started_at) { 2.hours.ago }
+ let(:finished_at) { 1.hour.ago }
+ let(:build) { create(:ci_build, author: user, started_at: started_at, finished_at: finished_at) }
subject { entity.as_json }
@@ -31,5 +33,54 @@ describe AnalyticsBuildEntity do
it 'contains the duration' do
expect(subject[:total_time]).to eq(hours: 1 )
end
+
+ context 'no started at or finished at date' do
+ let(:started_at) { nil }
+ let(:finished_at) { nil }
+
+ it 'does not blow up' do
+ expect{ subject[:date] }.not_to raise_error
+ end
+
+ it 'shows the right message' do
+ expect(subject[:date]).to eq('Not started')
+ end
+
+ it 'shows the right total time' do
+ expect(subject[:total_time]).to eq({})
+ end
+ end
+
+ context 'no started at date' do
+ let(:started_at) { nil }
+
+ it 'does not blow up' do
+ expect{ subject[:date] }.not_to raise_error
+ end
+
+ it 'shows the right message' do
+ expect(subject[:date]).to eq('Not started')
+ end
+
+ it 'shows the right total time' do
+ expect(subject[:total_time]).to eq({})
+ end
+ end
+
+ context 'no finished at date' do
+ let(:finished_at) { nil }
+
+ it 'does not blow up' do
+ expect{ subject[:date] }.not_to raise_error
+ end
+
+ it 'shows the right message' do
+ expect(subject[:date]).to eq('about 2 hours ago')
+ end
+
+ it 'shows the right total time' do
+ expect(subject[:total_time]).to eq({ hours: 2 })
+ end
+ end
end
end
diff --git a/spec/serializers/build_entity_spec.rb b/spec/serializers/build_entity_spec.rb
index 6dcfaec259e..60c9642ee2c 100644
--- a/spec/serializers/build_entity_spec.rb
+++ b/spec/serializers/build_entity_spec.rb
@@ -1,23 +1,30 @@
require 'spec_helper'
describe BuildEntity do
+ let(:build) { create(:ci_build) }
+
let(:entity) do
described_class.new(build, request: double)
end
subject { entity.as_json }
- context 'when build is a regular job' do
- let(:build) { create(:ci_build) }
+ it 'contains paths to build page and retry action' do
+ expect(subject).to include(:build_path, :retry_path)
+ end
- it 'contains paths to build page and retry action' do
- expect(subject).to include(:build_path, :retry_path)
- expect(subject).not_to include(:play_path)
- end
+ it 'does not contain sensitive information' do
+ expect(subject).not_to include(/token/)
+ expect(subject).not_to include(/variables/)
+ end
+
+ it 'contains timestamps' do
+ expect(subject).to include(:created_at, :updated_at)
+ end
- it 'does not contain sensitive information' do
- expect(subject).not_to include(/token/)
- expect(subject).not_to include(/variables/)
+ context 'when build is a regular job' do
+ it 'does not contain path to play action' do
+ expect(subject).not_to include(:play_path)
end
end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index ff113efd916..ebb11166964 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -1,31 +1,10 @@
require 'spec_helper'
describe Ci::ProcessPipelineService, services: true do
- let(:pipeline) { create(:ci_pipeline, ref: 'master') }
+ let(:pipeline) { create(:ci_empty_pipeline, ref: 'master') }
let(:user) { create(:user) }
- let(:config) { nil }
-
- before do
- allow(pipeline).to receive(:ci_yaml_file).and_return(config)
- end
describe '#execute' do
- def all_builds
- pipeline.builds
- end
-
- def builds
- all_builds.where.not(status: [:created, :skipped])
- end
-
- def process_pipeline
- described_class.new(pipeline.project, user).execute(pipeline)
- end
-
- def succeed_pending
- builds.pending.update_all(status: 'success')
- end
-
context 'start queuing next builds' do
before do
create(:ci_build, :created, pipeline: pipeline, name: 'linux', stage_idx: 0)
@@ -223,10 +202,6 @@ describe Ci::ProcessPipelineService, services: true do
pipeline.builds.running_or_pending.each(&:success)
expect(manual_actions).to be_many # production and clear cache
end
-
- def manual_actions
- pipeline.manual_actions
- end
end
end
@@ -282,15 +257,6 @@ describe Ci::ProcessPipelineService, services: true do
expect(builds.map(&:status)).to eq(%w[success skipped pending])
end
end
-
- def create_build(name, stage_idx, when_value = nil)
- create(:ci_build,
- :created,
- pipeline: pipeline,
- name: name,
- stage_idx: stage_idx,
- when: when_value)
- end
end
context 'when failed build in the middle stage is retried' do
@@ -327,65 +293,92 @@ describe Ci::ProcessPipelineService, services: true do
end
end
- context 'creates a builds from .gitlab-ci.yml' do
- let(:config) do
- YAML.dump({
- rspec: {
- stage: 'test',
- script: 'rspec'
- },
- rubocop: {
- stage: 'test',
- script: 'rubocop'
- },
- deploy: {
- stage: 'deploy',
- script: 'deploy'
- }
- })
+ context 'when there are builds that are not created yet' do
+ let(:pipeline) do
+ create(:ci_pipeline, config: config)
end
- # Using stubbed .gitlab-ci.yml created in commit factory
- #
+ let(:config) do
+ { rspec: { stage: 'test', script: 'rspec' },
+ deploy: { stage: 'deploy', script: 'rsync' } }
+ end
before do
- stub_ci_pipeline_yaml_file(config)
create(:ci_build, :created, pipeline: pipeline, name: 'linux', stage: 'build', stage_idx: 0)
create(:ci_build, :created, pipeline: pipeline, name: 'mac', stage: 'build', stage_idx: 0)
end
- it 'when processing a pipeline' do
- # Currently we have two builds with state created
+ it 'processes the pipeline' do
+ # Currently we have five builds with state created
+ #
expect(builds.count).to eq(0)
expect(all_builds.count).to eq(2)
- # Create builds will mark the created as pending
- expect(process_pipeline).to be_truthy
+ # Process builds service will enqueue builds from the first stage.
+ #
+ process_pipeline
+
expect(builds.count).to eq(2)
expect(all_builds.count).to eq(2)
- # When we builds succeed we will create a rest of pipeline from .gitlab-ci.yml
- # We will have 2 succeeded, 2 pending (from stage test), total 5 (one more build from deploy)
+ # When builds succeed we will enqueue remaining builds.
+ #
+ # We will have 2 succeeded, 1 pending (from stage test), total 4 (two
+ # additional build from `.gitlab-ci.yml`).
+ #
succeed_pending
- expect(process_pipeline).to be_truthy
+ process_pipeline
+
expect(builds.success.count).to eq(2)
- expect(builds.pending.count).to eq(2)
- expect(all_builds.count).to eq(5)
+ expect(builds.pending.count).to eq(1)
+ expect(all_builds.count).to eq(4)
- # When we succeed the 2 pending from stage test,
- # We will queue a deploy stage, no new builds will be created
+ # When pending build succeeds in stage test, we enqueue deploy stage.
+ #
succeed_pending
- expect(process_pipeline).to be_truthy
+ process_pipeline
+
expect(builds.pending.count).to eq(1)
- expect(builds.success.count).to eq(4)
- expect(all_builds.count).to eq(5)
+ expect(builds.success.count).to eq(3)
+ expect(all_builds.count).to eq(4)
- # When we succeed last pending build, we will have a total of 5 succeeded builds, no new builds will be created
+ # When the last one succeeds we have 4 successful builds.
+ #
succeed_pending
- expect(process_pipeline).to be_falsey
- expect(builds.success.count).to eq(5)
- expect(all_builds.count).to eq(5)
+ process_pipeline
+
+ expect(builds.success.count).to eq(4)
+ expect(all_builds.count).to eq(4)
end
end
end
+
+ def all_builds
+ pipeline.builds
+ end
+
+ def builds
+ all_builds.where.not(status: [:created, :skipped])
+ end
+
+ def process_pipeline
+ described_class.new(pipeline.project, user).execute(pipeline)
+ end
+
+ def succeed_pending
+ builds.pending.update_all(status: 'success')
+ end
+
+ def manual_actions
+ pipeline.manual_actions
+ end
+
+ def create_build(name, stage_idx, when_value = nil)
+ create(:ci_build,
+ :created,
+ pipeline: pipeline,
+ name: name,
+ stage_idx: stage_idx,
+ when: when_value)
+ end
end
diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb
index ddf3527dc0f..13654a0881c 100644
--- a/spec/services/labels/transfer_service_spec.rb
+++ b/spec/services/labels/transfer_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Labels::TransferService, services: true do
describe '#execute' do
- let(:user) { create(:user) }
+ let(:user) { create(:admin) }
let(:group_1) { create(:group) }
let(:group_2) { create(:group) }
let(:group_3) { create(:group) }
diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
index a44312dd363..bb7830c7eea 100644
--- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
+++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
@@ -20,13 +20,19 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
let(:todo_service) { TodoService.new }
let(:merge_request) do
- create(:merge_request, merge_user: user, source_branch: 'master',
- target_branch: 'feature', source_project: project, target_project: project,
+ create(:merge_request, merge_user: user,
+ source_branch: 'master',
+ target_branch: 'feature',
+ source_project: project,
+ target_project: project,
state: 'opened')
end
before do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:head_pipeline)
+ .and_return(pipeline)
+
allow(service).to receive(:todo_service).and_return(todo_service)
end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 3f5df049ea2..dc945ca4868 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -24,6 +24,8 @@ describe MergeRequests::BuildService, services: true do
end
before do
+ project.team << [user, :guest]
+
allow(CompareService).to receive_message_chain(:new, :execute).and_return(compare)
allow(project).to receive(:commit).and_return(commit_1)
allow(project).to receive(:commit).and_return(commit_2)
@@ -168,6 +170,16 @@ describe MergeRequests::BuildService, services: true do
expect(merge_request.title).to eq("Resolve \"#{issue.title}\"")
end
+ context 'when issue is not accessible to user' do
+ before do
+ project.team.truncate
+ end
+
+ it 'uses branch title as the merge request title' do
+ expect(merge_request.title).to eq("#{issue.iid} fix issue")
+ end
+ end
+
context 'issue does not exist' do
let(:source_branch) { "#{issue.iid.succ}-fix-issue" }
diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
index 807f89e80b7..05cdbe5287a 100644
--- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
+++ b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
@@ -10,6 +10,7 @@ describe MergeRequests::MergeRequestDiffCacheService do
expect(Rails.cache).to receive(:read).with(cache_key).and_return({})
expect(Rails.cache).to receive(:write).with(cache_key, anything)
+ allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => true))
subject.execute(merge_request)
end
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index b3aa63817c7..f92978a33a3 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -27,7 +27,10 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
context 'first time enabling' do
before do
- allow(merge_request).to receive(:pipeline).and_return(pipeline)
+ allow(merge_request)
+ .to receive(:head_pipeline)
+ .and_return(pipeline)
+
service.execute(merge_request)
end
@@ -49,8 +52,12 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) }
before do
- allow(mr_merge_if_green_enabled).to receive(:pipeline).and_return(pipeline)
- allow(mr_merge_if_green_enabled).to receive(:mergeable?).and_return(true)
+ allow(mr_merge_if_green_enabled).to receive(:head_pipeline)
+ .and_return(pipeline)
+
+ allow(mr_merge_if_green_enabled).to receive(:mergeable?)
+ .and_return(true)
+
allow(pipeline).to receive(:success?).and_return(true)
end
@@ -144,9 +151,12 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
before do
# This behavior of MergeRequest: we instantiate a new object
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_wrap_original do
- Ci::Pipeline.find(pipeline.id)
- end
+ #
+ allow_any_instance_of(MergeRequest)
+ .to receive(:head_pipeline)
+ .and_wrap_original do
+ Ci::Pipeline.find(pipeline.id)
+ end
end
it "doesn't merge if any of stages failed" do
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index e1f90e17cce..16d5f2bf0b8 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -7,7 +7,15 @@ timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 10
Capybara.javascript_driver = :poltergeist
Capybara.register_driver :poltergeist do |app|
- Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: timeout, window_size: [1366, 768])
+ Capybara::Poltergeist::Driver.new(
+ app,
+ js_errors: true,
+ timeout: timeout,
+ window_size: [1366, 768],
+ phantomjs_options: [
+ '--load-images=no'
+ ]
+ )
end
Capybara.default_max_wait_time = timeout
diff --git a/spec/support/javascript_fixtures_helpers.rb b/spec/support/javascript_fixtures_helpers.rb
index adc3f48b434..99e98eebdb4 100644
--- a/spec/support/javascript_fixtures_helpers.rb
+++ b/spec/support/javascript_fixtures_helpers.rb
@@ -1,3 +1,4 @@
+require 'action_dispatch/testing/test_request'
require 'fileutils'
require 'gitlab/popen'
@@ -30,13 +31,17 @@ module JavaScriptFixturesHelpers
if response_mime_type.html?
doc = Nokogiri::HTML::DocumentFragment.parse(fixture)
+ link_tags = doc.css('link')
+ link_tags.remove
+
scripts = doc.css('script')
scripts.remove
fixture = doc.to_html
# replace relative links
- fixture.gsub!(%r{="/}, '="https://fixture.invalid/')
+ test_host = ActionDispatch::TestRequest::DEFAULT_ENV['HTTP_HOST']
+ fixture.gsub!(%r{="/}, "=\"http://#{test_host}/")
end
FileUtils.mkdir_p(File.dirname(fixture_file_name))
diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb
index 0497e391860..691d7e05f57 100644
--- a/spec/support/matchers/access_matchers.rb
+++ b/spec/support/matchers/access_matchers.rb
@@ -7,7 +7,7 @@ module AccessMatchers
extend RSpec::Matchers::DSL
include Warden::Test::Helpers
- def emulate_user(user)
+ def emulate_user(user, project = nil)
case user
when :user
login_as(create(:user))
@@ -19,6 +19,18 @@ module AccessMatchers
login_as(create(:user, external: true))
when User
login_as(user)
+ when :owner
+ raise ArgumentError, "cannot emulate owner without project" unless project
+
+ login_as(project.owner)
+ when *Gitlab::Access.sym_options.keys
+ raise ArgumentError, "cannot emulate user #{user} without project" unless project
+
+ role = user
+ user = create(:user)
+ project.public_send(:"add_#{role}", user)
+
+ login_as(user)
else
raise ArgumentError, "cannot emulate user #{user}"
end
@@ -26,8 +38,7 @@ module AccessMatchers
def description_for(user, type)
if user.kind_of?(User)
- # User#inspect displays too much information for RSpec's description
- # messages
+ # User#inspect displays too much information for RSpec's descriptions
"be #{type} for the specified user"
else
"be #{type} for #{user}"
@@ -36,21 +47,31 @@ module AccessMatchers
matcher :be_allowed_for do |user|
match do |url|
- emulate_user(user)
- visit url
+ emulate_user(user, @project)
+ visit(url)
+
status_code != 404 && current_path != new_user_session_path
end
+ chain :of do |project|
+ @project = project
+ end
+
description { description_for(user, 'allowed') }
end
matcher :be_denied_for do |user|
match do |url|
- emulate_user(user)
- visit url
+ emulate_user(user, @project)
+ visit(url)
+
status_code == 404 || current_path == new_user_session_path
end
+ chain :of do |project|
+ @project = project
+ end
+
description { description_for(user, 'denied') }
end
end
diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb
index 3fddfb3b62f..8020faa1f9c 100644
--- a/spec/views/layouts/_head.html.haml_spec.rb
+++ b/spec/views/layouts/_head.html.haml_spec.rb
@@ -1,10 +1,6 @@
require 'spec_helper'
describe 'layouts/_head' do
- before do
- stub_template 'layouts/_user_styles.html.haml' => ''
- end
-
it 'escapes HTML-safe strings in page_title' do
stub_helper_with_safe_string(:page_title)
diff --git a/spec/views/projects/merge_requests/_new_submit.html.haml_spec.rb b/spec/views/projects/merge_requests/_new_submit.html.haml_spec.rb
new file mode 100644
index 00000000000..4f698a34ab5
--- /dev/null
+++ b/spec/views/projects/merge_requests/_new_submit.html.haml_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe 'projects/merge_requests/_new_submit.html.haml', :view do
+ let(:merge_request) { create(:merge_request) }
+ let!(:pipeline) { create(:ci_empty_pipeline) }
+
+ before do
+ controller.prepend_view_path('app/views/projects')
+
+ assign(:merge_request, merge_request)
+ assign(:commits, merge_request.commits)
+ assign(:project, merge_request.target_project)
+
+ allow(view).to receive(:can?).and_return(true)
+ allow(view).to receive(:url_for).and_return('#')
+ allow(view).to receive(:current_user).and_return(merge_request.author)
+ end
+
+ context 'when there are pipelines for merge request but no pipeline for last commit' do
+ before do
+ assign(:pipelines, Ci::Pipeline.all)
+ assign(:pipeline, nil)
+ end
+
+ it 'shows <<Pipelines>> tab and hides <<Builds>> tab' do
+ render
+ expect(rendered).to have_text('Pipelines 1')
+ expect(rendered).not_to have_text('Builds')
+ end
+ end
+end
diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb
index 18a1aab766c..95e2458da35 100644
--- a/spec/workers/authorized_projects_worker_spec.rb
+++ b/spec/workers/authorized_projects_worker_spec.rb
@@ -1,22 +1,33 @@
require 'spec_helper'
describe AuthorizedProjectsWorker do
+ let(:worker) { described_class.new }
+
describe '#perform' do
it "refreshes user's authorized projects" do
user = create(:user)
- expect(User).to receive(:find_by).with(id: user.id).and_return(user)
- expect(user).to receive(:refresh_authorized_projects)
+ expect(worker).to receive(:refresh).with(an_instance_of(User))
- described_class.new.perform(user.id)
+ worker.perform(user.id)
end
- context "when user is not found" do
+ context "when the user is not found" do
it "does nothing" do
- expect_any_instance_of(User).not_to receive(:refresh_authorized_projects)
+ expect(worker).not_to receive(:refresh)
- described_class.new.perform(999_999)
+ described_class.new.perform(-1)
end
end
end
+
+ describe '#refresh', redis: true do
+ it 'refreshes the authorized projects of the user' do
+ user = create(:user)
+
+ expect(user).to receive(:refresh_authorized_projects)
+
+ worker.refresh(user)
+ end
+ end
end