diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 219077d79b805b29d46ba78ef94274c1deb85e21..ff8aa351226a1ac8a8e1171ed309fddfbb604aa2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -18,6 +18,7 @@ variables:
   SIMPLECOV: "true"
   USE_DB: "true"
   USE_BUNDLE_INSTALL: "true"
+  GIT_DEPTH: "20"
 
 before_script:
   - source ./scripts/prepare_build.sh
@@ -134,6 +135,11 @@ spinach 9 10: *spinach-knapsack
   image: "ruby:2.3"
   only:
     - master
+  cache:
+    key: "ruby-23"
+    paths:
+      - vendor/apt
+      - vendor/ruby
 
 .rspec-knapsack-ruby23: &rspec-knapsack-ruby23
   <<: *rspec-knapsack
diff --git a/CHANGELOG b/CHANGELOG
index 3fd908d30d68b658b76e58c21a8717675f19f961..d2dcafc84a0e205b5dd327ebbb2e9e7222e8e705 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,87 @@
 Please view this file on the master branch, on stable branches it's out of date.
-v 8.10.0(unreleased)
-  - Add notifications dropdown for groups
 
+v 8.10.0 (unreleased)
+  - Fix commit builds API, return all builds for all pipelines for given commit. !4849
+  - Replace Haml with Hamlit to make view rendering faster. !3666
+  - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
+  - Display last commit of deleted branch in push events !4699 (winniehell)
+  - Add Sidekiq queue duration to transaction metrics.
+  - Let Workhorse serve format-patch diffs
+  - Make images fit to the size of the viewport !4810
+  - Fix check for New Branch button on Issue page !4630 (winniehell)
+  - Fix MR-auto-close text added to description. !4836
+  - Fix pagination when sorting by columns with lots of ties (like priority)
+  - Exclude email check from the standard health check
+  - Fix changing issue state columns in milestone view
+  - Add notification settings dropdown for groups
+  - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
+  - PipelinesFinder uses git cache data
+  - Check for conflicts with existing Project's wiki path when creating a new project.
+  - Remove unused front-end variable -> default_issues_tracker
+  - Add API endpoint for a group issues !4520 (mahcsig)
+  - Add Bugzilla integration !4930 (iamtjg)
+  - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
+  - Add basic system information like memory and disk usage to the admin panel
+
+v 8.9.3 (unreleased)
+  - MergeRequestDiff reload content use update_columns to avoid multiple YAML de/serializations
+  - Decreased min width of screen to 1280px for pinned sidebar
+  - Fix encrypted data backwards compatibility after upgrading attr_encrypted gem
+  - Update mobile button icons to be more inline with typical UI paradigms
+  - Fixes missing avatar on system notes. !4954
+  - Improve performance of obtaining the maximum access of a user in a project
+
+v 8.9.2
+  - Fix visibility of snippets when searching.
+  - Fix an information disclosure when requesting access to a group containing private projects.
+  - Update omniauth-saml to 1.6.0 !4951
+
+v 8.9.1
+  - Refactor labels documentation. !3347
+  - Eager load award emoji on notes. !4628
+  - Fix some CI wording in documentation. !4660
+  - Document `GIT_STRATEGY` and `GIT_DEPTH`. !4720
+  - Add documentation for the export & import features. !4732
+  - Add some docs for Docker Registry configuration. !4738
+  - Ensure we don't send the "access request declined" email to access requesters on project deletion. !4744
+  - Display group/project access requesters separately in the admin area. !4798
+  - Add documentation and examples for configuring cloud storage for registry images. !4812
+  - Clarifies documentation about artifact expiry. !4831
+  - Fix the Network graph links. !4832
+  - Fix MR-auto-close text added to description. !4836
+  - Add documentation for award emoji now that comments can be awarded with emojis. !4839
+  - Fix typo in export failure email. !4847
+  - Fix header vertical centering. !4170
+  - Fix subsequent SAML sign ins. !4718
+  - Set button label when picking an option from status dropdown. !4771
+  - Prevent invalid URLs from raising exceptions in WikiLink Filter. !4775
+  - Handle external issues in IssueReferenceFilter. !4789
+  - Support for rendering/redacting multiple documents. !4828
+  - Update Todos documentation and screenshots to include new functionality. !4840
+  - Hide nav arrows by default. !4843
+  - Added bottom padding to label color suggestion link. !4845
+  - Use jQuery objects in ref dropdown. !4850
+  - Fix GitLab project import issues related to notes and builds. !4855
+  - Restrict header logo to 36px so it doesn't overflow. !4861
+  - Fix unwanted label unassignment. !4863
+  - Fix mobile Safari bug where horizontal nav arrows would flicker on scroll. !4869
+  - Restore old behavior around diff notes to outdated discussions. !4870
+  - Fix merge requests project settings help link anchor. !4873
+  - Fix 404 when accessing pipelines as guest user on public projects. !4881
+  - Remove width restriction for logo on sign-in page. !4888
+  - Bump gitlab_git to 10.2.3 to fix false truncated warnings with ISO-8559 files. !4884
+  - Apply selected value as label. !4886
+  - Fix temp file being deleted after the request while importing a GitLab project. !4894
+  - Fix pagination when sorting by columns with lots of ties (like priority)
+  - Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise.
+  - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
+  - Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912
+  - Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915
+  - Remove duplicate 'New Page' button on edit wiki page
+
+v 8.9.0
 v 8.9.0 (unreleased)
+  - Fix group visibility form layout in application settings
   - Fix builds API response not including commit data
   - Fix error when CI job variables key specified but not defined
   - Fix pipeline status when there are no builds in pipeline
@@ -15,7 +94,6 @@ v 8.9.0 (unreleased)
   - Fix endless redirections when accessing user OAuth applications when they are disabled
   - Allow enabling wiki page events from Webhook management UI
   - Bump rouge to 1.11.0
-  - Fix MR-auto-close text added to description
   - Fix issue with arrow keys not working in search autocomplete dropdown
   - Fix an issue where note polling stopped working if a window was in the
     background during a refresh.
@@ -39,7 +117,6 @@ v 8.9.0 (unreleased)
   - Implement a fair usage of shared runners
   - Remove project notification settings associated with deleted projects
   - Fix 404 page when viewing TODOs that contain milestones or labels in different projects
-  - Wrap code blocks on Activies and Todos page !4783 (winniehell)
   - Add a metric for the number of new Redis connections created by a transaction
   - Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
   - Redesign navigation for project pages
@@ -100,6 +177,7 @@ v 8.9.0 (unreleased)
   - Add Application Setting to configure Container Registry token expire delay (default 5min)
   - Cache assigned issue and merge request counts in sidebar nav
   - Use Knapsack only in CI environment
+  - Updated project creation page to match new UI #2542
   - Cache project build count in sidebar nav
   - Add milestone expire date to the right sidebar
   - Manually mark a issue or merge request as a todo
@@ -155,6 +233,10 @@ v 8.9.0 (unreleased)
   - Add tooltip to pin/unpin navbar
   - Add new sub nav style to Wiki and Graphs sub navigation
 
+v 8.8.6
+  - Fix visibility of snippets when searching.
+  - Update omniauth-saml to 1.6.0 !4951
+
 v 8.8.5
   - Import GitHub repositories respecting the API rate limit !4166
   - Fix todos page throwing errors when you have a project pending deletion !4300
@@ -285,6 +367,10 @@ v 8.8.0
   - When creating a .gitignore file a dropdown with templates will be provided
   - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
 
+v 8.7.8
+  - Fix visibility of snippets when searching.
+  - Update omniauth-saml to 1.6.0 !4951
+
 v 8.7.7
   - Fix import by `Any Git URL` broken if the URL contains a space
   - Prevent unauthorized access to other projects build traces
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 8bd6ba8c5c366585c0343c027cb738d5fdeb8d4d..879be8a98fc7a080771e966eb405fca1b40108de 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.7.5
+0.7.7
diff --git a/Gemfile b/Gemfile
index 092ea9d69b0b300052dd11c5e07555968f710c1b..52de9ef981309dea29ab0f68997a429e41d59c71 100644
--- a/Gemfile
+++ b/Gemfile
@@ -30,7 +30,7 @@ gem 'omniauth-github',        '~> 1.1.1'
 gem 'omniauth-gitlab',        '~> 1.0.0'
 gem 'omniauth-google-oauth2', '~> 0.2.0'
 gem 'omniauth-kerberos',      '~> 0.3.0', group: :kerberos
-gem 'omniauth-saml',          '~> 1.5.0'
+gem 'omniauth-saml',          '~> 1.6.0'
 gem 'omniauth-shibboleth',    '~> 1.2.0'
 gem 'omniauth-twitter',       '~> 1.2.0'
 gem 'omniauth_crowd',         '~> 2.2.0'
@@ -76,7 +76,7 @@ gem 'rack-cors',    '~> 0.4.0', require: 'rack/cors'
 gem "kaminari", "~> 0.17.0"
 
 # HAML
-gem "haml-rails", '~> 0.9.0'
+gem 'hamlit', '~> 2.5'
 
 # Files attachments
 gem "carrierwave", '~> 0.10.0'
@@ -91,6 +91,7 @@ gem 'fog-core', '~> 1.40'
 gem 'fog-local', '~> 0.3'
 gem 'fog-google', '~> 0.3'
 gem 'fog-openstack', '~> 0.1'
+gem 'fog-rackspace', '~> 0.1.1'
 
 # for aws storage
 gem "unf", '~> 0.1.4'
@@ -234,7 +235,7 @@ gem 'net-ssh',            '~> 3.0.1'
 gem 'base32',             '~> 0.3.0'
 
 # Sentry integration
-gem 'sentry-raven', '~> 0.15'
+gem 'sentry-raven', '~> 1.1.0'
 
 gem 'premailer-rails', '~> 1.9.0'
 
@@ -346,3 +347,6 @@ gem "paranoia", "~> 2.0"
 
 # Health check
 gem 'health_check', '~> 1.5.1'
+
+# System information
+gem 'vmstat', '~> 2.1.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index ba16e4bf3378be01879b9136f96739abe5b71b58..4c5350ba63988c92adc54b9229466ab81b2f05da 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -243,6 +243,11 @@ GEM
       fog-core (>= 1.39)
       fog-json (>= 1.0)
       ipaddress (>= 0.8)
+    fog-rackspace (0.1.1)
+      fog-core (>= 1.35)
+      fog-json (>= 1.0)
+      fog-xml (>= 0.1)
+      ipaddress (>= 0.8)
     fog-xml (0.1.2)
       fog-core
       nokogiri (~> 1.5, >= 1.5.11)
@@ -277,7 +282,7 @@ GEM
       posix-spawn (~> 0.3)
     gitlab_emoji (0.3.1)
       gemojione (~> 2.2, >= 2.2.1)
-    gitlab_git (10.2.0)
+    gitlab_git (10.2.3)
       activesupport (~> 4.0)
       charlock_holmes (~> 0.7.3)
       github-linguist (~> 4.7.0)
@@ -320,14 +325,10 @@ GEM
     grape-entity (0.4.8)
       activesupport
       multi_json (>= 1.3.2)
-    haml (4.0.7)
+    hamlit (2.5.0)
+      temple (~> 0.7.6)
+      thor
       tilt
-    haml-rails (0.9.0)
-      actionpack (>= 4.0.1)
-      activesupport (>= 4.0.1)
-      haml (>= 4.0.6, < 5.0)
-      html2haml (>= 1.0.1)
-      railties (>= 4.0.1)
     hashie (3.4.3)
     health_check (1.5.1)
       rails (>= 2.3.0)
@@ -337,11 +338,6 @@ GEM
     html-pipeline (1.11.0)
       activesupport (>= 2)
       nokogiri (~> 1.4)
-    html2haml (2.0.0)
-      erubis (~> 2.7.0)
-      haml (~> 4.0.0)
-      nokogiri (~> 1.6.0)
-      ruby_parser (~> 3.5)
     htmlentities (4.3.4)
     http_parser.rb (0.5.3)
     httparty (0.13.7)
@@ -468,9 +464,9 @@ GEM
     omniauth-oauth2 (1.3.1)
       oauth2 (~> 1.0)
       omniauth (~> 1.2)
-    omniauth-saml (1.5.0)
+    omniauth-saml (1.6.0)
       omniauth (~> 1.3)
-      ruby-saml (~> 1.1, >= 1.1.1)
+      ruby-saml (~> 1.3)
     omniauth-shibboleth (1.2.1)
       omniauth (>= 1.0.0)
     omniauth-twitter (1.2.1)
@@ -631,9 +627,8 @@ GEM
     ruby-fogbugz (0.2.1)
       crack (~> 0.4)
     ruby-progressbar (1.8.1)
-    ruby-saml (1.1.2)
+    ruby-saml (1.3.0)
       nokogiri (>= 1.5.10)
-      uuid (~> 2.3)
     ruby_parser (3.8.2)
       sexp_processor (~> 4.1)
     rubyntlm (0.5.2)
@@ -665,7 +660,7 @@ GEM
       activesupport (>= 3.1, < 4.3)
     select2-rails (3.5.9.3)
       thor (~> 0.14)
-    sentry-raven (0.15.6)
+    sentry-raven (1.1.0)
       faraday (>= 0.7.6)
     settingslogic (2.0.9)
     sexp_processor (4.7.0)
@@ -733,6 +728,7 @@ GEM
       railties (>= 3.2.5, < 6)
     teaspoon-jasmine (2.2.0)
       teaspoon (>= 1.0.0)
+    temple (0.7.7)
     term-ansicolor (1.3.2)
       tins (~> 1.0)
     test_after_commit (0.4.2)
@@ -789,6 +785,7 @@ GEM
       coercible (~> 1.0)
       descendants_tracker (~> 0.0, >= 0.0.3)
       equalizer (~> 0.0, >= 0.0.9)
+    vmstat (2.1.0)
     warden (1.2.6)
       rack (>= 1.0)
     web-console (2.3.0)
@@ -866,6 +863,7 @@ DEPENDENCIES
   fog-google (~> 0.3)
   fog-local (~> 0.3)
   fog-openstack (~> 0.1)
+  fog-rackspace (~> 0.1.1)
   font-awesome-rails (~> 4.6.1)
   foreman
   fuubar (~> 2.0.0)
@@ -882,7 +880,7 @@ DEPENDENCIES
   gon (~> 6.0.1)
   grape (~> 0.13.0)
   grape-entity (~> 0.4.2)
-  haml-rails (~> 0.9.0)
+  hamlit (~> 2.5)
   health_check (~> 1.5.1)
   hipchat (~> 1.5.0)
   html-pipeline (~> 1.11.0)
@@ -920,7 +918,7 @@ DEPENDENCIES
   omniauth-gitlab (~> 1.0.0)
   omniauth-google-oauth2 (~> 0.2.0)
   omniauth-kerberos (~> 0.3.0)
-  omniauth-saml (~> 1.5.0)
+  omniauth-saml (~> 1.6.0)
   omniauth-shibboleth (~> 1.2.0)
   omniauth-twitter (~> 1.2.0)
   omniauth_crowd (~> 2.2.0)
@@ -960,7 +958,7 @@ DEPENDENCIES
   sdoc (~> 0.3.20)
   seed-fu (~> 2.3.5)
   select2-rails (~> 3.5.9)
-  sentry-raven (~> 0.15)
+  sentry-raven (~> 1.1.0)
   settingslogic (~> 2.0.9)
   sham_rack
   shoulda-matchers (~> 2.8.0)
@@ -993,6 +991,7 @@ DEPENDENCIES
   unicorn-worker-killer (~> 0.4.2)
   version_sorter (~> 2.0.0)
   virtus (~> 1.0.1)
+  vmstat (~> 2.1.0)
   web-console (~> 2.0)
   webmock (~> 1.21.0)
   wikicloth (= 0.8.1)
diff --git a/VERSION b/VERSION
index 6c07f65628533d96d3fe02f92c6c3cdfdb6dc44e..213504430f3e756aa83dc22dce99bd6d8176291f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.9.0-pre
+8.10.0-pre
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 0206db461da3f588ad2a0313d5d22129cdf3aed4..b6dbf2d0cc1dba0cf57537da7775e82004ca350c 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -50,7 +50,7 @@
 #= require_directory ./ci
 #= require_directory ./commit
 #= require_directory ./extensions
-#= require_directory ./lib
+#= require_directory ./lib/utils
 #= require_directory ./u2f
 #= require_directory .
 #= require fuzzaldrin-plus
@@ -199,7 +199,6 @@ $ ->
     $('.header-content .header-logo').toggle()
     $('.header-content .navbar-collapse').toggle()
     $('.navbar-toggle').toggleClass('active')
-    $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
 
   # Show/hide comments on diff
   $body.on "click", ".js-toggle-diff-comments", (e) ->
@@ -261,7 +260,7 @@ $ ->
   new Aside()
 
   # Sidenav pinning
-  if $window.width() < 1440 and $.cookie('pin_nav') is 'true'
+  if $window.width() < 1280 and $.cookie('pin_nav') is 'true'
     $.cookie('pin_nav', 'false', { path: '/' })
     $('.page-with-sidebar')
       .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 030f1564862631aeaf4140f15756e58abac8c0a1..37d0adaa625d29bb7f630da362dce6dd8eaa7377 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -341,7 +341,9 @@ class @AwardsHandler
       for emoji in frequentlyUsedEmojis
         $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
 
-      $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
+      $('.emoji-menu-content')
+        .prepend(ul)
+        .prepend($('<h5>').text('Frequently used'))
 
     @frequentEmojiBlockRendered = true
 
@@ -356,7 +358,7 @@ class @AwardsHandler
 
       if term
         # Generate a search result block
-        h5 = $('<h5>').text('Search results').addClass('emoji-search')
+        h5 = $('<h5>').text('Search results')
         found_emojis = @searchEmojis(term).show()
         ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
         $('.emoji-menu-content ul, .emoji-menu-content h5').hide()
diff --git a/app/assets/javascripts/blob/template_selector.js.coffee b/app/assets/javascripts/blob/template_selector.js.coffee
index e76e303189dcd18a222135a0134bf1772916fb7c..40c9169beac4e4393f70af5bf2382af456ff306a 100644
--- a/app/assets/javascripts/blob/template_selector.js.coffee
+++ b/app/assets/javascripts/blob/template_selector.js.coffee
@@ -19,6 +19,7 @@ class @TemplateSelector
       data: @data,
       filterable: true,
       selectable: true,
+      toggleLabel: @toggleLabel,
       search:
         fields: ['name']
       clicked: @onClick
@@ -31,6 +32,9 @@ class @TemplateSelector
       @onFilenameUpdate()
     )
 
+  toggleLabel: (item) ->
+    item.name
+
   onFilenameUpdate: ->
     return unless @$input.length
 
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 2a7bf0bc3061cf850fd70a33c6bbce6092c02cb4..1b0d9f0b1ae6508abea3abdd97efb326d4040627 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -186,6 +186,8 @@ class GitLabDropdown
             @fullData = data
 
             @parseData @fullData
+
+            @filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input
         }
 
     # Init filterable
@@ -280,7 +282,7 @@ class GitLabDropdown
         html = @renderData(data)
 
     # Render the full menu
-    full_html = @renderMenu(html.join(""))
+    full_html = @renderMenu(html)
 
     @appendMenu(full_html)
 
@@ -351,7 +353,8 @@ class GitLabDropdown
     if @options.renderMenu
       menu_html = @options.renderMenu(html)
     else
-      menu_html = "<ul>#{html}</ul>"
+      menu_html = $('<ul />')
+        .append(html)
 
     return menu_html
 
@@ -360,7 +363,9 @@ class GitLabDropdown
     selector = '.dropdown-content'
     if @dropdown.find(".dropdown-toggle-page").length
       selector = ".dropdown-page-one .dropdown-content"
-    $(selector, @dropdown).html html
+    $(selector, @dropdown)
+      .empty()
+      .append(html)
 
   # Render the row
   renderItem: (data, group = false, index = false) ->
@@ -459,7 +464,7 @@ class GitLabDropdown
 
       # Toggle the dropdown label
       if @options.toggleLabel
-        @updateLabel()
+        @updateLabel(selectedObject, el, @)
       else
         selectedObject
     else if el.hasClass(INDETERMINATE_CLASS)
@@ -486,7 +491,7 @@ class GitLabDropdown
 
       # Toggle the dropdown label
       if @options.toggleLabel
-        @updateLabel(selectedObject, el)
+        @updateLabel(selectedObject, el, @)
       if value?
         if !field.length and fieldName
           @addInput(fieldName, value)
@@ -585,8 +590,8 @@ class GitLabDropdown
       # Scroll the dropdown content up
       $dropdownContent.scrollTop(listItemTop - dropdownContentTop)
 
-  updateLabel: (selected = null, el = null) =>
-    $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
+  updateLabel: (selected = null, el = null, instance = null) =>
+    $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el, instance)
 
 $.fn.glDropdown = (opts) ->
   return @.each ->
diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/application.js.coffee
index 91f81a5d2490c0c40dd2c61d80bc2a8f4d306b97..e0f681acf0b53315089c45a62a5103e2d969a191 100644
--- a/app/assets/javascripts/graphs/application.js.coffee
+++ b/app/assets/javascripts/graphs/application.js.coffee
@@ -4,5 +4,4 @@
 # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 # the compiled file.
 #
-#= require Chart
 #= require_tree .
diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee
index d0901be1509eff7981e7592c67f14cc725106b30..0527c66461c8d9d0f5e93f1cbde5cda936ff7c9a 100644
--- a/app/assets/javascripts/issuable.js.coffee
+++ b/app/assets/javascripts/issuable.js.coffee
@@ -59,21 +59,23 @@ issuable_created = false
   filterResults: (form) =>
     formData = form.serialize()
 
-    $('.issues-holder, .merge-requests-holder').css('opacity', '0.5')
     formAction = form.attr('action')
     issuesUrl = formAction
     issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
     issuesUrl += formData
 
-    Turbolinks.visit(issuesUrl);
+    Turbolinks.visit(issuesUrl)
 
   initChecks: ->
+    @issuableBulkActions = $('.bulk-update').data('bulkActions')
+
     $('.check_all_issues').off('click').on('click', ->
       $('.selected_issue').prop('checked', @checked)
       Issuable.checkChanged()
     )
 
-    $('.selected_issue').off('change').on('change', Issuable.checkChanged)
+    $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@))
+
 
   checkChanged: ->
     checked_issues = $('.selected_issue:checked')
@@ -88,3 +90,6 @@ issuable_created = false
       $('#update_issues_ids').val []
       $('.issues_bulk_update').hide()
       $('.issues-other-filters').show()
+      @issuableBulkActions.willUpdateLabels = false
+
+    return true
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 157361404e07d1271844e34ad6d31b1da7cd8654..f446aa49cde388c6242a791b4ff37d0d9fa5a240 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -99,7 +99,7 @@ class @Issue
 
     # If the user doesn't have the required permissions the container isn't
     # rendered at all.
-    return unless $container
+    return if $container.length is 0
 
     $.getJSON($container.data('path'))
       .error ->
diff --git a/app/assets/javascripts/issue_status_select.js.coffee b/app/assets/javascripts/issue_status_select.js.coffee
index c5740f27ddd5a154a0ef94efa8a69a4b5e9adab5..ed50e2e698ff8cd4842c9bb2505a73b5770b5366 100644
--- a/app/assets/javascripts/issue_status_select.js.coffee
+++ b/app/assets/javascripts/issue_status_select.js.coffee
@@ -6,6 +6,13 @@ class @IssueStatusSelect
       $(el).glDropdown(
         selectable: true
         fieldName: fieldName
+        toggleLabel: (selected, el, instance) =>
+          label = 'Author'
+          $item = instance.dropdown.find('.is-active')
+          label = $item.text() if $item.length
+          label
+        clicked: (item, $el, e)->
+          e.preventDefault()
         id: (obj, el) ->
           $(el).data("id")
       )
diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee
index b454f9389dd789326d75dafd26303ceed476d509..6b0e69dbae7a5e52cbd9c8ce5f93b357b4e17845 100644
--- a/app/assets/javascripts/issues-bulk-assignment.js.coffee
+++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee
@@ -7,6 +7,11 @@ class @IssuableBulkActions
       @issues = @getElement('.issues-list .issue')
     } = opts
 
+    # Save instance
+    @form.data 'bulkActions', @
+
+    @willUpdateLabels = false
+
     @bindEvents()
 
     # Fixes bulk-assign not working when navigating through pages
@@ -87,11 +92,12 @@ class @IssuableBulkActions
         add_label_ids     : []
         remove_label_ids  : []
 
-    @getLabelsToApply().map (id) ->
-      formData.update.add_label_ids.push id
+    if @willUpdateLabels
+      @getLabelsToApply().map (id) ->
+        formData.update.add_label_ids.push id
 
-    @getLabelsToRemove().map (id) ->
-      formData.update.remove_label_ids.push id
+      @getLabelsToRemove().map (id) ->
+        formData.update.remove_label_ids.push id
 
     formData
 
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 6a10db10eb1254ae73cb363eefdb43dcafc0e3c9..e95fd96a83f234cf4d786ad6d0d6c938d576c97d 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -319,6 +319,8 @@ class @LabelsSelect
 
         multiSelect: $dropdown.hasClass 'js-multiselect'
         clicked: (label) ->
+          _this.enableBulkLabelDropdown()
+
           if $dropdown.hasClass('js-filter-bulk-update')
             return
 
@@ -377,3 +379,8 @@ class @LabelsSelect
       label_ids.push $("#issue_#{issue_id}").data('labels')
 
     _.intersection.apply _, label_ids
+
+  enableBulkLabelDropdown: ->
+    if $('.selected_issue:checked').length
+      issuableBulkActions = $('.bulk-update').data('bulkActions')
+      issuableBulkActions.willUpdateLabels = true
diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee
index f8f0aea427e5bf10639c6eb620286795313d32df..f639f7f589278e96a212dd4261c9496e963428a1 100644
--- a/app/assets/javascripts/layout_nav.js.coffee
+++ b/app/assets/javascripts/layout_nav.js.coffee
@@ -3,11 +3,10 @@ hideEndFade = ($scrollingTabs) ->
     $this = $(@)
 
     $this
-      .find('.fade-right')
-      .toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth'))
+      .siblings('.fade-right')
+      .toggleClass('scrolling', $this.width() < $this.prop('scrollWidth'))
 
 $ ->
-  $('.fade-left').addClass('end-scroll')
 
   hideEndFade($('.scrolling-tabs'))
 
@@ -21,5 +20,5 @@ $ ->
     currentPosition = $this.scrollLeft()
     maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
 
-    $this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
-    $this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)
+    $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0)
+    $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1)
diff --git a/app/assets/javascripts/lib/chart.js.coffee b/app/assets/javascripts/lib/chart.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..82217fc5107fd6e48665ab33e0273b2f0f62d4b7
--- /dev/null
+++ b/app/assets/javascripts/lib/chart.js.coffee
@@ -0,0 +1 @@
+#= require Chart
diff --git a/app/assets/javascripts/lib/d3.js.coffee b/app/assets/javascripts/lib/d3.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..74f0a0bb06aacc579896b9faa9df11589ed9b756
--- /dev/null
+++ b/app/assets/javascripts/lib/d3.js.coffee
@@ -0,0 +1 @@
+#= require d3
diff --git a/app/assets/javascripts/lib/raphael.js.coffee b/app/assets/javascripts/lib/raphael.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..ab8e5979b871fcbed55b5ae60fab143850882a7d
--- /dev/null
+++ b/app/assets/javascripts/lib/raphael.js.coffee
@@ -0,0 +1,3 @@
+#= require raphael
+#= require g.raphael
+#= require g.bar
diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/utils/animate.js.coffee
similarity index 100%
rename from app/assets/javascripts/lib/animate.js.coffee
rename to app/assets/javascripts/lib/utils/animate.js.coffee
diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/utils/common_utils.js.coffee
similarity index 100%
rename from app/assets/javascripts/lib/common_utils.js.coffee
rename to app/assets/javascripts/lib/utils/common_utils.js.coffee
diff --git a/app/assets/javascripts/lib/datetime_utility.js.coffee b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee
similarity index 100%
rename from app/assets/javascripts/lib/datetime_utility.js.coffee
rename to app/assets/javascripts/lib/utils/datetime_utility.js.coffee
diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
similarity index 100%
rename from app/assets/javascripts/lib/emoji_aliases.js.coffee.erb
rename to app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
diff --git a/app/assets/javascripts/lib/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js
similarity index 100%
rename from app/assets/javascripts/lib/jquery.timeago.js
rename to app/assets/javascripts/lib/utils/jquery.timeago.js
diff --git a/app/assets/javascripts/lib/md5.js b/app/assets/javascripts/lib/utils/md5.js
similarity index 100%
rename from app/assets/javascripts/lib/md5.js
rename to app/assets/javascripts/lib/utils/md5.js
diff --git a/app/assets/javascripts/lib/notify.js.coffee b/app/assets/javascripts/lib/utils/notify.js.coffee
similarity index 100%
rename from app/assets/javascripts/lib/notify.js.coffee
rename to app/assets/javascripts/lib/utils/notify.js.coffee
diff --git a/app/assets/javascripts/lib/text_utility.js.coffee b/app/assets/javascripts/lib/utils/text_utility.js.coffee
similarity index 57%
rename from app/assets/javascripts/lib/text_utility.js.coffee
rename to app/assets/javascripts/lib/utils/text_utility.js.coffee
index bb2772dfed2efa36a577e46cb91d3d4961add07f..7bcb876d056ad2c262bd453a8d4231e8f96b6ed0 100644
--- a/app/assets/javascripts/lib/text_utility.js.coffee
+++ b/app/assets/javascripts/lib/utils/text_utility.js.coffee
@@ -10,17 +10,41 @@
   gl.text.selectedText = (text, textarea) ->
     text.substring(textarea.selectionStart, textarea.selectionEnd)
 
-  gl.text.insertText = (textArea, text, tag, selected, wrap) ->
+  gl.text.lineBefore = (text, textarea) ->
+    split = text.substring(0, textarea.selectionStart).trim().split('\n')
+    split[split.length - 1]
+
+  gl.text.lineAfter = (text, textarea) ->
+    text.substring(textarea.selectionEnd).trim().split('\n')[0]
+
+  gl.text.blockTagText = (text, textArea, blockTag, selected) ->
+    lineBefore = @lineBefore(text, textArea)
+    lineAfter = @lineAfter(text, textArea)
+
+    if lineBefore is blockTag and lineAfter is blockTag
+      # To remove the block tag we have to select the line before & after
+      if blockTag?
+        textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1)
+        textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1)
+
+      selected
+    else
+      "#{blockTag}\n#{selected}\n#{blockTag}"
+
+  gl.text.insertText = (textArea, text, tag, blockTag, selected, wrap) ->
     selectedSplit = selected.split('\n')
     startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
 
-    if selectedSplit.length > 1 and not wrap
-      insertText = selectedSplit.map((val) ->
-        if val.indexOf(tag) is 0
-          "#{val.replace(tag, '')}"
-        else
-          "#{tag}#{val}"
-      ).join('\n')
+    if selectedSplit.length > 1 and (not wrap or blockTag?)
+      if blockTag?
+        insertText = @blockTagText(text, textArea, blockTag, selected)
+      else
+        insertText = selectedSplit.map((val) ->
+          if val.indexOf(tag) is 0
+            "#{val.replace(tag, '')}"
+          else
+            "#{tag}#{val}"
+        ).join('\n')
     else
       insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
 
@@ -51,7 +75,7 @@
 
       textArea.setSelectionRange pos, pos
 
-  gl.text.updateText = (textArea, tag, wrap) ->
+  gl.text.updateText = (textArea, tag, blockTag, wrap) ->
     $textArea = $(textArea)
     oldVal = $textArea.val()
     textArea = $textArea.get(0)
@@ -59,7 +83,7 @@
     selected = @selectedText(text, textArea)
     $textArea.focus()
 
-    @insertText(textArea, text, tag, selected, wrap)
+    @insertText(textArea, text, tag, blockTag, selected, wrap)
 
   gl.text.init = (form) ->
     self = @
@@ -70,6 +94,7 @@
         self.updateText(
           $this.closest('.md-area').find('textarea'),
           $this.data('md-tag'),
+          $this.data('md-block'),
           not $this.data('md-prepend')
         )
 
diff --git a/app/assets/javascripts/lib/type_utility.js.coffee b/app/assets/javascripts/lib/utils/type_utility.js.coffee
similarity index 100%
rename from app/assets/javascripts/lib/type_utility.js.coffee
rename to app/assets/javascripts/lib/utils/type_utility.js.coffee
diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/utils/url_utility.js.coffee
similarity index 100%
rename from app/assets/javascripts/lib/url_utility.js.coffee
rename to app/assets/javascripts/lib/utils/url_utility.js.coffee
diff --git a/app/assets/javascripts/lib/utf8_encode.js b/app/assets/javascripts/lib/utils/utf8_encode.js
similarity index 100%
rename from app/assets/javascripts/lib/utf8_encode.js
rename to app/assets/javascripts/lib/utils/utf8_encode.js
diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee
index 0037a3a21c268fb917600ccf9dd72cc94450162a..a19e68b39e291516d4e07fe64e46039ba38faa13 100644
--- a/app/assets/javascripts/milestone.js.coffee
+++ b/app/assets/javascripts/milestone.js.coffee
@@ -4,18 +4,10 @@ class @Milestone
       type: "PUT"
       url: issue_url
       data: data
-      success: (data) ->
-        if data.saved == true
-          if data.assignee_avatar_url
-            img_tag = $('<img/>')
-            img_tag.attr('src', data.assignee_avatar_url)
-            img_tag.addClass('avatar s16')
-            $(li).find('.assignee-icon').html(img_tag)
-          else
-            $(li).find('.assignee-icon').html('')
-          $(li).effect 'highlight'
-        else
-          new Flash("Issue update failed", 'alert')
+      success: (_data) =>
+        @successCallback(_data, li)
+      error: (data) ->
+        new Flash("Issue update failed", 'alert')
       dataType: "json"
 
   @sortIssues: (data) ->
@@ -25,9 +17,10 @@ class @Milestone
       type: "PUT"
       url: sort_issues_url
       data: data
-      success: (data) ->
-        if data.saved != true
-          new Flash("Issues update failed", 'alert')
+      success: (_data) =>
+        @successCallback(_data)
+      error: ->
+        new Flash("Issues update failed", 'alert')
       dataType: "json"
 
   @sortMergeRequests: (data) ->
@@ -37,9 +30,10 @@ class @Milestone
       type: "PUT"
       url: sort_mr_url
       data: data
-      success: (data) ->
-        if data.saved != true
-          new Flash("MR update failed", 'alert')
+      success: (_data) =>
+        @successCallback(_data)
+      error: (data) ->
+        new Flash("Issue update failed", 'alert')
       dataType: "json"
 
   @updateMergeRequest: (li, merge_request_url, data) ->
@@ -47,20 +41,23 @@ class @Milestone
       type: "PUT"
       url: merge_request_url
       data: data
-      success: (data) ->
-        if data.saved == true
-          if data.assignee_avatar_url
-            img_tag = $('<img/>')
-            img_tag.attr('src', data.assignee_avatar_url)
-            img_tag.addClass('avatar s16')
-            $(li).find('.assignee-icon').html(img_tag)
-          else
-            $(li).find('.assignee-icon').html('')
-          $(li).effect 'highlight'
-        else
-          new Flash("Issue update failed", 'alert')
+      success: (_data) =>
+        @successCallback(_data, li)
+      error: (data) ->
+        new Flash("Issue update failed", 'alert')
       dataType: "json"
 
+  @successCallback: (data, element) =>
+    if data.assignee
+      img_tag = $('<img/>')
+      img_tag.attr('src', data.assignee.avatar_url)
+      img_tag.addClass('avatar s16')
+      $(element).find('.assignee-icon').html(img_tag)
+    else
+      $(element).find('.assignee-icon').html('')
+
+    $(element).effect 'highlight'
+
   constructor: ->
     oldMouseStart = $.ui.sortable.prototype._mouseStart
     $.ui.sortable.prototype._mouseStart = (event, overrideHandle, noActivation) ->
@@ -81,8 +78,10 @@ class @Milestone
       stop: (event, ui) ->
         $(".issues-sortable-list").css "min-height", "0px"
       update: (event, ui) ->
-        data = $(this).sortable("serialize")
-        Milestone.sortIssues(data)
+        # Prevents sorting from container which element has been removed.
+        if $(this).find(ui.item).length > 0
+          data = $(this).sortable("serialize")
+          Milestone.sortIssues(data)
 
       receive: (event, ui) ->
         new_state = $(this).data('state')
diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/application.js.coffee
index cb9eead855bed6f50c05fef7af6a5cdd69af4363..f75f63869c58bba87ad7935f971d8575ca2e246c 100644
--- a/app/assets/javascripts/network/application.js.coffee
+++ b/app/assets/javascripts/network/application.js.coffee
@@ -4,9 +4,6 @@
 # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 # the compiled file.
 #
-#= require raphael
-#= require g.raphael
-#= require g.bar
 #= require_tree .
 
 $ ->
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index 96e10dd7e8ae34410f8d9f35949075cf9455f08a..3288c801388453b03653eff797cad4b2711a08b8 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -70,17 +70,20 @@ class @Project
         fieldName: 'ref'
         renderRow: (ref) ->
           if ref.header?
-            "<li class='dropdown-header'>#{ref.header}</li>"
+            $('<li />')
+              .addClass('dropdown-header')
+              .text(ref.header)
           else
-            isActiveClass = if ref is selected then 'is-active' else ''
-
-            "<li>
-              <a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'>
-                #{ref}
-              </a>
-            </li>"
+            link = $('<a />')
+              .attr('href', '#')
+              .addClass(if ref is selected then 'is-active' else '')
+              .text(ref)
+              .attr('data-ref', escape(ref))
+
+            $('<li />')
+              .append(link)
         id: (obj, $el) ->
-          $el.data('ref')
+          $el.attr('data-ref')
         toggleLabel: (obj, $el) ->
           $el.text().trim()
         clicked: (e) ->
diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/application.js.coffee
index 647ffbf5f45f53ef2a11be07272c7ccc835d2265..91cacfece463abccaffbc9dc2bc27074e6719436 100644
--- a/app/assets/javascripts/users/application.js.coffee
+++ b/app/assets/javascripts/users/application.js.coffee
@@ -1,8 +1,2 @@
-# This is a manifest file that'll be compiled into including all the files listed below.
-# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
-# be included in the compiled file accessible from http://example.com/assets/application.js
-# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-# the compiled file.
 #
-#= require d3
 #= require_tree .
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index a7bcb4565606a7cbf1e2b549a5cd244fada6e26f..c32ce5195c6fffdc0e618ecca09deb4c1634ef53 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -26,7 +26,6 @@ header {
       text-align: center;
 
       #tanuki-logo, img {
-        width: 36px;
         height: 36px;
       }
     }
@@ -132,6 +131,10 @@ header {
       transition-duration: .3s;
       z-index: 999;
 
+      svg, img {
+        height: 36px;
+      }
+
       &:hover {
         cursor: pointer;
       }
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 0281b06d3ba3e874358c892cbd47b8c6b9d459eb..6e5f216c894a4c6213e6fc83dadb4a5a650ee782 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -1,6 +1,6 @@
 @mixin fade($gradient-direction, $rgba, $gradient-color) {
-  visibility: visible;
-  opacity: 1;
+  visibility: hidden;
+  opacity: 0;
   z-index: 2;
   position: absolute;
   bottom: 12px;
@@ -13,17 +13,16 @@
   background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
   background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
 
-  &.end-scroll {
-    visibility: hidden;
-    opacity: 0;
+  &.scrolling {
+    visibility: visible;
+    opacity: 1;
     transition-duration: .3s;
   }
 
   .fa {
     position: relative;
-    top: 3px;
-    font-size: 13px;
-    color: $btn-placeholder-gray;
+    top: 5px;
+    font-size: 18px;
   }
 }
 
@@ -32,6 +31,7 @@
   overflow-x: auto;
   overflow-y: hidden;
   -webkit-overflow-scrolling: touch;
+
   &::-webkit-scrollbar {
     display: none;
   }
@@ -272,7 +272,7 @@
     float: right;
     padding: 7px 0 0;
 
-    @media (max-width: $screen-xs-max) {
+    @media (max-width: $screen-sm-max) {
       display: none;
     }
 
@@ -303,41 +303,9 @@
   }
 
   .nav-links {
-    @include scrolling-links();
     border-bottom: none;
     height: 51px;
 
-    svg {
-      position: relative;
-      top: 2px;
-      margin-right: 2px;
-      height: 15px;
-      width: auto;
-
-      path,
-      polygon {
-        fill: $layout-link-gray;
-      }
-    }
-
-    .fade-right {
-      @include fade(left, rgba(250, 250, 250, 0.4), $background-color);
-      right: 0;
-
-      .fa {
-        right: -7px;
-      }
-    }
-
-    .fade-left {
-      @include fade(right, rgba(250, 250, 250, 0.4), $background-color);
-      left: 0;
-
-      .fa {
-        left: -7px;
-      }
-    }
-
     li {
 
       a {
@@ -373,18 +341,6 @@
       }
     }
   }
-
-  .nav-control {
-
-    .fade-right {
-      @media (min-width: $screen-xs-max) {
-        right: 68px;
-      }
-      @media (max-width: $screen-xs-min) {
-        right: 0;
-      }
-    }
-  }
 }
 
 .scrolling-tabs-container {
@@ -392,15 +348,42 @@
 
   .nav-links {
     @include scrolling-links();
+  }
+
+  .fade-right {
+    @include fade(left, rgba(255, 255, 255, 0.4), $background-color);
+    right: -5px;
+
+    .fa {
+      right: -7px;
+    }
+  }
+
+  .fade-left {
+    @include fade(right, rgba(255, 255, 255, 0.4), $background-color);
+    left: -5px;
+
+    .fa {
+      left: -7px;
+    }
+  }
+
+  &.sub-nav-scroll {
 
     .fade-right {
-      @include fade(left, rgba(255, 255, 255, 0.4), $background-color);
       right: 0;
+
+      .fa {
+        right: -23px;
+      }
     }
 
     .fade-left {
-      @include fade(right, rgba(255, 255, 255, 0.4), $background-color);
       left: 0;
+
+      .fa {
+        left: 10px;
+      }
     }
   }
 }
@@ -413,21 +396,19 @@
 
     .fade-right {
       @include fade(left, rgba(255, 255, 255, 0.4), $white-light);
-      right: 0;
+      right: -5px;
+
+      .fa {
+        right: -7px;
+      }
     }
 
     .fade-left {
       @include fade(right, rgba(255, 255, 255, 0.4), $white-light);
-      left: 0;
-    }
-
-    &.event-filter {
-      .fade-right {
-        visibility: hidden;
+      left: -5px;
 
-        @media (max-width: $screen-xs-max) {
-          visibility: visible;
-        }
+      .fa {
+        left: -7px;
       }
     }
   }
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 98f917ce69b5c64df22c68286801d12c520512ea..e8d6a7f2775d4ec4caea5241a971cb60676b9e0f 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -1,5 +1,6 @@
 .page-with-sidebar {
   padding-top: $header-height;
+  padding-bottom: 25px;
   transition: padding $sidebar-transition-duration;
 
   .sidebar-wrapper {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index c37574ca7a1b9d9494b542c46dcb204b18915805..87f8a17659fce4d8562740f4d9b1f840e62a3902 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px;
 $gutter_width: 290px;
 $gutter_inner_width: 258px;
 $sidebar-transition-duration: .15s;
-$sidebar-breakpoint: 1440px;
+$sidebar-breakpoint: 1280px;
 
 /*
  * UI elements
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index 6211f3a52eb7f665b4d6103b83a08f5613f4526d..5faedfedd660fe8bcafab778184a3dd81b260a30 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -8,8 +8,9 @@
 .emoji-menu {
   position: absolute;
   margin-top: 3px;
-  z-index: 1000;
-  min-width: 160px;
+  padding: $gl-padding;
+  z-index: 9;
+  width: 300px;
   font-size: 14px;
   background-color: $award-emoji-menu-bg;
   border: 1px solid $award-emoji-menu-border;
@@ -33,20 +34,18 @@
   }
 
   .emoji-menu-content {
-    padding: $gl-padding;
-    width: 300px;
     height: 300px;
     overflow-y: scroll;
-
-    input.emoji-search {
-      background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC");
-      background-repeat: no-repeat;
-      background-position: right 5px center;
-      background-size: 16px;
-    }
   }
 }
 
+.emoji-search {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC");
+  background-repeat: no-repeat;
+  background-position: right 5px center;
+  background-size: 16px;
+}
+
 .emoji-menu-list {
   list-style: none;
   padding-left: 0;
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 0b710ef168b63e34e959ec8208de0180806fcdfe..00ab42bec5cb75c36c0efcf35029c8d6f0bc5d1b 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -63,5 +63,6 @@
     border: 1px solid $table-border-gray;
     padding: 5px;
     margin: 5px;
+    max-height: calc(100vh - 100px);
   }
 }
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 21ff6ab71f09dbcab8a7a6ba3978015da683b669..542fa244689ac63dda5428540aac9667c3892a22 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -10,6 +10,7 @@
     border: 1px solid $table-border-gray;
     padding: 5px;
     margin: 5px;
+    max-height: calc(100vh - 100px);
   }
 }
 
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index f5f67e2cd84e2401a8c35c3ef10a9a756f93b6ba..47bfd144930da55caa917262f819597d015948e2 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -6,6 +6,7 @@
     height: 30px;
     display: inline-block;
     margin-right: 10px;
+    margin-bottom: 10px;
   }
 
   &.suggest-colors-dropdown {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index aca82f7f7bf082b55913b4eb54100e77e6762168..124f4afaa0d497b7269bc75bbd108af0912a4f07 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -264,8 +264,15 @@
       margin-bottom: 4px;
     }
 
+    .item-title {
+      @media (min-width: $screen-sm-min) {
+        width: 49%;
+      }
+    }
+
     .avatar {
-      margin-left: 0;
+      left: 0;
+      top: 2px;
     }
 
     .commit-row-info {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index ffba3dc5bc68a8bf99ae9f553dc19a8baad1e73f..ac8c02b59dcbe7a10085d50ec4617ac2f8c69a8b 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -41,6 +41,10 @@ ul.notes {
     .timeline-icon {
       .avatar {
         visibility: hidden;
+
+        .discussion-body & {
+          visibility: visible;
+        }
       }
     }
   }
@@ -113,6 +117,7 @@ ul.notes {
           border: 1px solid $table-border-gray;
           padding: 5px;
           margin: 5px 0;
+          max-height: calc(100vh - 100px);
         }
       }
     }
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d3e59d7fdb95415f385059c5cc8fb58b6d63bcbb..89ce1b2df2093a8b6f327496cc59e577abc92334 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -13,10 +13,53 @@
 
 .new_project,
 .edit-project {
-  fieldset.features {
-    .control-label {
+  fieldset {
+    &.features .control-label {
       font-weight: normal;
     }
+    .form-group {
+      margin-bottom: 5px;
+    }
+    &> .form-group {
+      padding-left: 0;
+    }
+  }
+  .help-block {
+    margin-bottom: 10px;
+  }
+  .project-path {
+    padding-right: 0;
+    .form-control {
+      border-radius: $border-radius-base;
+    }
+  }
+  .input-group > div {
+    &:last-child {
+      padding-right: 0;
+    }
+  }
+  @media (max-width: $screen-xs-max) {
+    .input-group > div {
+      margin-bottom: 14px;
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+    fieldset > .form-group:first-child {
+      padding-right: 0;
+    }
+  }
+
+  .input-group-addon {
+    &.static-namespace {
+      height: 35px;
+      border-radius: 3px;
+      border: 1px solid #e5e5e5;
+    }
+    &+ .select2 a {
+      border-top-left-radius: 0;
+      border-bottom-left-radius: 0;
+    }
   }
 }
 
@@ -365,10 +408,28 @@ a.deploy-project-label {
   }
 }
 
-.project-import .btn {
-  float: left;
-  margin-bottom: 10px;
-  margin-right: 10px;
+.project-import {
+  .form-group {
+    margin-bottom: 0;
+  }
+  .import-buttons {
+    padding-left: 0;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-flex-wrap: wrap;
+    flex-wrap: wrap;
+    .btn {
+      margin-right: 10px;
+      padding: 8px 12px;
+    }
+    &> div {
+      margin-bottom: 14px;
+      padding-left: 0;
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
 }
 
 .project-stats {
diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c67370b667beb7bf44ab593a0d5df9c0c0bbb87
--- /dev/null
+++ b/app/controllers/admin/system_info_controller.rb
@@ -0,0 +1,13 @@
+class Admin::SystemInfoController < Admin::ApplicationController
+  def show
+    system_info = Vmstat.snapshot
+
+    @cpus = system_info.cpus.length
+
+    @mem_used = system_info.memory.active_bytes
+    @mem_total = system_info.memory.total_bytes
+
+    @disk_used = system_info.disks[0].used_bytes
+    @disk_total = system_info.disks[0].total_bytes
+  end
+end
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 71ba6153021fb1919044288de79a4a8a7e9f3e16..de6bc689bb7d6fa208c7692d9d0ffc4e1e56f05a 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,5 +1,5 @@
 class Dashboard::GroupsController < Dashboard::ApplicationController
   def index
-    @group_members = current_user.group_members.page(params[:page])
+    @group_members = current_user.group_members.includes(:source).page(params[:page])
   end
 end
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index f99aa490d3eafa31af0afe2d27dff1b7ac62bfb2..513348c39af2aa30ab3956476e11c5d2ae6135d6 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -12,9 +12,13 @@ def create
       return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
     end
 
+    imported_file = project_params[:file].path + "-import"
+
+    FileUtils.copy_entry(project_params[:file].path, imported_file)
+
     @project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
                                                         current_user,
-                                                        File.expand_path(project_params[:file].path),
+                                                        File.expand_path(imported_file),
                                                         project_params[:path]).execute
 
     if @project.saved?
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index cd8b2911674b78cac69bdee0835f5da98e0f5a22..7599fec3cdf26ae30e95a74b017a0d4ace4634f6 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -16,6 +16,7 @@ class InvalidPathError < StandardError; end
   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
 
   def new
     commit unless @repository.empty?
@@ -146,4 +147,10 @@ def editor_variables
       file_content_encoding: params[:encoding]
     }
   end
+
+  def validate_diff_params
+    if [:since, :to, :offset].any? { |key| params[key].blank? }
+      render nothing: true
+    end
+  end
 end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 6751737d15ea7d7e00a4c1d5ba7af2c4f36d6153..d162a5a3165b1f5805191fe4165b380bbcb764ba 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -18,9 +18,16 @@ def show
     apply_diff_view_cookie!
 
     @grouped_diff_notes = commit.notes.grouped_diff_notes
+    @notes = commit.notes.non_diff_notes.fresh
+    
+    Banzai::NoteRenderer.render(
+      @grouped_diff_notes.values.flatten + @notes,
+      @project,
+      current_user,
+    )
 
     @note = @project.build_commit_note(commit)
-    @notes = commit.notes.non_diff_notes.fresh
+
     @noteable = @commit
     @comments_target = {
       noteable_type: 'Commit',
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 4e2d3bebb2e79c385c83e80fb57af7439b7002d3..8b8df6807397b8fb8048686bfa16f13ec6c5e3c1 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -62,8 +62,12 @@ def edit
   end
 
   def show
+    raw_notes = @issue.notes_with_associations.fresh
+
+    @notes = Banzai::NoteRenderer.
+      render(raw_notes, @project, current_user, @path, @project_wiki, @ref)
+
     @note     = @project.notes.new(noteable: @issue)
-    @notes    = @issue.notes.with_associations.fresh
     @noteable = @issue
 
     respond_to do |format|
@@ -111,6 +115,7 @@ def update
           render :edit
         end
       end
+
       format.json do
         render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
       end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 851822d805a21682669e2fa0fa9d4a1776fb4dde..dd86b940a08c62ae61e011ab4cb45b42854ad823 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -59,7 +59,13 @@ def show
     respond_to do |format|
       format.html
       format.json   { render json: @merge_request }
-      format.patch  { render text: @merge_request.to_patch }
+      format.patch  do
+        headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository,
+                                                        @merge_request.diff_base_commit.id,
+                                                        @merge_request.last_commit.id))
+        headers['Content-Disposition'] = 'inline'
+        head :ok
+      end
       format.diff do
         return render_404 unless @merge_request.diff_refs
 
@@ -85,6 +91,15 @@ def diffs
 
     @grouped_diff_notes = @merge_request.notes.grouped_diff_notes
 
+    Banzai::NoteRenderer.render(
+      @grouped_diff_notes.values.flatten,
+      @project,
+      current_user,
+      @path,
+      @project_wiki,
+      @ref
+    )
+
     respond_to do |format|
       format.html
       format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
@@ -190,7 +205,9 @@ def cancel_merge_when_build_succeeds
   def merge
     return access_denied! unless @merge_request.can_be_merged_by?(current_user)
 
-    unless @merge_request.mergeable?
+    # Disable the CI check if merge_when_build_succeeds is enabled since we have
+    # to wait until CI completes to know
+    unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
       @status = :failed
       return
     end
@@ -204,8 +221,13 @@ def merge
 
     @merge_request.update(merge_error: nil)
 
-    if params[:merge_when_build_succeeds].present? 
-      if @merge_request.pipeline && @merge_request.pipeline.active?
+    if params[:merge_when_build_succeeds].present?
+      unless @merge_request.pipeline
+        @status = :failed
+        return
+      end
+
+      if @merge_request.pipeline.active?
         MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
                                                         .execute(@merge_request)
         @status = :merge_when_build_succeeds
@@ -320,8 +342,21 @@ def validates_merge_request
   def define_show_vars
     # Build a note object for comment form
     @note = @project.notes.new(noteable: @merge_request)
-    @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
-    @discussions = @notes.discussions
+
+    @discussions = @merge_request.mr_and_commit_notes.
+      inc_author_project_award_emoji.
+      fresh.
+      discussions
+
+    @notes = Banzai::NoteRenderer.render(
+      @discussions.flatten,
+      @project,
+      current_user,
+      @path,
+      @project_wiki,
+      @ref
+    )
+
     @noteable = @merge_request
 
     # Get commits from repository
@@ -368,4 +403,9 @@ def merge_params
   def ensure_ref_fetched
     @merge_request.ensure_ref_fetched
   end
+
+  def merge_when_build_succeeds_active?
+    params[:merge_when_build_succeeds].present? &&
+      @merge_request.pipeline && @merge_request.pipeline.active?
+  end
 end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 836f79ff0803a936c7d7cc93f02cb382ceda95b8..e14fe26dde7812af60cd2f1d6eb682b67a0be009 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -24,6 +24,10 @@ def index
   def create
     @note = Notes::CreateService.new(project, current_user, note_params).execute
 
+    if @note.is_a?(Note)
+      Banzai::NoteRenderer.render([@note], @project, current_user)
+    end
+
     respond_to do |format|
       format.json { render json: note_json(@note) }
       format.html { redirect_back_or_default }
@@ -33,6 +37,10 @@ def create
   def update
     @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
 
+    if @note.is_a?(Note)
+      Banzai::NoteRenderer.render([@note], @project, current_user)
+    end
+
     respond_to do |format|
       format.json { render json: note_json(@note) }
       format.html { redirect_back_or_default }
@@ -118,6 +126,8 @@ def note_json(note)
         name:   note.name
       }
     elsif note.valid?
+      Banzai::NoteRenderer.render([note], @project, current_user)
+
       {
         valid: true,
         id: note.id,
diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb
index c19a795d467ac9156d160d93ceee3adb86835a9c..641fbf838f143d89cf68ed2ce4b875c91aabc5da 100644
--- a/app/finders/pipelines_finder.rb
+++ b/app/finders/pipelines_finder.rb
@@ -29,10 +29,10 @@ def from_ids(pipelines, ids)
   end
 
   def branches
-    project.repository.branches.map(&:name)
+    project.repository.branch_names
   end
 
   def tags
-    project.repository.tags.map(&:name)
+    project.repository.tag_names
   end
 end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 418598418348c3ed5220b9107d704d4fc1435f5b..62d13a4b4f326f11125888aa9eacae55cb495838 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -197,7 +197,7 @@ def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago
 
   def render_markup(file_name, file_content)
     if gitlab_markdown?(file_name)
-      Haml::Helpers.preserve(markdown(file_content))
+      Hamlit::RailsHelpers.preserve(markdown(file_content))
     elsif asciidoc?(file_name)
       asciidoc(file_content)
     elsif plain?(file_name)
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 4b4bc3d4276b8e85de9333086b8d26a91150b2d8..428a42266d0e12ec484a5b78b0be596ad0acce83 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,10 +1,10 @@
 module BlobHelper
-  def highlighter(blob_name, blob_content, nowrap: false)
-    Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap)
+  def highlighter(blob_name, blob_content, repository: nil, nowrap: false)
+    Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository)
   end
 
-  def highlight(blob_name, blob_content, nowrap: false, plain: false)
-    Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain)
+  def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
+    Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
   end
 
   def no_highlight_files
diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb
index 91dd91718dcc920f33f75069227681b29f520558..0e456214d37653baf79aa6e74f6d896bc99be448 100644
--- a/app/helpers/javascript_helper.rb
+++ b/app/helpers/javascript_helper.rb
@@ -1,7 +1,5 @@
 module JavascriptHelper
-  def page_specific_javascripts(js = nil)
-    @page_specific_javascripts = js unless js.nil?
-
-    @page_specific_javascripts
+  def page_specific_javascript_tag(js)
+    javascript_include_tag asset_path(js), { "data-turbolinks-track" => true }
   end
 end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 9c58b956007ca228367bc038765b297f11f4e678..f5950879ccb5ce3637c12d228d61bbbab2b11190 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -196,7 +196,8 @@ def public_project_rules
       @public_project_rules ||= project_guest_rules + [
         :download_code,
         :fork_project,
-        :read_commit_status
+        :read_commit_status,
+        :read_pipeline
       ]
     end
 
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index ca5a685dd1132906c04ad32f20ccfb11d31070f7..10324bf22579ccffa360b974198fc45465adfbe0 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -163,13 +163,26 @@ def ci_yaml_file
     end
 
     def skip_ci?
-      git_commit_message =~ /(\[ci skip\])/ if git_commit_message
+      git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message
     end
 
     def environments
       builds.where.not(environment: nil).success.pluck(:environment).uniq
     end
 
+    # Manually set the notes for a Ci::Pipeline
+    # There is no ActiveRecord relation between Ci::Pipeline and notes
+    # as they are related to a commit sha. This method helps importing
+    # them using the +Gitlab::ImportExport::RelationFactory+ class.
+    def notes=(notes)
+      notes.each do |note|
+        note[:id] = nil
+        note[:commit_id] = sha
+        note[:noteable_id] = self['id']
+        note.save!
+      end
+    end
+
     def notes
       Note.for_commit_id(sha)
     end
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index f8d5d4486fd43f93c20dea83e76cb8854d2f1099..c9c47ec7419641462301456fe88268bd77ed57ba 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -13,6 +13,7 @@ class Variable < ActiveRecord::Base
 
     attr_encrypted :value, 
        mode: :per_attribute_iv_and_salt,
+       insecure_mode: true,
        key: Gitlab::Application.secrets.db_key_base,
        algorithm: 'aes-256-cbc'
   end
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index 539c7c31e30f1ee8c42c70bae9716742fb4efbd3..06beff177b17fa371edd628f0c7f96120a8d9266 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -2,10 +2,11 @@ module Awardable
   extend ActiveSupport::Concern
 
   included do
-    has_many :award_emoji, as: :awardable, dependent: :destroy
+    has_many :award_emoji, -> { includes(:user) }, as: :awardable, dependent: :destroy
 
     if self < Participable
-      participant :award_emoji_with_associations
+      # By default we always load award_emoji user association
+      participant :award_emoji
     end
   end
 
@@ -34,12 +35,9 @@ def order_votes_desc(emoji_name)
     end
   end
 
-  def award_emoji_with_associations
-    award_emoji.includes(:user)
-  end
-
   def grouped_awards(with_thumbs: true)
-    awards = award_emoji_with_associations.group_by(&:name)
+    # By default we always load award_emoji user association
+    awards = award_emoji.group_by(&:name)
 
     if with_thumbs
       awards[AwardEmoji::UPVOTE_NAME]   ||= []
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 0ccd3474b81027340d99300b68576c8e4800bbd0..d6f55885dd6c57200361905aa82806a5e92f8e5d 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -19,9 +19,14 @@ module Issuable
     belongs_to :milestone
     has_many :notes, as: :noteable, dependent: :destroy do
       def authors_loaded?
-        # We check first if we're loaded to not load unnecesarily.
+        # We check first if we're loaded to not load unnecessarily.
         loaded? && to_a.all? { |note| note.association(:author).loaded? }
       end
+
+      def award_emojis_loaded?
+        # We check first if we're loaded to not load unnecessarily.
+        loaded? && to_a.all? { |note| note.association(:award_emoji).loaded? }
+      end
     end
     has_many :label_links, as: :target, dependent: :destroy
     has_many :labels, through: :label_links
@@ -49,7 +54,7 @@ def authors_loaded?
 
     scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
     scope :join_project, -> { joins(:project) }
-    scope :inc_notes_with_associations, -> { includes(notes: :author) }
+    scope :inc_notes_with_associations, -> { includes(notes: [ :project, :author, :award_emoji ]) }
     scope :references_project, -> { references(:project) }
     scope :non_archived, -> { join_project.where(projects: { archived: false }) }
 
@@ -112,15 +117,18 @@ def full_search(query)
     end
 
     def sort(method, excluded_labels: [])
-      case method.to_s
-      when 'milestone_due_asc' then order_milestone_due_asc
-      when 'milestone_due_desc' then order_milestone_due_desc
-      when 'downvotes_desc' then order_downvotes_desc
-      when 'upvotes_desc' then order_upvotes_desc
-      when 'priority' then order_labels_priority(excluded_labels: excluded_labels)
-      else
-        order_by(method)
-      end
+      sorted = case method.to_s
+               when 'milestone_due_asc' then order_milestone_due_asc
+               when 'milestone_due_desc' then order_milestone_due_desc
+               when 'downvotes_desc' then order_downvotes_desc
+               when 'upvotes_desc' then order_upvotes_desc
+               when 'priority' then order_labels_priority(excluded_labels: excluded_labels)
+               else
+                 order_by(method)
+               end
+
+      # Break ties with the ID column for pagination
+      sorted.order(id: :desc)
     end
 
     def order_labels_priority(excluded_labels: [])
@@ -257,7 +265,14 @@ def notes_with_associations
     # already have their authors loaded (possibly because the scope
     # `inc_notes_with_associations` was used) and skip the inclusion if that's
     # the case.
-    notes.authors_loaded? ? notes : notes.includes(:author)
+    includes = []
+    includes << :author unless notes.authors_loaded?
+    includes << :award_emoji unless notes.award_emojis_loaded?
+    if includes.any?
+      notes.includes(includes)
+    else
+      notes
+    end
   end
 
   def updated_tasks
diff --git a/app/models/event.rb b/app/models/event.rb
index 716039fb54b9533477c5be3fd286124b2f28086e..d7d23c7ae6dc236c96f907222fbb00e542a20d68 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -315,7 +315,7 @@ def note_target_type
 
   def body?
     if push?
-      push_with_commits?
+      push_with_commits? || rm_ref?
     elsif note?
       true
     else
diff --git a/app/models/group.rb b/app/models/group.rb
index e66e04371b2c4a66cd805fdbe82ce26c0e35f91c..c70c719e33856d4e24c7d17b2ac876e27616788f 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -11,7 +11,7 @@ class Group < Namespace
   has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members
 
   has_many :owners,
-    -> { where(members: { access_level: Gitlab::Access::OWNER }) },
+    -> { where(members: { requested_at: nil, access_level: Gitlab::Access::OWNER }) },
     through: :group_members,
     source: :user
 
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 95fd510eb3a7460d6546192f7140473487498bb1..33d2a69ebaffaa74d9c362f35fec0923305b748a 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -20,7 +20,7 @@ def legacy_diff_note?
   end
 
   def discussion_id
-    @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code, active?)
+    @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
   end
 
   def diff_file_hash
diff --git a/app/models/member.rb b/app/models/member.rb
index c74a16367dbd9f0f8b27e071b6096ef8fceac8bf..57161397e2bc244bea742a65c070f88d1b82b473 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -32,6 +32,7 @@ class Member < ActiveRecord::Base
   scope :request, -> { where.not(requested_at: nil) }
   scope :non_request, -> { where(requested_at: nil) }
   scope :non_pending, -> { non_request.non_invite }
+  scope :has_access, -> { where('access_level > 0') }
 
   scope :guests, -> { where(access_level: GUEST) }
   scope :reporters, -> { where(access_level: REPORTER) }
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 36bc98bdb1e1a18cee0972225acc48eeb7fa1ecd..53d9aa588af8bba38d0279f7ac4d50774f686606 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -264,19 +264,19 @@ def wipless_title
     self.title.sub(WIP_REGEX, "")
   end
 
-  def mergeable?
-    return false unless mergeable_state?
+  def mergeable?(skip_ci_check: false)
+    return false unless mergeable_state?(skip_ci_check: skip_ci_check)
 
     check_if_can_be_merged
 
     can_be_merged?
   end
 
-  def mergeable_state?
+  def mergeable_state?(skip_ci_check: false)
     return false unless open?
     return false if work_in_progress?
     return false if broken?
-    return false unless mergeable_ci_state?
+    return false unless skip_ci_check || mergeable_ci_state?
 
     true
   end
@@ -319,13 +319,6 @@ def mr_and_commit_notes
     )
   end
 
-  # Returns the commit as a series of email patches.
-  #
-  # see "git format-patch"
-  def to_patch
-    target_project.repository.format_patch(diff_base_commit.sha, source_sha)
-  end
-
   def hook_attrs
     attrs = {
       source: source_project.try(:hook_attrs),
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index aca377cc6001b677660799a165ffdf396a6f2c2a..86331a33c051db7afbf978e2e342d42306a86f24 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -108,44 +108,46 @@ def unmerged_commits
   # Reload all commits related to current merge request from repo
   # and save it as array of hashes in st_commits db field
   def reload_commits
+    new_attributes = {}
+
     commit_objects = unmerged_commits
 
     if commit_objects.present?
-      self.st_commits = dump_commits(commit_objects)
+      new_attributes[:st_commits] = dump_commits(commit_objects)
     end
 
-    save
+    update_columns_serialized(new_attributes)
   end
 
   # Reload diffs between branches related to current merge request from repo
   # and save it as array of hashes in st_diffs db field
   def reload_diffs
+    new_attributes = {}
     new_diffs = []
 
     if commits.size.zero?
-      self.state = :empty
+      new_attributes[:state] = :empty
     else
       diff_collection = unmerged_diffs
 
       if diff_collection.overflow?
         # Set our state to 'overflow' to make the #empty? and #collected?
         # methods (generated by StateMachine) return false.
-        self.state = :overflow
+        new_attributes[:state] = :overflow
       end
 
-      self.real_size = diff_collection.real_size
+      new_attributes[:real_size] = diff_collection.real_size
 
       if diff_collection.any?
         new_diffs = dump_diffs(diff_collection)
-        self.state = :collected
+        new_attributes[:state] = :collected
       end
     end
 
-    self.st_diffs = new_diffs
-
-    self.base_commit_sha = self.repository.merge_base(self.head, self.base)
+    new_attributes[:st_diffs] = new_diffs
+    new_attributes[:base_commit_sha] = self.repository.merge_base(self.head, self.base)
 
-    self.save
+    update_columns_serialized(new_attributes)
   end
 
   # Collect array of Git::Diff objects
@@ -190,4 +192,29 @@ def compare
         )
       end
   end
+
+  private
+
+  #
+  # #save or #update_attributes providing changes on serialized attributes do a lot of
+  # serialization and deserialization calls resulting in bad performance.
+  # Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide.
+  # As a tradeoff we need to reload the current instance to properly manage time objects on those serialized
+  # attributes. So to keep the same behaviour as the attribute assignment we reload the instance.
+  # The difference is in the usage of
+  # #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns)
+  #
+  # Ex:
+  #
+  #   new_attributes[:st_commits].first.slice(:committed_date)
+  #   => {:committed_date=>2014-02-27 11:01:38 +0200}
+  #   YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date)))
+  #   => {:committed_date=>2014-02-27 10:01:38 +0100}
+  #
+  def update_columns_serialized(new_attributes)
+    return unless new_attributes.any?
+
+    update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone))
+    reload
+  end
 end
diff --git a/app/models/note.rb b/app/models/note.rb
index 8d1646475500ae26c822e32089e3b3ea6dfb219d..8db500a521997532333cf5843870dfcc84c81092 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -6,6 +6,10 @@ class Note < ActiveRecord::Base
   include Awardable
   include Importable
 
+  # Attribute containing rendered and redacted Markdown as generated by
+  # Banzai::ObjectRenderer.
+  attr_accessor :note_html
+
   default_value_for :system, false
 
   attr_mentionable :note, pipeline: :note
@@ -49,11 +53,13 @@ class Note < ActiveRecord::Base
   scope :fresh, ->{ order(created_at: :asc, id: :asc) }
   scope :inc_author_project, ->{ includes(:project, :author) }
   scope :inc_author, ->{ includes(:author) }
+  scope :inc_author_project_award_emoji, ->{ includes(:project, :author, :award_emoji) }
 
   scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') }
   scope :non_diff_notes, ->{ where(type: ['Note', nil]) }
 
   scope :with_associations, -> do
+    # FYI noteable cannot be loaded for LegacyDiffNote for commits
     includes(:author, :noteable, :updated_by,
              project: [:project_members, { group: [:group_members] }])
   end
diff --git a/app/models/project.rb b/app/models/project.rb
index ca3bc04e2dda54d116e0e22e80856e4be3158b8a..73ded09c162ed50b3ba1d71c2cb12bb526730d05 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -81,6 +81,7 @@ def update_forks_visibility_level
   has_one :jira_service, dependent: :destroy
   has_one :redmine_service, dependent: :destroy
   has_one :custom_issue_tracker_service, dependent: :destroy
+  has_one :bugzilla_service, dependent: :destroy
   has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
   has_one :external_wiki_service, dependent: :destroy
 
@@ -163,6 +164,7 @@ def update_forks_visibility_level
   validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
   validate :visibility_level_allowed_by_group
   validate :visibility_level_allowed_as_fork
+  validate :check_wiki_path_conflict
 
   add_authentication_token_field :runners_token
   before_save :ensure_runners_token
@@ -539,6 +541,16 @@ def visibility_level_allowed_as_fork
     self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.")
   end
 
+  def check_wiki_path_conflict
+    return if path.blank?
+
+    path_to_check = path.ends_with?('.wiki') ? path.chomp('.wiki') : "#{path}.wiki"
+
+    if Project.where(namespace_id: namespace_id, path: path_to_check).exists?
+      errors.add(:name, 'has already been taken')
+    end
+  end
+
   def to_param
     path
   end
diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb
index ca8a9b4217b6e5334cf52d8f0bec6fd9047a5190..331123a5a5b58ca4c83a9963234442b452a5d5db 100644
--- a/app/models/project_import_data.rb
+++ b/app/models/project_import_data.rb
@@ -7,6 +7,7 @@ class ProjectImportData < ActiveRecord::Base
                  marshal: true,
                  encode: true,
                  mode: :per_attribute_iv_and_salt,
+                 insecure_mode: true,
                  algorithm: 'aes-256-cbc'
 
   serialize :data, JSON
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..260f60309576112905a5aa37598730310729e081
--- /dev/null
+++ b/app/models/project_services/bugzilla_service.rb
@@ -0,0 +1,25 @@
+class BugzillaService < IssueTrackerService
+
+  prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+  def title
+    if self.properties && self.properties['title'].present?
+      self.properties['title']
+    else
+      'Bugzilla'
+    end
+  end
+
+  def description
+    if self.properties && self.properties['description'].present?
+      self.properties['description']
+    else
+      'Bugzilla issue tracker'
+    end
+  end
+
+  def to_param
+    'bugzilla'
+  end
+
+end
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index 6b2b1daa724bab5559831c72abe931150911796a..8f2db46a7ba3832effcdeb8a28ce82e8e5e3839f 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -32,7 +32,4 @@ def fields
     ]
   end
 
-  def initialize_properties
-    self.properties = {} if properties.nil?
-  end
 end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 73e736820af25d81fcc2ba9b0eb65a3be44ea222..0865b979ce052cf6dc56e45db69e4e77f553199d 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -137,20 +137,10 @@ def human_max_access(user_id)
   def max_member_access(user_id)
     access = []
 
-    project.members.non_request.each do |member|
-      if member.user_id == user_id
-        access << member.access_field if member.access_field
-        break
-      end
-    end
+    access += project.members.non_request.where(user_id: user_id).has_access.pluck(:access_level)
 
     if group
-      group.members.non_request.each do |member|
-        if member.user_id == user_id
-          access << member.access_field if member.access_field
-          break
-        end
-      end
+      access += group.members.non_request.where(user_id: user_id).has_access.pluck(:access_level)
     end
 
     if project.invited_groups.any? && project.allowed_to_share_with_group?
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 221c87164caa4006b2ba2045c2a699430d45e6f0..2a6a3b086c200282120f5bf5a0e3c4c67b336d85 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -130,7 +130,7 @@ def find_branch(name)
   end
 
   def find_tag(name)
-    raw_repository.tags.find { |tag| tag.name == name }
+    tags.find { |tag| tag.name == name }
   end
 
   def add_branch(user, branch_name, target)
@@ -978,6 +978,10 @@ def ls_files(ref)
     raw_repository.ls_files(actual_ref)
   end
 
+  def gitattribute(path, name)
+    raw_repository.attributes(path)[name]
+  end
+
   def copy_gitattributes(ref)
     actual_ref = ref || root_ref
     begin
diff --git a/app/models/service.rb b/app/models/service.rb
index 40d39933ad873ededeee4281602bd4462c839ed5..d7a32c282679221a095f523ee0b6bdb08e2517c3 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -170,6 +170,7 @@ def self.available_services_names
       bamboo
       buildkite
       builds_email
+      bugzilla
       campfire
       custom_issue_tracker
       drone_ci
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index f8034cb5e6b5f1a2a7dc7e7054f6233151f728a0..5ec933601ac8d2a8486e54ce90722587a748a549 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base
     length: { within: 0..255 },
     format: { with: Gitlab::Regex.file_name_regex,
               message: Gitlab::Regex.file_name_regex_message }
+
   validates :content, presence: true
   validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
 
@@ -81,6 +82,11 @@ def size
     0
   end
 
+  # alias for compatibility with blobs and highlighting
+  def path
+    file_name
+  end
+
   def name
     file_name
   end
@@ -135,7 +141,16 @@ def search_code(query)
     end
 
     def accessible_to(user)
-      where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user)
+      return are_public unless user.present?
+      return all if user.admin?
+
+      where(
+        'visibility_level IN (:visibility_levels)
+         OR author_id = :author_id
+         OR project_id IN (:project_ids)',
+         visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL],
+         author_id: user.id,
+         project_ids: user.authorized_projects.select(:id))
     end
   end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index 876ccc69d8d0ad50cada764b52fd2ffcef6437b4..767d6366c79fcda2f98e87e916752cf9bf727eb0 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -25,6 +25,7 @@ class User < ActiveRecord::Base
   attr_encrypted :otp_secret,
     key:       Gitlab::Application.config.secret_key_base,
     mode:      :per_attribute_iv_and_salt,
+    insecure_mode: true,
     algorithm: 'aes-256-cbc'
 
   devise :two_factor_authenticatable,
@@ -57,7 +58,7 @@ class User < ActiveRecord::Base
 
   # Groups
   has_many :members, dependent: :destroy
-  has_many :group_members, dependent: :destroy, source: 'GroupMember'
+  has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember'
   has_many :groups, through: :group_members
   has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group
   has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group
@@ -65,7 +66,7 @@ class User < ActiveRecord::Base
   # Projects
   has_many :groups_projects,          through: :groups, source: :projects
   has_many :personal_projects,        through: :namespace, source: :projects
-  has_many :project_members,          dependent: :destroy, class_name: 'ProjectMember'
+  has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember'
   has_many :projects,                 through: :project_members
   has_many :created_projects,         foreign_key: :creator_id, class_name: 'Project'
   has_many :users_star_projects, dependent: :destroy
@@ -308,7 +309,7 @@ def to_reference(_from_project = nil)
 
   def generate_password
     if self.force_random_password
-      self.password = self.password_confirmation = Devise.friendly_token.first(8)
+      self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min)
     end
   end
 
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 540bf54b9209ceb4c0c4b3855c45e972d81cb181..239bd17a035aaa34344b417b4383c344f2f7ffab 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -159,8 +159,9 @@ def mark_todo(issuable, current_user)
   def create_todos(users, attributes)
     Array(users).map do |user|
       next if pending_todos(user, attributes).exists?
-      Todo.create(attributes.merge(user_id: user.id))
+      todo = Todo.create(attributes.merge(user_id: user.id))
       user.update_todos_count_cache
+      todo
     end
   end
 
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index c883e8f97da5f3446039c79f7a6c55e493b04639..30ab07171642ca704e8aa05c42b291d24a261d6e 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -15,7 +15,7 @@
       = f.label :default_snippet_visibility, class: 'control-label col-sm-2'
       .col-sm-10
         = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
-    .form-group.group-visibility-level-holder
+    .form-group.project-visibility-level-holder
       = f.label :default_group_visibility, class: 'control-label col-sm-2'
       .col-sm-10
         = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
diff --git a/app/views/admin/background_jobs/_head.html.haml b/app/views/admin/background_jobs/_head.html.haml
index d78682532ed29c74f89d10387d9cc8cd39a4753a..9d722bd7382ae560b12a9bb306420b7b35b04e3a 100644
--- a/app/views/admin/background_jobs/_head.html.haml
+++ b/app/views/admin/background_jobs/_head.html.haml
@@ -1,5 +1,9 @@
 .nav-links.sub-nav
   %ul{ class: (container_class) }
+    = nav_link(controller: :system_info) do
+      = link_to admin_system_info_path, title: 'System Info' do
+        %span
+          System Info
     = nav_link(controller: :background_jobs) do
       = link_to admin_background_jobs_path, title: 'Background Jobs' do
         %span
diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..3ef2f20b589cb87f3be0d7b2f35188a2ebc5373d
--- /dev/null
+++ b/app/views/admin/system_info/show.html.haml
@@ -0,0 +1,22 @@
+- @no_container = true
+- page_title "System Info"
+= render 'admin/background_jobs/head'
+
+%div{ class: (container_class) }
+  .prepend-top-default
+  .row
+    .col-sm-4
+      .light-well
+        %h4 CPU
+        .data
+          %h1= "#{@cpus} cores"
+    .col-sm-4
+      .light-well
+        %h4 Memory
+        .data
+          %h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}"
+    .col-sm-4
+      .light-well
+        %h4 Disk
+        .data
+          %h1= "#{number_to_human_size(@disk_used)} / #{number_to_human_size(@disk_total)}"
diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml
index b0a709a568a62ac73c42b56e888becc704922264..8f6d13b881a266931a40b8bf3432c085b84d4aaa 100644
--- a/app/views/admin/users/groups.html.haml
+++ b/app/views/admin/users/groups.html.haml
@@ -1,11 +1,12 @@
 - page_title "Groups", @user.name, "Users"
 = render 'admin/users/head'
 
-- if @user.group_members.present?
+- group_members = @user.group_members.includes(:source)
+- if group_members.any?
   .panel.panel-default
     .panel-heading Groups:
     %ul.well-list
-      - @user.group_members.each do |group_member|
+      - group_members.each do |group_member|
         - group = group_member.group
         %li.group_member
           %span{class: ("list-item-name" unless group_member.owner?)}
diff --git a/app/views/ci/errors/show.haml b/app/views/ci/errors/show.haml
deleted file mode 100644
index 2788112c83510151e32eee5afe6072aafaaa963b..0000000000000000000000000000000000000000
--- a/app/views/ci/errors/show.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%h3.error Error
-= @error
diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml
deleted file mode 100644
index 09e7e6535217c0d3c4b1fa9837237e9875f2aee1..0000000000000000000000000000000000000000
--- a/app/views/ci/shared/_guide.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-.bs-callout.help-callout
-  %h4 How to setup CI for this project
-
-  %ol
-    %li
-      Add at least one runner to the project.
-      Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions.
-    %li
-      Put the .gitlab-ci.yml in the root of your repository. Examples can be found in
-      #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}.
-      You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
-    %li
-      Return to this page and refresh it, it should show a new build.
diff --git a/app/views/ci/shared/_no_runners.html.haml b/app/views/ci/shared/_no_runners.html.haml
deleted file mode 100644
index f56c37d9b370cfa7a904f61b2d8afd865f6c3200..0000000000000000000000000000000000000000
--- a/app/views/ci/shared/_no_runners.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.alert.alert-danger
-  %p
-    Now you need Runners to process your builds.
-  %span
-    Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it
-
-
diff --git a/app/views/emojis/index.html.haml b/app/views/emojis/index.html.haml
index 97401a2e618eca912784df91c5baaaf28ae8a784..8b38b4c2bd4a9b354774f3333779167983ead07b 100644
--- a/app/views/emojis/index.html.haml
+++ b/app/views/emojis/index.html.haml
@@ -1,6 +1,6 @@
 .emoji-menu
+  = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis"
   .emoji-menu-content
-    = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control"
     - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis|
       %h5.emoji-menu-title
         = Gitlab::AwardEmoji::CATEGORIES[category]
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index dc4ff17e31abfc4bfd704e28327f8945cc6496a4..ea54ef226ec046deaefbdccfdb355a2c6d34fdc7 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -1,3 +1,5 @@
+- project = event.project
+
 .event-title
   %span.author_name= link_to_author event
   %span.event_label.pushed #{event.action_name} #{event.ref_type}
@@ -5,19 +7,18 @@
     %strong= event.ref_name
   - else
     %strong
-      = link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), title: h(event.target_title)
+      = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title)
   at
-  = link_to_project event.project
+  = link_to_project project
 
 - if event.push_with_commits?
-  - project = event.project
   .event-body
     %ul.well-list.event_commits
       - few_commits = event.commits[0...2]
       - few_commits.each do |commit|
         = render "events/commit", commit: commit, project: project, event: event
 
-      - create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project)
+      - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project)
       - if event.commits_count > 1
         %li.commits-stat
           - if event.commits_count > 2
@@ -27,18 +28,26 @@
             - from = event.commit_from
             - from_label = truncate_sha(from)
           - else
-            - from = event.project.default_branch
+            - from = project.default_branch
             - from_label = from
 
-          = link_to namespace_project_compare_path(event.project.namespace, event.project, from: from, to: event.commit_to) do
+          = link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do
             Compare #{from_label}...#{truncate_sha(event.commit_to)}
 
           - if create_mr
             %span{"data-user-is" => event.author_id, "data-display" => "inline"}
               or
-              = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do
+              = 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}
-          = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do
+          = link_to create_mr_path(project.default_branch, event.ref_name, project) do
             Create Merge Request
+- elsif event.rm_ref?
+  - repository = project.repository
+  - last_commit = repository.commit(event.commit_from)
+  - if last_commit
+    .event-body
+      %ul.well-list.event_commits
+        = render "events/commit", commit: last_commit, project: project, event: event
+
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index e0ed657919eb2cca180e3b80e52e97ffc82e276a..757de92d6d409d9bccfbbd3b27279e94f8a60611 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -30,8 +30,8 @@
 
   = javascript_include_tag "application"
 
-  - if page_specific_javascripts
-    = javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true}
+  - if content_for?(:page_specific_javascripts)
+    = yield :page_specific_javascripts
 
   = csrf_meta_tags
 
diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml
deleted file mode 100644
index 24c68a6dbf5da21cff58278ec4bed6758af4a1e6..0000000000000000000000000000000000000000
--- a/app/views/layouts/ci/_info.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-- if current_user && current_user.is_admin? && Ci::Runner.count.zero?
-  = render 'ci/shared/no_runners'
diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml
deleted file mode 100644
index 2e56d0ac6a33fdfa1cfa8dc24a76920c36c54e82..0000000000000000000000000000000000000000
--- a/app/views/layouts/ci/_page.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-.page-with-sidebar{ class: page_sidebar_class }
-  = render "layouts/broadcast"
-  .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
-
-    - if defined?(sidebar) && sidebar
-      = render "layouts/ci/#{sidebar}"
-    - elsif current_user
-      = render 'layouts/nav/dashboard'
-    .collapse-nav
-      = render partial: 'layouts/collapse_button'
-    - if current_user
-      = link_to current_user, class: 'sidebar-user', title: "Profile" do
-        = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
-        .username
-          = current_user.username
-  .content-wrapper
-    = render "layouts/flash"
-    = render 'layouts/ci/info'
-    %div{ class: container_class }
-      .content
-        .clearfix
-          = yield
diff --git a/app/views/layouts/ci/notify.html.haml b/app/views/layouts/ci/notify.html.haml
deleted file mode 100644
index 270b206df5e9d67626a89ef0a80487ce4f8d57b2..0000000000000000000000000000000000000000
--- a/app/views/layouts/ci/notify.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-%html{lang: "en"}
-  %head
-    %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
-      %title
-        GitLab CI
-
-  %body
-    = yield :header
-
-    %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"}
-      %tr
-        %td{align: "left", style: "margin: 0; padding: 10px;"}
-          = yield
-          %br
-      %tr
-        %td{align: "left", style: "margin: 0; padding: 10px;"}
-          %p{style: "font-size:small;color:#777"}
-            - if @project
-              You're receiving this notification because you are the one who triggered a build on the #{@project.name} project.
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 40a2c81eebdbf074ed203d9834178b37a0d7bbb2..1a39572ac3cf044d17cc9f417bf27d688fc4eeed 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -6,7 +6,7 @@
         = icon('bars')
       %button.navbar-toggle{type: 'button'}
         %span.sr-only Toggle navigation
-        = icon('angle-left')
+        = icon('ellipsis-v')
 
       .navbar-collapse.collapse
         %ul.nav.navbar-nav
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 66e5ec1ad1ab86ada1cb279d09ba8db92c62be0d..5ee8772882ec68a9e31a00c7c06344cc79da6865 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -1,15 +1,16 @@
-%div{ class: nav_control_class }
+.scrolling-tabs-container{ class: nav_control_class }
   = render 'layouts/nav/admin_settings'
-
+  .fade-left
+    = icon('angle-left')
+  .fade-right
+    = icon('angle-right')
   %ul.nav-links.scrolling-tabs
-    %li.fade-left
-      = icon('arrow-left')
     = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
       = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
         %span
           Overview
-    = nav_link(controller: %w(background_jobs logs health_check)) do
-      = link_to admin_background_jobs_path, title: 'Monitoring' do
+    = nav_link(controller: %w(system_info background_jobs logs health_check)) do
+      = link_to admin_system_info_path, title: 'Monitoring' do
         %span
           Monitoring
     = nav_link(controller: :broadcast_messages) do
@@ -37,5 +38,3 @@
         = link_to admin_spam_logs_path, title: "Spam Logs" do
           %span
             Spam Logs
-    %li.fade-right
-      = icon('arrow-right')
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index f7aa9fab7cfc012ca44edab25bff8b7a0ea1afe3..d7d36c84b6caa9dc647e823af93cb427f2f1ea9d 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,9 +1,10 @@
-%div{ class: nav_control_class }
+.scrolling-tabs-container{ class: nav_control_class }
   = render 'layouts/nav/group_settings'
-
+  .fade-left
+    = icon('angle-left')
+  .fade-right
+    = icon('angle-right')
   %ul.nav-links.scrolling-tabs
-    %li.fade-left
-      = icon('arrow-left')
     = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
       = link_to group_path(@group), title: 'Home' do
         %span
@@ -32,5 +33,3 @@
       = link_to group_group_members_path(@group), title: 'Members' do
         %span
           Members
-    %li.fade-right
-      = icon('arrow-right')
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 44ea939b7e4c305726900996f719c0698c222fca..96fe62c39c3f6b755a56bd15dea8f3d55d0cd158 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,48 +1,49 @@
-%ul.nav-links.scrolling-tabs
-  %li.fade-left
-    = icon('arrow-left')
-  = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
-    = link_to profile_path, title: 'Profile Settings' do
-      %span
-        Profile
-  = nav_link(controller: [:accounts, :two_factor_auths]) do
-    = link_to profile_account_path, title: 'Account' do
-      %span
-        Account
-  - if current_application_settings.user_oauth_applications?
-    = nav_link(controller: 'oauth/applications') do
-      = link_to applications_profile_path, title: 'Applications' do
-        %span
-          Applications
-  = nav_link(controller: :personal_access_tokens) do
-    = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
-      %span
-        Personal Access Tokens
-  = nav_link(controller: :emails) do
-    = link_to profile_emails_path, title: 'Emails' do
-      %span
-        Emails
-  - unless current_user.ldap_user?
-    = nav_link(controller: :passwords) do
-      = link_to edit_profile_password_path, title: 'Password' do
-        %span
-          Password
-  = nav_link(controller: :notifications) do
-    = link_to profile_notifications_path, title: 'Notifications' do
-      %span
-        Notifications
+.scrolling-tabs-container
+  .fade-left
+    = icon('angle-left')
+  .fade-right
+    = icon('angle-right')
+  %ul.nav-links.scrolling-tabs
+    = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
+      = link_to profile_path, title: 'Profile Settings' do
+        %span
+          Profile
+    = nav_link(controller: [:accounts, :two_factor_auths]) do
+      = link_to profile_account_path, title: 'Account' do
+        %span
+          Account
+    - if current_application_settings.user_oauth_applications?
+      = nav_link(controller: 'oauth/applications') do
+        = link_to applications_profile_path, title: 'Applications' do
+          %span
+            Applications
+    = nav_link(controller: :personal_access_tokens) do
+      = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
+        %span
+          Personal Access Tokens
+    = nav_link(controller: :emails) do
+      = link_to profile_emails_path, title: 'Emails' do
+        %span
+          Emails
+    - unless current_user.ldap_user?
+      = nav_link(controller: :passwords) do
+        = link_to edit_profile_password_path, title: 'Password' do
+          %span
+            Password
+    = nav_link(controller: :notifications) do
+      = link_to profile_notifications_path, title: 'Notifications' do
+        %span
+          Notifications
 
-  = nav_link(controller: :keys) do
-    = link_to profile_keys_path, title: 'SSH Keys' do
-      %span
-        SSH Keys
-  = nav_link(controller: :preferences) do
-    = link_to profile_preferences_path, title: 'Preferences' do
-      %span
-        Preferences
-  = nav_link(path: 'profiles#audit_log') do
-    = link_to audit_log_profile_path, title: 'Audit Log' do
-      %span
-        Audit Log
-  %li.fade-right
-    = icon('arrow-right')
+    = nav_link(controller: :keys) do
+      = link_to profile_keys_path, title: 'SSH Keys' do
+        %span
+          SSH Keys
+    = nav_link(controller: :preferences) do
+      = link_to profile_preferences_path, title: 'Preferences' do
+        %span
+          Preferences
+    = nav_link(path: 'profiles#audit_log') do
+      = link_to audit_log_profile_path, title: 'Audit Log' do
+        %span
+          Audit Log
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 27e840df5036417e7104b77ef8acc040cae750d6..dcef427cda30fca32f8199f6b783ebedbffd8698 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -24,10 +24,12 @@
                 data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
                 Leave Project
 
-%div{ class: nav_control_class }
+.scrolling-tabs-container{ class: nav_control_class }
+  .fade-left
+    = icon('angle-left')
+  .fade-right
+    = icon('angle-right')
   %ul.nav-links.scrolling-tabs
-    %li.fade-left
-      = icon('arrow-left')
     = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
       = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
         %span
@@ -111,5 +113,3 @@
       %li.hidden
         = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
           Commits
-    %li.fade-right
-      = icon('arrow-right')
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index ca6714ef42b18c69f26f2df3d82f2ed0478b8f01..58d961d93ca0b79af8b4136b73a2820ca35e41a3 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -12,13 +12,13 @@
         %li.confidential-issue-warning
           = icon('warning')
           %span This is a confidential issue. Your comment will not be visible to the public.
-          
+
       %li.pull-right
         .toolbar-group
           = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
           = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" })
           = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
-          = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`" }, title: "Insert code" })
+          = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
           = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
           = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
           = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml
index da522b53417fc4dc418910c9a5c167df8f980117..771a2e0df7d7767fa5522ad2386ea24277221f3b 100644
--- a/app/views/projects/_merge_request_settings.html.haml
+++ b/app/views/projects/_merge_request_settings.html.haml
@@ -8,4 +8,4 @@
         %strong Only allow merge requests to be merged if the build succeeds
       .help-block
         Builds need to be configured to enable this feature.
-        = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
+        = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml
index b1769759dce7a62e1c4368ebe50d2b782a44af8e..58524418a6786d3d5619128a8375e2955b780599 100644
--- a/app/views/projects/blob/_text.html.haml
+++ b/app/views/projects/blob/_text.html.haml
@@ -16,4 +16,4 @@
       .file-content.code
         .nothing-here-block Empty file
     - else
-      = render 'shared/file_highlight', blob: blob
+      = render 'shared/file_highlight', blob: blob, repository: @repository
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 54dab4bff07c5bc0eb8df386c1041c642a42b128..61152649907ddb7630ecd4ea9aaff97e9cb1a165 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,8 +1,10 @@
-.scrolling-tabs-container
+.scrolling-tabs-container.sub-nav-scroll
+  .fade-left
+    = icon('angle-left')
+  .fade-right
+    = icon('angle-right')
   .nav-links.sub-nav.scrolling-tabs
     %ul{ class: (container_class) }
-      %li.fade-left
-        = icon('arrow-left')
       = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
         = link_to project_files_path(@project) do
           Files
@@ -26,5 +28,3 @@
       = nav_link(controller: [:tags, :releases]) do
         = link_to namespace_project_tags_path(@project.namespace, @project) do
           Tags
-      %li.fade-right
-        = icon('arrow-right')
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index a388d9a0a61fc9df3b0a3dec0a83c40f31dcc018..ca347406dfe383fce067f543f889dde8aebbcce5 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,7 +1,9 @@
 .nav-links.sub-nav
   %ul{ class: (container_class) }
 
-    - page_specific_javascripts asset_path("graphs/application.js")
+    - content_for :page_specific_javascripts do
+      = page_specific_javascript_tag('lib/chart.js')
+      = page_specific_javascript_tag('graphs/application.js')
     = nav_link(action: :show) do
       = link_to 'Contributors', namespace_project_graph_path
     = nav_link(action: :commits) do
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 593af319a4797f60209eafc429249e42a568b9fb..3ca30b4ba6b5cff8aab9bdd463ce409434d9921d 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,5 +1,7 @@
 - page_title "Network", @ref
-- page_specific_javascripts asset_path("network/application.js")
+- content_for :page_specific_javascripts do
+  = page_specific_javascript_tag('lib/raphael.js')
+  = page_specific_javascript_tag('network/application.js')
 = render "projects/commits/head"
 = render "head"
 %div{ class: (container_class) }
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 3c1c6060504256b9a5f3923ba84eb8f772a256b1..8a73b0773574ae66ade3ef51b36a59c0c2a5a486 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,110 +1,121 @@
 - page_title    'New Project'
 - header_title  "Projects", dashboard_projects_path
 
-%h3.page-title
-  New Project
-%hr
-
 .project-edit-container
   .project-edit-errors
     = render 'projects/errors'
-  .project-edit-content
-
-    = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f|
-      .form-group
-        = f.label :path, class: 'control-label' do
-          Project owner
-        .col-sm-10
-          = f.select :namespace_id, namespaces_options(:current_user), {}, {class: 'select2 js-select-namespace', tabindex: 1}
-        
-          - if current_user.can_create_group?
-            .help-block
-              Want to house several dependent projects under the same namespace?
-              = link_to "Create a group", new_group_path
-              
-      .form-group
-        = f.label :path, class: 'control-label' do
-          Project name
-        .col-sm-10
-          = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
-
-      - if import_sources_enabled?
-        .project-import.js-toggle-container
-          .form-group
-            %label.control-label Import project from
-            .col-sm-10
-              - if github_import_enabled?
-                - if github_import_configured?
-                  = link_to status_import_github_path, class: 'btn import_github' do
-                    %i.fa.fa-github
-                    GitHub
-                - else
-                  = link_to '#', class: 'how_to_import_link btn import_github' do
-                    %i.fa.fa-github
-                    GitHub
-                  = render 'github_import_modal'
-
-              - if bitbucket_import_enabled?
-                - if bitbucket_import_configured?
-                  = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do
-                    %i.fa.fa-bitbucket
-                    Bitbucket
-                - else
-                  = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do
-                    %i.fa.fa-bitbucket
-                    Bitbucket
-                  = render 'bitbucket_import_modal'
-
-              - if gitlab_import_enabled?
-                - if gitlab_import_configured?
-                  = link_to status_import_gitlab_path, class: 'btn import_gitlab' do
-                    %i.fa.fa-heart
-                    GitLab.com
+  .row.prepend-top-default
+    .col-lg-3.profile-settings-sidebar
+      %h4.prepend-top-0
+        New project
+      %p
+        Create or Import your project from popular Git services
+    .col-lg-9
+      = form_for @project, html: { class: 'new_project' } do |f|
+        %fieldset.append-bottom-0
+          .form-group.col-xs-12.col-sm-6
+            = f.label :namespace_id, class: 'label-light' do
+              %span
+                Project path
+            .form-group
+              .input-group
+                - if current_user.can_select_namespace?
+                  .input-group-addon
+                    = root_url
+                  = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
                 - else
-                  = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do
-                    %i.fa.fa-heart
-                    GitLab.com
-                  = render 'gitlab_import_modal'
-
-              - if gitorious_import_enabled?
-                = link_to new_import_gitorious_path, class: 'btn import_gitorious' do
-                  %i.icon-gitorious.icon-gitorious-small
-                  Gitorious.org
-
-              - if google_code_import_enabled?
-                = link_to new_import_google_code_path, class: 'btn import_google_code' do
-                  %i.fa.fa-google
-                  Google Code
-
-              - if fogbugz_import_enabled?
-                = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
-                  %i.fa.fa-bug
-                  Fogbugz
-
-              - if git_import_enabled?
-                = link_to "#", class: 'btn js-toggle-button import_git' do
-                  %i.fa.fa-git
-                  %span Repo by URL
-
-              - if gitlab_project_import_enabled?
-                = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do
-                  %i.fa.fa-gitlab
-                  %span GitLab export
-
-          .js-toggle-content.hide
-            = render "shared/import_form", f: f
-
-      .prepend-botton-10
-
-      .form-group
-        = f.label :description, class: 'control-label' do
-          Description
-          %span.light (optional)
-        .col-sm-10
-          = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3
-      = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
+                  .input-group-addon.static-namespace
+                    #{root_url}#{current_user.username}/
+          .form-group.col-xs-12.col-sm-6.project-path
+            = f.label :namespace_id, class: 'label-light' do
+              %span
+                Project name
+            = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
+        - if current_user.can_create_group?
+          .help-block
+            Want to house several dependent projects under the same namespace?
+            = link_to "Create a group", new_group_path
+
+        - if import_sources_enabled?
+          .project-import.js-toggle-container
+            .form-group.clearfix
+              = f.label :visibility_level, class: 'label-light' do
+                Import project from
+              .col-sm-12.import-buttons
+                %div
+                  - if github_import_enabled?
+                    - if github_import_configured?
+                      = link_to status_import_github_path, class: 'btn import_github' do
+                        %i.fa.fa-github
+                        GitHub
+                    - else
+                      = link_to '#', class: 'how_to_import_link btn import_github' do
+                        %i.fa.fa-github
+                        GitHub
+                      = render 'github_import_modal'
+                %div
+                  - if bitbucket_import_enabled?
+                    - if bitbucket_import_configured?
+                      = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do
+                        %i.fa.fa-bitbucket
+                        Bitbucket
+                    - else
+                      = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do
+                        %i.fa.fa-bitbucket
+                        Bitbucket
+                      = render 'bitbucket_import_modal'
+                %div
+                  - if gitlab_import_enabled?
+                    - if gitlab_import_configured?
+                      = link_to status_import_gitlab_path, class: 'btn import_gitlab' do
+                        %i.fa.fa-heart
+                        GitLab.com
+                    - else
+                      = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do
+                        %i.fa.fa-heart
+                        GitLab.com
+                      = render 'gitlab_import_modal'
+                %div
+                  - if gitorious_import_enabled?
+                    = link_to new_import_gitorious_path, class: 'btn import_gitorious' do
+                      %i.icon-gitorious.icon-gitorious-small
+                      Gitorious.org
+                %div
+                  - if google_code_import_enabled?
+                    = link_to new_import_google_code_path, class: 'btn import_google_code' do
+                      %i.fa.fa-google
+                      Google Code
+                %div
+                  - if fogbugz_import_enabled?
+                    = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
+                      %i.fa.fa-bug
+                      Fogbugz
+                %div
+                  - if git_import_enabled?
+                    = link_to "#", class: 'btn js-toggle-button import_git' do
+                      %i.fa.fa-git
+                      %span Repo by URL
+                %div
+                  - if gitlab_project_import_enabled?
+                    = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do
+                      %i.fa.fa-gitlab
+                      %span GitLab export
+
+            .js-toggle-content.hide
+              = render "shared/import_form", f: f
+
+        .form-group
+          = f.label :description, class: 'label-light' do
+            Project description
+            %span.light (optional)
+          = f.text_area :description, placeholder: 'Description format',  class: "form-control", rows: 3, maxlength: 250
+
+        .form-group.project-visibility-level-holder
+          = f.label :visibility_level, class: 'label-light' do
+            Visibility Level
+            = link_to "(?)", help_page_path("public_access", "public_access")
+          = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
 
-      .form-actions
         = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
         = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
 
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index c04d291412cf9bd4b9d42fc69aaf5bb2ed02129c..a5e163b91e90821d22b1738c1e16265988fdbf99 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -32,7 +32,7 @@
       .note-body{class: note_editable ? 'js-task-list-container' : ''}
         .note-text
           = preserve do
-            = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author)
+            = note.note_html
           = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
         - if note_editable
           = render 'projects/notes/edit_form', note: note
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index bf5d09d50c20ab993b43624fe0e039fcf78c7a01..817bf9b3f69d898379d36084da08ceb00186b581 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -15,9 +15,10 @@
         Edit Page
 
     .nav-controls
-      - if can?(current_user, :create_wiki, @project)
-        = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
-          New Page
+      - if !(@page && @page.persisted?)
+        - if can?(current_user, :create_wiki, @project)
+          = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+            New Page
       = render 'main_links'
 
 
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index 0fe8a3b490a8293d6b8ea0afbf08031ea87e7db7..290743feb4a57c4fb80a343e67188fad2a1b9d27 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -2,9 +2,10 @@
 .blob-result
   .file-holder
     .file-title
-      = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
+      - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename))
+      = link_to blob_link do
         %i.fa.fa-file
         %strong
           = blob.filename
     .file-content.code.term
-      = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
+      = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index aa18e6f236f0e9580564f6121c43e61eaa0d1cdb..8824bcc158e6455ffe80506181aebcfe558b094f 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,9 +1,5 @@
 %ul.nav-links.event-filter.scrolling-tabs
-  %li.fade-left
-    = icon('arrow-left')
   = event_filter_link EventFilter.push, 'Push events'
   = event_filter_link EventFilter.merged, 'Merge events'
   = event_filter_link EventFilter.comments, 'Comments'
   = event_filter_link EventFilter.team, 'Team'
-  %li.fade-right
-    = icon('arrow-right')
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 37dcf39c062d0621d2444e324e59b19407bb6000..e26693bf5b9613a0419b520b10e983371e6d1e9f 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,13 +1,16 @@
+- repository = nil unless local_assigns.key?(:repository)
+
 .file-content.code.js-syntax-highlight
   .line-numbers
     - if blob.data.present?
       - link_icon = icon('link')
+      - link = blob_link if defined?(blob_link)
       - blob.data.each_line.each_with_index do |_, index|
         - offset = defined?(first_line_number) ? first_line_number : 1
         - i = index + offset
         -# We're not using `link_to` because it is too slow once we get to thousands of lines.
-        %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
+        %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i}
           = link_icon
           = i
   .blob-content{data: {blob_id: blob.id}}
-    = highlight(blob.name, blob.data, plain: blob.no_highlighting?)
+    = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?)
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 47b66d44e43fcb953fce413d48b45d51bad1a8d9..3c03c220ddda28bad006fa7429b63fbb418ef6e8 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -21,7 +21,8 @@
       = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do
         - render_colored_label(label)
 
-    - if assignee
-      = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
-                class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
-        - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
+    %span{ class: "assignee-icon" }
+      - if assignee
+        = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
+                  class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
+          - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 92305594a8167c1381dadc1ef100d2e7537d7817..68665858c3efb5255506bbd96a880c318629d782 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,6 +1,8 @@
 - page_title       @user.name
 - page_description @user.bio
-- page_specific_javascripts asset_path("users/application.js")
+- content_for :page_specific_javascripts do
+  = page_specific_javascript_tag('lib/d3.js')
+  = page_specific_javascript_tag('users/application.js')
 - header_title     @user.name, user_path(@user)
 - @no_container = true
 
diff --git a/config/application.rb b/config/application.rb
index 05fec995ed38e43ee739ed773b068bd5efaed13f..2b0595ede2b2d487ba2bb84191c21d2356b40878 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -84,6 +84,8 @@ class Application < Rails::Application
     config.assets.precompile << "graphs/application.js"
     config.assets.precompile << "users/application.js"
     config.assets.precompile << "network/application.js"
+    config.assets.precompile << "lib/utils/*.js"
+    config.assets.precompile << "lib/*.js"
 
     # Version of your assets, change this if you want to expire all your assets
     config.assets.version = '1.0'
diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb
deleted file mode 100644
index 1516476815a56e96907396b699a59de3b2f931c4..0000000000000000000000000000000000000000
--- a/config/initializers/haml.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-Haml::Template.options[:ugly] = true
-
-# Remove the `:coffee` and `:coffeescript` filters
-#
-# See https://git.io/vztMu and http://stackoverflow.com/a/17571242/223897
-Haml::Filters.remove_filter('coffee')
-Haml::Filters.remove_filter('coffeescript')
diff --git a/config/initializers/hamlit.rb b/config/initializers/hamlit.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7b545d8c06c6eea5e53597d11b9867c013df2c8d
--- /dev/null
+++ b/config/initializers/hamlit.rb
@@ -0,0 +1,18 @@
+module Hamlit
+  class TemplateHandler
+    def call(template)
+      Engine.new(
+        generator: Temple::Generators::RailsOutputBuffer,
+        attr_quote: '"',
+      ).call(template.source)
+    end
+  end
+end
+
+ActionView::Template.register_template_handler(
+  :haml,
+  Hamlit::TemplateHandler.new,
+)
+
+Hamlit::Filters.remove_filter('coffee')
+Hamlit::Filters.remove_filter('coffeescript')
diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb
index 79e2d23ab2eb9fb3071019734e8a8c1604ce6620..6796407d4e64b5427112c36e980674c69c09366f 100644
--- a/config/initializers/health_check.rb
+++ b/config/initializers/health_check.rb
@@ -1,3 +1,17 @@
+# Email forcibly included in the standard checks, but the email health check
+# doesn't support the full range of SMTP options, which can result in failures
+# for valid SMTP configurations.
+# Overwrite the HealthCheck's detection of whether email is configured
+# in order to avoid the email check during standard checks
+module HealthCheck
+  class Utils
+    def self.mailer_configured?
+      false
+    end
+  end
+end
+
 HealthCheck.setup do |config|
   config.standard_checks = ['database', 'migrations', 'cache']
+  config.full_checks = ['database', 'migrations', 'cache']
 end
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index d159f4eded2dde2d6c8814e9030ab23784d33be9..75f89d524e71543b73c24883dec1b4ed9be14e74 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -113,6 +113,10 @@
     config.instrument_methods(Banzai::Renderer)
     config.instrument_methods(Banzai::Querying)
 
+    config.instrument_instance_methods(Banzai::ObjectRenderer)
+    config.instrument_instance_methods(Banzai::Redactor)
+    config.instrument_methods(Banzai::NoteRenderer)
+
     [Issuable, Mentionable, Participable].each do |klass|
       config.instrument_instance_methods(klass)
       config.instrument_instance_methods(klass::ClassMethods)
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index 2287a76fca77827c02387f50388648e310c4d6c4..bd37080b1c895957bb84fb2f1466dbfa19c0bbf5 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -10,6 +10,7 @@
 if Rails.env.production?
   Rails.application.config.action_mailer.delivery_method = :smtp
 
+  ActionMailer::Base.delivery_method = :smtp
   ActionMailer::Base.smtp_settings = {
     address: "email.server.com",
     port: 465,
diff --git a/config/routes.rb b/config/routes.rb
index e45293cdf7fd8db9dc15790c9c909579300c47ec..bdfb16a66bf3188e0f65580992ac227b6e35216d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -280,6 +280,7 @@
     resource :logs, only: [:show]
     resource :health_check, controller: 'health_check', only: [:show]
     resource :background_jobs, controller: 'background_jobs', only: [:show]
+    resource :system_info, controller: 'system_info', only: [:show]
 
     resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
       root to: 'projects#index', as: :projects
diff --git a/doc/README.md b/doc/README.md
index f1283cea0ad16c114dd5b6065be6a9b850dd9221..be0d17084c7f6dcaac3cb6f3c6fb96356a848c30 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -44,6 +44,7 @@
 - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
 - [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
 - [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint.
+- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong
 - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
 - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability.
 - [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
new file mode 100644
index 0000000000000000000000000000000000000000..e5701b86cf3e4dc9325f5f4184e174fa4214f26f
--- /dev/null
+++ b/doc/administration/troubleshooting/debug.md
@@ -0,0 +1,120 @@
+# Debugging Tips
+
+Sometimes things don't work the way they should. Here are some tips on debugging issues out
+in production.
+
+## The GNU Project Debugger (gdb)
+
+`gdb` is a must-have tool for debugging issues. To install on Ubuntu/Debian:
+
+```
+sudo apt-get install gdb
+```
+
+On CentOS:
+
+```
+sudo yum install gdb
+```
+
+## Common Problems
+
+Many of the tips to diagnose issues below apply to many different situations. We'll use one
+concrete example to illustrate what you can do to learn what is going wrong.
+
+### 502 Gateway Timeout after unicorn spins at 100% CPU
+
+This error occurs when the Web server times out (default: 60 s) after not
+hearing back from the unicorn worker. If the CPU spins to 100% while this in
+progress, there may be something taking longer than it should.
+
+To fix this issue, we first need to figure out what is happening. The
+following tips are only recommended if you do NOT mind users being affected by
+downtime. Otherwise skip to the next section.
+
+1. Load the problematic URL
+1. Run `sudo gdb -p <PID>` to attach to the unicorn process.
+1. In the gdb window, type:
+
+    ```
+    call (void) rb_backtrace()
+    ```
+
+1. This forces the process to generate a Ruby backtrace. Check
+   `/var/log/gitlab/unicorn/unicorn_stderr.log` for the backtace. For example, you may see:
+
+    ```ruby
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects'
+    from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name'
+    ```
+
+1. To see the current threads, run:
+
+    ```
+    apply all thread bt
+    ```
+
+1. Once you're done debugging with `gdb`, be sure to detach from the process and exit:
+
+    ```
+    detach
+    exit
+    ```
+
+Note that if the unicorn process terminates before you are able to run these
+commands, gdb will report an error. To buy more time, you can always raise the
+Unicorn timeout. For omnibus users, you can edit `/etc/gitlab/gitlab.rb` and
+increase it from 60 seconds to 300:
+
+```ruby
+unicorn['worker_timeout'] = 300
+```
+
+For source installations, edit `config/unicorn.rb`.
+
+[Reconfigure] GitLab for the changes to take effect.
+
+[Reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
+
+#### Troubleshooting without affecting other users
+
+The previous section attached to a running unicorn process, and this may have
+undesirable effects for users trying to access GitLab during this time. If you
+are concerned about affecting others during a production system, you can run a
+separate Rails process to debug the issue:
+
+1. Log in to your GitLab account.
+1. Copy the URL that is causing problems (e.g. https://gitlab.com/ABC).
+1. Obtain the private token for your user (Profile Settings -> Account).
+1. Bring up the GitLab Rails console. For omnibus users, run:
+
+    ````
+    sudo gitlab-rails console
+    ```
+
+1. At the Rails console, run:
+
+    ```ruby
+    [1] pry(main)> app.get '<URL FROM STEP 1>/private_token?<TOKEN FROM STEP 2>'
+    ```
+
+    For example:
+
+    ```ruby
+    [1] pry(main)> app.get 'https://gitlab.com/gitlab-org/gitlab-ce/issues/1?private_token=123456'
+    ```
+
+1. In a new window, run `top`. It should show this ruby process using 100% CPU. Write down the PID.
+1. Follow step 2 from the previous section on using gdb.
+
+# More information
+
+* [Debugging Stuck Ruby Processes](https://blog.newrelic.com/2013/04/29/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/)
+* [Cheatsheet of using gdb and ruby processes](gdb-stuck-ruby.txt)
diff --git a/doc/administration/troubleshooting/gdb-stuck-ruby.txt b/doc/administration/troubleshooting/gdb-stuck-ruby.txt
new file mode 100644
index 0000000000000000000000000000000000000000..13d5dfcffa4ca3002826f87ddec432ac2422edcd
--- /dev/null
+++ b/doc/administration/troubleshooting/gdb-stuck-ruby.txt
@@ -0,0 +1,142 @@
+# Here's the script I'll use to demonstrate - it just loops forever:
+
+$ cat test.rb 
+#!/usr/bin/env ruby
+
+loop do
+  sleep 1
+end
+
+# Now, I'll start the script in the background, and redirect stdout and stderr
+# to /dev/null:
+
+$ ruby ./test.rb >/dev/null 2>/dev/null &
+[1] 1343
+
+# Next, I'll grab the PID of the script (1343):
+
+$ ps aux | grep test.rb
+vagrant   1343  0.0  0.4   3884  1652 pts/0    S    14:42   0:00 ruby ./test.rb
+vagrant   1345  0.0  0.2   4624   852 pts/0    S+   14:42   0:00 grep --color=auto test.rb
+
+# Now I start gdb. Note that I'm using sudo here. This may or may not be
+# necessary in your setup. I'd try without sudo first, and fall back to adding
+# it if the next step fails:
+
+$ sudo gdb
+GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
+Copyright (C) 2012 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
+and "show warranty" for details.
+This GDB was configured as "i686-linux-gnu".
+For bug reporting instructions, please see:
+<http://bugs.launchpad.net/gdb-linaro/>.
+
+# OK, now I'm in gdb, and I want to instruct it to attach to our Ruby process.
+# I can do that using the 'attach' command, which takes a PID (the one we
+# gathered above):
+
+(gdb) attach 1343
+Attaching to process 1343
+Reading symbols from /opt/vagrant_ruby/bin/ruby...done.
+Reading symbols from /lib/i386-linux-gnu/librt.so.1...(no debugging symbols found)...done.
+Loaded symbols for /lib/i386-linux-gnu/librt.so.1
+Reading symbols from /lib/i386-linux-gnu/libdl.so.2...(no debugging symbols found)...done.
+Loaded symbols for /lib/i386-linux-gnu/libdl.so.2
+Reading symbols from /lib/i386-linux-gnu/libcrypt.so.1...(no debugging symbols found)...done.
+Loaded symbols for /lib/i386-linux-gnu/libcrypt.so.1
+Reading symbols from /lib/i386-linux-gnu/libm.so.6...(no debugging symbols found)...done.
+Loaded symbols for /lib/i386-linux-gnu/libm.so.6
+Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
+Loaded symbols for /lib/i386-linux-gnu/libc.so.6
+Reading symbols from /lib/i386-linux-gnu/libpthread.so.0...(no debugging symbols found)...done.
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
+Loaded symbols for /lib/i386-linux-gnu/libpthread.so.0
+Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
+Loaded symbols for /lib/ld-linux.so.2
+0xb770c424 in __kernel_vsyscall ()
+
+# Great, now gdb is attached to the target process. If the step above fails, try
+# going back and running gdb under sudo. The next thing I want to do is gather
+# C-level backtraces from all threads in the process. The following command
+# stands for 'thread apply all backtrace':
+
+(gdb) t a a bt
+
+Thread 1 (Thread 0xb74d76c0 (LWP 1343)):
+#0  0xb770c424 in __kernel_vsyscall ()
+#1  0xb75d7abd in select () from /lib/i386-linux-gnu/libc.so.6
+#2  0x08069c56 in rb_thread_wait_for (time=...) at eval.c:11376
+#3  0x080a20fd in rb_f_sleep (argc=1, argv=0xbf85f490) at process.c:1633
+#4  0x0805e0e2 in call_cfunc (argv=0xbf85f490, argc=1, len=-1, recv=3075299660, func=0x80a20b0 <rb_f_sleep>)
+    at eval.c:5778
+#5  rb_call0 (klass=3075304600, recv=3075299660, id=9393, oid=9393, argc=1, argv=0xbf85f490, body=0xb74c85a8, flags=2)
+    at eval.c:5928
+#6  0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=9393, argc=1, argv=0xbf85f490, scope=1, 
+    self=<optimized out>) at eval.c:6176
+#7  0x080651ec in rb_eval (self=3075299660, n=0xb74c4e1c) at eval.c:3521
+#8  0x0805c31c in rb_yield_0 (val=6, self=3075299660, klass=<optimized out>, flags=0, avalue=0) at eval.c:5095
+#9  0x0806a1e5 in loop_i () at eval.c:5227
+#10 0x08058dbd in rb_rescue2 (b_proc=0x806a1c0 <loop_i>, data1=0, r_proc=0, data2=0) at eval.c:5491
+#11 0x08058f28 in rb_f_loop () at eval.c:5252
+#12 0x0805e0c1 in call_cfunc (argv=0x0, argc=0, len=0, recv=3075299660, func=0x8058ef0 <rb_f_loop>) at eval.c:5781
+#13 rb_call0 (klass=3075304600, recv=3075299660, id=4121, oid=4121, argc=0, argv=0x0, body=0xb74d4dbc, flags=2)
+    at eval.c:5928
+#14 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=4121, argc=0, argv=0x0, scope=1, self=<optimized out>)
+    at eval.c:6176
+#15 0x080651ec in rb_eval (self=3075299660, n=0xb74c4dcc) at eval.c:3521
+#16 0x080662c6 in rb_eval (self=3075299660, n=0xb74c4de0) at eval.c:3236
+#17 0x08068ee4 in ruby_exec_internal () at eval.c:1654
+#18 0x08068f24 in ruby_exec () at eval.c:1674
+#19 0x0806b2cd in ruby_run () at eval.c:1684
+#20 0x08053771 in main (argc=2, argv=0xbf860204, envp=0xbf860210) at main.c:48
+
+# C backtraces are sometimes sufficient, but often Ruby backtraces are necessary
+# for debugging as well. Ruby has a built-in function called rb_backtrace() that
+# we can use to dump out a Ruby backtrace, but it prints to stdout or stderr
+# (depending on your Ruby version), which might have been redirected to a file
+# or to /dev/null (as in our example) when the process started up.
+#
+# To get aroundt this, we'll do a little trick and redirect the target process's
+# stdout and stderr to the current TTY, so that any output from the process
+# will appear directly on our screen.
+
+# First, let's close the existing file descriptors for stdout and stderr
+# (FD 1 and 2, respectively):
+(gdb) call (void) close(1)
+(gdb) call (void) close(2)
+
+# Next, we need to figure out the device name for the current TTY:
+(gdb) shell tty
+/dev/pts/0
+
+# OK, now we can pass the device name obtained above to open() and attach
+# file descriptors 1 and 2 back to the current TTY with these calls:
+
+(gdb) call (int) open("/dev/pts/0", 2, 0)
+$1 = 1
+(gdb) call (int) open("/dev/pts/0", 2, 0)
+$2 = 2
+
+# Finally, we call rb_backtrace() in order to dump the Ruby backtrace:
+
+(gdb) call (void) rb_backtrace()
+  from ./test.rb:4:in `sleep'
+  from ./test.rb:4
+  from ./test.rb:3:in `loop'
+  from ./test.rb:3
+
+# And here's how we get out of gdb. Once you've quit, you'll probably want to
+# clean up the stuck process by killing it.
+
+(gdb) quit
+A debugging session is active.
+
+  Inferior 1 [process 1343] will be detached.
+
+Quit anyway? (y or n) y
+Detaching from program: /opt/vagrant_ruby/bin/ruby, process 1343
+$ 
diff --git a/doc/api/builds.md b/doc/api/builds.md
index de9989443528fc425a86b581739d0695637a371e..2adea11247e7dedeceb8ad1747a1b918d9aebc2f 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -107,6 +107,11 @@ Example of response
 
 Get a list of builds for specific commit in a project.
 
+This endpoint will return all builds, from all pipelines for a given commit.
+If the commit SHA is not found, it will respond with 404, otherwise it will
+return an array of builds (an empty array if there are no builds for this
+particular commit).
+
 ```
 GET /projects/:id/repository/commits/:sha/builds
 ```
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 0bc82ef9edb9afaacfe354bbc1f5509929b4ff3c..708fc691f679673b703de2598e68380c45bcb9f0 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -28,7 +28,7 @@ GET /issues?labels=foo,bar&state=opened
 | Attribute | Type | Required | Description |
 | --------- | ---- | -------- | ----------- |
 | `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
-| `labels`  | string  | no    | Comma-separated list of label names |
+| `labels`  | string  | no    | Comma-separated list of label names, issues with any of the labels will be returned |
 | `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
 | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
 
@@ -83,6 +83,82 @@ Example response:
 ]
 ```
 
+## List group issues
+
+Get a list of a group's issues.
+
+```
+GET /groups/:id/issues
+GET /groups/:id/issues?state=opened
+GET /groups/:id/issues?state=closed
+GET /groups/:id/issues?labels=foo
+GET /groups/:id/issues?labels=foo,bar
+GET /groups/:id/issues?labels=foo,bar&state=opened
+GET /groups/:id/issues?milestone=1.0.0
+GET /groups/:id/issues?milestone=1.0.0&state=opened
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id`      | integer | yes   | The ID of a group |
+| `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
+| `labels`  | string  | no    | Comma-separated list of label names, issues must have all labels to be returned |
+| `milestone` | string| no    | The milestone title |
+| `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
+| `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
+
+
+```bash
+curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/4/issues
+```
+
+Example response:
+
+```json
+[
+   {
+      "project_id" : 4,
+      "milestone" : {
+         "due_date" : null,
+         "project_id" : 4,
+         "state" : "closed",
+         "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.",
+         "iid" : 3,
+         "id" : 11,
+         "title" : "v3.0",
+         "created_at" : "2016-01-04T15:31:39.788Z",
+         "updated_at" : "2016-01-04T15:31:39.788Z"
+      },
+      "author" : {
+         "state" : "active",
+         "web_url" : "https://gitlab.example.com/u/root",
+         "avatar_url" : null,
+         "username" : "root",
+         "id" : 1,
+         "name" : "Administrator"
+      },
+      "description" : "Omnis vero earum sunt corporis dolor et placeat.",
+      "state" : "closed",
+      "iid" : 1,
+      "assignee" : {
+         "avatar_url" : null,
+         "web_url" : "https://gitlab.example.com/u/lennie",
+         "state" : "active",
+         "username" : "lennie",
+         "id" : 9,
+         "name" : "Dr. Luella Kovacek"
+      },
+      "labels" : [],
+      "id" : 41,
+      "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.",
+      "updated_at" : "2016-01-04T15:31:46.176Z",
+      "created_at" : "2016-01-04T15:31:46.176Z",
+      "subscribed" : false,
+      "user_notes_count": 1
+   }
+]
+```
+
 ## List project issues
 
 Get a list of a project's issues.
@@ -104,7 +180,7 @@ GET /projects/:id/issues?iid=42
 | `id`      | integer | yes   | The ID of a project |
 | `iid`     | integer | no    | Return the issue having the given `iid` |
 | `state`   | string  | no    | Return all issues or just those that are `opened` or `closed`|
-| `labels`  | string  | no    | Comma-separated list of label names |
+| `labels`  | string  | no    | Comma-separated list of label names, issues with any of the labels will be returned |
 | `milestone` | string| no    | The milestone title |
 | `order_by`| string  | no    | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
 | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index d416a826f797f68d6e5324f8a562d85fa4813326..31902e145f6ef64934cbb7930bf89b6ba422d9e7 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -65,6 +65,13 @@ curl -H "Authorization: Bearer OAUTH-TOKEN" https://localhost:3000/api/v3/user
 
 ## Resource Owner Password Credentials
 
+## Deprecation Notice
+
+1. Starting in GitLab 9.0, the Resource Owner Password Credentials will be *disabled* for users with two-factor authentication turned on.
+2. These users can access the API using [personal access tokens] instead.
+
+---
+
 In this flow, a token is requested in exchange for the resource owner credentials (username and password). 
 The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g. the
 client is part of the device operating system or a highly privileged application), and when other authorization grant types are not
@@ -100,3 +107,5 @@ client = OAuth2::Client.new('the_client_id', 'the_client_secret', :site => "http
 access_token = client.password.get_token('user@example.com', 'sekret')
 puts access_token.token
 ```
+
+[personal access tokens]: ./README.md#personal-access-tokens
diff --git a/doc/api/services.md b/doc/api/services.md
index ccfc0fccb7f20d467afb7dd0966effc03e9ccc18..32d6e2dea785f7df116083316129e4434bf3de0a 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -374,40 +374,6 @@ Get Gemnasium service settings for a project.
 GET /projects/:id/services/gemnasium
 ```
 
-## GitLab CI
-
-Continuous integration server from GitLab
-
-### Create/Edit GitLab CI service
-
-Set GitLab CI service for a project.
-
-```
-PUT /projects/:id/services/gitlab-ci
-```
-
-Parameters:
-
-- `token` (**required**) - GitLab CI project specific token
-- `project_url` (**required**) - http://ci.gitlabhq.com/projects/3
-- `enable_ssl_verification` (optional) - Enable SSL verification
-
-### Delete GitLab CI service
-
-Delete GitLab CI service for a project.
-
-```
-DELETE /projects/:id/services/gitlab-ci
-```
-
-### Get GitLab CI service settings
-
-Get GitLab CI service settings for a project.
-
-```
-GET /projects/:id/services/gitlab-ci
-```
-
 ## HipChat
 
 Private group chat and IM
diff --git a/doc/api/session.md b/doc/api/session.md
index 71e93d0bb0aaf2820ac0aec8ec5dc6d4076969a4..066a055702df222478f0996aeee6105ef81e6932 100644
--- a/doc/api/session.md
+++ b/doc/api/session.md
@@ -1,5 +1,12 @@
 # Session
 
+## Deprecation Notice
+
+1. Starting in GitLab 9.0, this feature will be *disabled* for users with two-factor authentication turned on.
+2. These users can access the API using [personal access tokens] instead.
+
+---
+
 You can login with both GitLab and LDAP credentials in order to obtain the
 private token.
 
@@ -45,3 +52,5 @@ Example response:
   "private_token": "9koXpg98eAheJpvBs5tK"
 }
 ```
+
+[personal access tokens]: ./README.md#personal-access-tokens
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 43a0fe35e42dbc5a9207998557fcf5f4d23659a9..b5152311f2814284e43de14a2153ea950703e8c7 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -56,7 +56,7 @@ PUT /application/settings
 | `gravatar_enabled`  | boolean | no  | Enable Gravatar |
 | `sign_in_text`      | string  | no  | Text on login page |
 | `home_page_url`     | string  | no  | Redirect to this URL when not logged in |
-| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `1`. |
+| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `2`. |
 | `restricted_visibility_levels` | array of integers | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is null which means there is no restriction. |
 | `max_attachment_size` | integer | no | Limit attachment size in MB |
 | `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 1892acda29b0f8afa8fdac984db144fdb6db2679..d2d1b04f893fb4b89348e4cf6ede5ed365be7fc6 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1034,8 +1034,8 @@ You can find the link under `/ci/lint` of your gitlab instance.
 
 ## Skipping builds
 
-If your commit message contains `[ci skip]`, the commit will be created but the
-builds will be skipped.
+If your commit message contains `[ci skip]` or `[skip ci]`, using any
+capitalization, the commit will be created but the builds will be skipped.
 
 ## Examples
 
diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md
index 194b8e002993803151e5577ded5800ff1ae4dd14..4620bb2dcde68372f9ce052c381faa94b63c3a9a 100644
--- a/doc/customization/issue_closing.md
+++ b/doc/customization/issue_closing.md
@@ -8,7 +8,7 @@ the matched text will be closed. This happens when the commit is pushed to a pro
 When not specified, the default `issue_closing_pattern` as shown below will be used:
 
 ```bash
-((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)
+((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)
 ```
 
 Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that matches a reference to a local issue (`#123`), cross-project issue (`group/project#123`) or a link to an issue (`https://gitlab.example.com/group/project/issues/123`).
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 12e33406cb694f32c25c7f1ae1e871c4a685fc48..33fd50f4c11d30423a08900a0668daa0a9584c2c 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -52,7 +52,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
 
 ### Components
 
-![GitLab Diagram Overview](gitlab_diagram_overview.png)
+![GitLab Diagram Overview](gitlab_architecture_diagram.png)
+ 
+_[edit diagram (for GitLab team members only)](https://docs.google.com/drawings/d/1fBzAyklyveF-i-2q-OHUIqDkYfjjxC4mq5shwKSZHLs/edit)_
 
 A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs.
 
diff --git a/doc/development/gitlab_architecture_diagram.png b/doc/development/gitlab_architecture_diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ab7ffd3c7b9dfc9557ca5145fa4f218a420e19a
Binary files /dev/null and b/doc/development/gitlab_architecture_diagram.png differ
diff --git a/doc/development/gitlab_diagram_overview.png b/doc/development/gitlab_diagram_overview.png
deleted file mode 100644
index d9b9eed3d8f6b244dabf5b3074324fcfa774704a..0000000000000000000000000000000000000000
Binary files a/doc/development/gitlab_diagram_overview.png and /dev/null differ
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index 21078c8d6f9aba69873a757e449d5658a1bf327c..9d7fe7440d27505ee8eb0e638d6a3d976ebcdf64 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -46,7 +46,7 @@ Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L9
 Using the inline `:coffee` or `:coffeescript` Haml filters comes with a
 performance overhead.
 
-_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-5-stable/config/initializers/haml.rb)
+_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/hamlit.rb)
 in an initializer._
 
 ### Further reading
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 8a7547e532281c0b6723744f1279f430ac883ea8..e2ca46504e71093dbf7ca7f824b7c6ac19537d8a 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -37,7 +37,6 @@ First, you need to provide information on whether the migration can be applied:
 For example: 
 
 ```
-# rubocop:disable all
 # Migration type: online without errors (works on previous version and new one)
 class MyMigration < ActiveRecord::Migration
 ...
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 6d04b9590e6e33ae1142f3af34f085b22f50133c..41685c7ee416beb866ae679d1fd2eeebfbe54757 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -33,3 +33,23 @@ bundle exec rake gitlab:generate_docs
 ```
 bundle exec rake services:doc
 ```
+
+## Updating Emoji Digests
+
+To update the Emoji digests file (used for Emoji autocomplete) you must run the
+following:
+
+```
+bundle exec rake gemojione:digests
+```
+
+This will update the file `fixtures/emojis/digests.json` based on the currently
+available Emoji.
+
+## Emoji Sprites
+
+Generating a sprite file containing all the Emoji can be done by running:
+
+```
+bundle exec rake gemojione:sprite
+```
diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md
index 3625c4191b8283c47bd02cb58ab4fe78d1f1ebd5..a6d22e5a04a299d02b840148ca7ce52de8ea9558 100644
--- a/doc/downgrade_ee_to_ce/README.md
+++ b/doc/downgrade_ee_to_ce/README.md
@@ -44,13 +44,13 @@ to avoid getting this error, you need to remove all instances of the
 **Omnibus Installation**
 
 ```
-$ sudo gitlab-rails runner "Service.where(type: 'JenkinsService').delete_all"
+$ sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all"
 ```
 
 **Source Installation**
 
 ```
-$ bundle exec rails runner "Service.where(type: 'JenkinsService').delete_all" production
+$ bundle exec rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" production
 ```
 
 ## Downgrade to CE
diff --git a/doc/install/installation.md b/doc/install/installation.md
index d9290b1fa76f620fbf447341e806abc7fc192988..dc8d9c6553513ad0c66d6cd8f10d8c3b34c3b86e 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -391,6 +391,10 @@ GitLab Shell is an SSH access and repository management software developed speci
 
 ### Install gitlab-workhorse
 
+GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
     cd /home/git
     sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
     cd gitlab-workhorse
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 09c6211b3ab80be68af2d28a037ca65bef6d2c8b..a65ac8a5f79adc5c6736de05efa39506f1e7ce9c 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -52,7 +52,7 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim
 
 ### CPU
 
-- 1 core works supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core
+- 1 core supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core
 - **2 cores** is the **recommended** number of cores and supports up to 500 users
 - 4 cores supports up to 2,000 users
 - 8 cores supports up to 5,000 users
diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md
index a2d7e922aadc2b762426f96b373ac978213f09d8..8d2c6351fb8dc608f0d08c18a9a771c0542b3003 100644
--- a/doc/integration/external-issue-tracker.md
+++ b/doc/integration/external-issue-tracker.md
@@ -1,7 +1,7 @@
 # External issue tracker
 
 GitLab has a great issue tracker but you can also use an external one such as
-Jira or Redmine. Issue trackers are configurable per GitLab project and allow
+Jira, Redmine, or Bugzilla. Issue trackers are configurable per GitLab project and allow
 you to do the following:
 
 - the **Issues** link on the GitLab project pages takes you to the appropriate
@@ -20,6 +20,7 @@ Visit the links below for details:
 
 - [Redmine](../project_services/redmine.md)
 - [Jira](../project_services/jira.md)
+- [Bugzilla](../project_services/bugzilla.md)
 
 ### Service Template
 
diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md
index 168bd85c26a697f714161096c61de9bbfd5fca1d..7947b0fedc4eeb19a385c569431523da7f4440d8 100644
--- a/doc/monitoring/performance/grafana_configuration.md
+++ b/doc/monitoring/performance/grafana_configuration.md
@@ -44,70 +44,32 @@ on a separate server)
 
 ## Apply retention policies and create continuous queries
 
-If you intend to import the GitLab provided Grafana dashboards, you will need
-to copy and run a set of queries against InfluxDB to create the needed data
-sets.
+If you intend to import the GitLab provided Grafana dashboards, you will need to
+set up the right retention policies and continuous queries. The easiest way of
+doing this is by using the [influxdb-management](https://gitlab.com/gitlab-org/influxdb-management)
+repository.
 
-On the InfluxDB server, run the following command, substituting your InfluxDB
-user and password:
+To use this repository you must first clone it:
 
-```bash
-influxdb --username admin -password super_secret
+```
+git clone https://gitlab.com/gitlab-org/influxdb-management.git
+cd influxdb-management
 ```
 
-This will drop you in to an InfluxDB interactive session. Copy the entire
-contents below and paste it in to the interactive session:
+Next you must install the required dependencies:
 
 ```
-CREATE RETENTION POLICY default ON gitlab DURATION 1h REPLICATION 1 DEFAULT
-CREATE RETENTION POLICY downsampled ON gitlab DURATION 7d REPLICATION 1
-CREATE CONTINUOUS QUERY grape_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_git_timings_per_action FROM gitlab."default".rails_method_calls WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY grape_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.grape_markdown_render_timings_overall FROM gitlab."default".rails_transactions WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY grape_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.grape_markdown_render_timings_per_action FROM gitlab."default".rails_transactions WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY grape_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_markdown_timings_overall FROM gitlab."default".rails_method_calls WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND method =~ /^Banzai/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY grape_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_method_call_timings_per_action_and_method FROM gitlab."default".rails_method_calls WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), method, action END;
-CREATE CONTINUOUS QUERY grape_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_method_call_timings_per_method FROM gitlab."default".rails_method_calls WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), method END;
-CREATE CONTINUOUS QUERY grape_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.grape_transaction_counts_overall FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY grape_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.grape_transaction_counts_per_action FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY grape_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.grape_transaction_timings_overall FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY grape_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.grape_transaction_timings_per_action FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY rails_file_descriptor_counts ON gitlab BEGIN SELECT sum(value) AS count INTO gitlab.downsampled.rails_file_descriptor_counts FROM gitlab."default".rails_file_descriptors GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_gc_counts ON gitlab BEGIN SELECT sum(count) AS total, sum(minor_gc_count) AS minor, sum(major_gc_count) AS major INTO gitlab.downsampled.rails_gc_counts FROM gitlab."default".rails_gc_statistics GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_gc_timings ON gitlab BEGIN SELECT mean(total_time) AS duration_mean, percentile(total_time, 95) AS duration_95th, percentile(total_time, 99) AS duration_99th INTO gitlab.downsampled.rails_gc_timings FROM gitlab."default".rails_gc_statistics GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_git_timings_per_action FROM gitlab."default".rails_method_calls WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY rails_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.rails_markdown_render_timings_overall FROM gitlab."default".rails_transactions WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.rails_markdown_render_timings_per_action FROM gitlab."default".rails_transactions WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY rails_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_markdown_timings_overall FROM gitlab."default".rails_method_calls WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND method =~ /^Banzai/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_memory_usage_overall ON gitlab BEGIN SELECT mean(value) AS memory_mean, percentile(value, 95) AS memory_95th, percentile(value, 99) AS memory_99th INTO gitlab.downsampled.rails_memory_usage_overall FROM gitlab."default".rails_memory_usage GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_method_call_timings_per_action_and_method FROM gitlab."default".rails_method_calls WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), method, action END;
-CREATE CONTINUOUS QUERY rails_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_method_call_timings_per_method FROM gitlab."default".rails_method_calls WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), method END;
-CREATE CONTINUOUS QUERY rails_object_counts_overall ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.rails_object_counts_overall FROM gitlab."default".rails_object_counts GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_object_counts_per_type ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.rails_object_counts_per_type FROM gitlab."default".rails_object_counts GROUP BY time(1m), type END;
-CREATE CONTINUOUS QUERY rails_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.rails_transaction_counts_overall FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.rails_transaction_counts_per_action FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY rails_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.rails_transaction_timings_overall FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY rails_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.rails_transaction_timings_per_action FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY rails_view_timings_per_action_and_view ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_view_timings_per_action_and_view FROM gitlab."default".rails_views WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action, view END;
-CREATE CONTINUOUS QUERY sidekiq_file_descriptor_counts ON gitlab BEGIN SELECT sum(value) AS count INTO gitlab.downsampled.sidekiq_file_descriptor_counts FROM gitlab."default".sidekiq_file_descriptors GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_gc_counts ON gitlab BEGIN SELECT sum(count) AS total, sum(minor_gc_count) AS minor, sum(major_gc_count) AS major INTO gitlab.downsampled.sidekiq_gc_counts FROM gitlab."default".sidekiq_gc_statistics GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_gc_timings ON gitlab BEGIN SELECT mean(total_time) AS duration_mean, percentile(total_time, 95) AS duration_95th, percentile(total_time, 99) AS duration_99th INTO gitlab.downsampled.sidekiq_gc_timings FROM gitlab."default".sidekiq_gc_statistics GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_git_timings_per_action FROM gitlab."default".sidekiq_method_calls WHERE method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY sidekiq_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.sidekiq_markdown_render_timings_overall FROM gitlab."default".sidekiq_transactions WHERE (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.sidekiq_markdown_render_timings_per_action FROM gitlab."default".sidekiq_transactions WHERE (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY sidekiq_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_markdown_timings_overall FROM gitlab."default".sidekiq_method_calls WHERE method =~ /^Banzai/ GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_memory_usage_overall ON gitlab BEGIN SELECT mean(value) AS memory_mean, percentile(value, 95) AS memory_95th, percentile(value, 99) AS memory_99th INTO gitlab.downsampled.sidekiq_memory_usage_overall FROM gitlab."default".sidekiq_memory_usage GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_method_call_timings_per_action_and_method FROM gitlab."default".sidekiq_method_calls GROUP BY time(1m), method, action END;
-CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_method_call_timings_per_method FROM gitlab."default".sidekiq_method_calls GROUP BY time(1m), method END;
-CREATE CONTINUOUS QUERY sidekiq_object_counts_overall ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.sidekiq_object_counts_overall FROM gitlab."default".sidekiq_object_counts GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_object_counts_per_type ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.sidekiq_object_counts_per_type FROM gitlab."default".sidekiq_object_counts GROUP BY time(1m), type END;
-CREATE CONTINUOUS QUERY sidekiq_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.sidekiq_transaction_counts_overall FROM gitlab."default".sidekiq_transactions GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.sidekiq_transaction_counts_per_action FROM gitlab."default".sidekiq_transactions GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY sidekiq_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.sidekiq_transaction_timings_overall FROM gitlab."default".sidekiq_transactions GROUP BY time(1m) END;
-CREATE CONTINUOUS QUERY sidekiq_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.sidekiq_transaction_timings_per_action FROM gitlab."default".sidekiq_transactions GROUP BY time(1m), action END;
-CREATE CONTINUOUS QUERY sidekiq_view_timings_per_action_and_view ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_view_timings_per_action_and_view FROM gitlab."default".sidekiq_views GROUP BY time(1m), action, view END;
-CREATE CONTINUOUS QUERY web_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.web_transaction_counts_overall FROM gitlab."default".rails_transactions GROUP BY time(1m) END;
+gem install bundler
+bundle install
 ```
 
+Now you must configure the repository by first copying `.env.example` to `.env`
+and then editing the `.env` file to contain the correct InfluxDB settings. Once
+configured you can simply run `bundle exec rake` and the InfluxDB database will
+be configured for you.
+
+For more information see the [influxdb-management README](https://gitlab.com/gitlab-org/influxdb-management/blob/master/README.md).
+
 ## Import Dashboards
 
 You can now import a set of default dashboards that will give you a good
diff --git a/doc/project_services/bugzilla.md b/doc/project_services/bugzilla.md
new file mode 100644
index 0000000000000000000000000000000000000000..215ed6fe9cc4f45677846843eb6c9d2ed9a44c94
--- /dev/null
+++ b/doc/project_services/bugzilla.md
@@ -0,0 +1,17 @@
+# Bugzilla Service
+
+Go to your project's **Settings > Services > Bugzilla** and fill in the required
+details as described in the table below.
+
+| Field | Description |
+| ----- | ----------- |
+| `description`   | A name for the issue tracker (to differentiate between instances, for example) |
+| `project_url`   | The URL to the project in Bugzilla which is being linked to this GitLab project. Note that the `project_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. |
+| `issues_url`    | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
+| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. |
+
+Once you have configured and enabled Bugzilla:
+
+- the **Issues** link on the GitLab project pages takes you to the appropriate
+  Bugzilla product page
+- clicking **New issue** on the project dashboard takes you to Bugzilla for entering a new issue
diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md
index f81a035f70b2cad13ed8b0d3936e6fb1a47d47cf..e15d5db3253e0860321b46de8fc8ad50cdf80119 100644
--- a/doc/project_services/project_services.md
+++ b/doc/project_services/project_services.md
@@ -30,6 +30,7 @@ further configuration instructions and details. Contributions are welcome.
 | [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server |
 | Buildkite | Continuous integration and deployments |
 | [Builds emails](builds_emails.md) |	Email the builds status to a list of recipients |
+| [Bugzilla](bugzilla.md) | Bugzilla issue tracker |
 | Campfire | Simple web-based real-time group chat |
 | Custom Issue Tracker | Custom issue tracker |
 | Drone CI | Continuous Integration platform built on Docker, written in Go |
diff --git a/doc/update/2.6-to-3.0.md b/doc/update/2.6-to-3.0.md
index 4827ef9501a112732c0eb5ac5726ceef7e72aabb..fb70eaacbc95af0524ca1117a9de3fec84103541 100644
--- a/doc/update/2.6-to-3.0.md
+++ b/doc/update/2.6-to-3.0.md
@@ -13,6 +13,10 @@
 git fetch origin
 git checkout v3.0.3
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u gitlab -H vim Gemfile
 
 # Install libs
 sudo -u gitlab bundle install --without development test postgres
diff --git a/doc/update/2.9-to-3.0.md b/doc/update/2.9-to-3.0.md
index f4a997a8c5e5676b2f33937e2331f52e06a98b8d..ce46b57c09ae6989447d95bee343e9fbf588d065 100644
--- a/doc/update/2.9-to-3.0.md
+++ b/doc/update/2.9-to-3.0.md
@@ -13,6 +13,11 @@
 sudo -u gitlab -H git fetch origin
 sudo -u gitlab -H git checkout v3.0.3
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u gitlab -H vim Gemfile
+
 # Install gems
 sudo -u gitlab -H bundle install --without development test postgres
 
diff --git a/doc/update/3.0-to-3.1.md b/doc/update/3.0-to-3.1.md
index a30485c42f799a396141d9c546801b2c3e266a0e..6ac83f3b60dd579ea5fa2055dfb4e73d0d11f5d6 100644
--- a/doc/update/3.0-to-3.1.md
+++ b/doc/update/3.0-to-3.1.md
@@ -25,6 +25,11 @@ sudo -u gitlab -H git checkout v3.1.0
 # Install new charlock_holmes
 sudo gem install charlock_holmes --version '0.6.9'
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u gitlab -H vim Gemfile
+
 # Install gems for MySQL
 sudo -u gitlab -H bundle install --without development test postgres sqlite
 
diff --git a/doc/update/3.1-to-4.0.md b/doc/update/3.1-to-4.0.md
index f1ef4df4744cae9304762ce956b8333346a12ee1..df53ed6de83cb4169f370f22a6096d8ee0ce0e73 100644
--- a/doc/update/3.1-to-4.0.md
+++ b/doc/update/3.1-to-4.0.md
@@ -26,6 +26,11 @@ I wrote a bash script which will do it automatically for you. Just make sure all
 sudo -u gitlab -H git fetch
 sudo -u gitlab -H git checkout 4-0-stable
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u gitlab -H vim Gemfile
+
 # Install gems for MySQL
 sudo -u gitlab -H bundle install --without development test postgres
 
diff --git a/doc/update/4.0-to-4.1.md b/doc/update/4.0-to-4.1.md
index d89d523591706ce0830300c77e17f1a5460dfcf7..c163bfd348d285b0d33142d9185ca17ef00cbe42 100644
--- a/doc/update/4.0-to-4.1.md
+++ b/doc/update/4.0-to-4.1.md
@@ -22,6 +22,11 @@ cd /home/gitlab/gitlab/
 sudo -u gitlab -H git fetch
 sudo -u gitlab -H git checkout 4-1-stable
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u gitlab -H vim Gemfile
+
 # Install gems for MySQL
 sudo -u gitlab -H bundle install --without development test postgres
 
diff --git a/doc/update/4.1-to-4.2.md b/doc/update/4.1-to-4.2.md
index 6fe4412ff90afaddee77e1b266b42e0fd1ac42a3..97367c5f347932a3ac17f1600711dc6f062a94de 100644
--- a/doc/update/4.1-to-4.2.md
+++ b/doc/update/4.1-to-4.2.md
@@ -17,7 +17,15 @@ sudo -u gitlab -H git fetch
 
 sudo -u gitlab -H git checkout 4-2-stable
 
-# Install libs
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u gitlab -H vim Gemfile
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u gitlab -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u gitlab -H bundle install --without development test postgres --deployment
 
 # update db
diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md
index f9faf65f9525d29367cc7fc21772ac5eb8cc2e7b..ee6de51c9233207055f19bdd0f044b419f173a99 100644
--- a/doc/update/4.2-to-5.0.md
+++ b/doc/update/4.2-to-5.0.md
@@ -85,8 +85,17 @@ sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml
 # edit it
 sudo -u git -H vim config/gitlab.yml
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
 
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
+
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production
 sudo -u git -H bundle exec rake gitlab:shell:build_missing_projects RAILS_ENV=production
diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md
index 9fbd1f88515eb62ec289c375df647c1458bd0d32..f0fddcf83afd9e9e1d7edde97c4f74eccfed30f7 100644
--- a/doc/update/5.0-to-5.1.md
+++ b/doc/update/5.0-to-5.1.md
@@ -42,7 +42,17 @@ cd /home/git/gitlab
 sudo rm tmp/sockets/gitlab.socket
 sudo -u git -H cp config/puma.rb.example config/puma.rb
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
+
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake migrate_merge_requests RAILS_ENV=production
 sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md
index cf9c4e4f770437662929f670e1dfc4a396a697ee..625fcc33852748956b21c5850c1325a52a037ea8 100644
--- a/doc/update/5.1-to-5.2.md
+++ b/doc/update/5.1-to-5.2.md
@@ -40,12 +40,28 @@ sudo -u git -H git checkout v1.4.0
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
 sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
diff --git a/doc/update/5.1-to-5.4.md b/doc/update/5.1-to-5.4.md
index 97a98ede070d9abbe4d3cf729b5a46fd6d444b1b..547d453914c40577ab3e7fba9054f16180c30c76 100644
--- a/doc/update/5.1-to-5.4.md
+++ b/doc/update/5.1-to-5.4.md
@@ -37,12 +37,28 @@ sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulner
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
 sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
diff --git a/doc/update/5.1-to-6.0.md b/doc/update/5.1-to-6.0.md
index a3fdd92bd2f17aac20f2b313c36a460ea069bfd8..c992c69678ea6fe415e61ca7d99677ed23a7a4e3 100644
--- a/doc/update/5.1-to-6.0.md
+++ b/doc/update/5.1-to-6.0.md
@@ -137,12 +137,28 @@ sudo apt-get install python-docutils
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production
 sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production
diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md
index 27613aeda07e536a8b170480a39df9906addd7c8..c5254f6fb0c73fb0e94b91c495b5965bf13f676e 100644
--- a/doc/update/5.2-to-5.3.md
+++ b/doc/update/5.2-to-5.3.md
@@ -31,12 +31,28 @@ sudo -u git -H git checkout 5-3-stable
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
 sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md
index 577b9a585ffb47407bd22ec2e5b8f1c5913996fc..c4a6146dcda3cbd7b4d4de6d1e1f4f36f22fe424 100644
--- a/doc/update/5.3-to-5.4.md
+++ b/doc/update/5.3-to-5.4.md
@@ -35,12 +35,28 @@ sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulner
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
 sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md
index d9c6d9bfb91cbc9b56f6865ee0761c5ba6e50af0..f0fee63432229a4b9c81afcf80dda2fe3682129c 100644
--- a/doc/update/5.4-to-6.0.md
+++ b/doc/update/5.4-to-6.0.md
@@ -73,12 +73,28 @@ sudo apt-get install python-docutils
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
 # PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production
 sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production
diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md
index c5eba1c01c457e341e28ef8b9508b082123d48f8..409faf309024d50fbc6f54f79908d423b39916dd 100644
--- a/doc/update/6.0-to-6.1.md
+++ b/doc/update/6.0-to-6.1.md
@@ -50,13 +50,28 @@ sudo -u git -H git checkout v1.7.9
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
-sudo -u git -H bundle install --without development test mysql --deployment
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
 
+# Install libs (with deployment this time)
+sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
 sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md
index a534528108a9171c1087159b5094be963659a1d4..150c7ae1c83c744724158a84d6bb31ce117ed7b9 100644
--- a/doc/update/6.1-to-6.2.md
+++ b/doc/update/6.1-to-6.2.md
@@ -45,13 +45,28 @@ sudo apt-get install logrotate
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-#PostgreSQL
-sudo -u git -H bundle install --without development test mysql --deployment
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
 
+# Install libs (with deployment this time)
+sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
 sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
diff --git a/doc/update/6.2-to-6.3.md b/doc/update/6.2-to-6.3.md
index b08ebde08084bbe584c87c4527c278bf50e93ca9..b96dfb8add7ba6728d00cfee6b639fbdcc299e71 100644
--- a/doc/update/6.2-to-6.3.md
+++ b/doc/update/6.2-to-6.3.md
@@ -40,13 +40,28 @@ The gitlab-shell config changed recently, so check for config file changes and m
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
 # PostgreSQL
-sudo -u git -H bundle install --without development test mysql --deployment
 
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
+sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 # Run database migrations
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
diff --git a/doc/update/6.3-to-6.4.md b/doc/update/6.3-to-6.4.md
index 951d92dfeb54ec0ad9214e05ad41d225e3b23838..37028be055f3dd36527d41feebeff7dae01e07e1 100644
--- a/doc/update/6.3-to-6.4.md
+++ b/doc/update/6.3-to-6.4.md
@@ -36,13 +36,28 @@ sudo -u git -H git checkout v1.8.0
 ```bash
 cd /home/git/gitlab
 
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
 # MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
 # PostgreSQL
-sudo -u git -H bundle install --without development test mysql --deployment
 
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
+sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 # Run database migrations
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
diff --git a/doc/update/6.4-to-6.5.md b/doc/update/6.4-to-6.5.md
index 0dae9a9fe594788679a21f5bb8ef30daf955b28e..982381a4db0d1ccce64eda53b8887b1acac5fb56 100644
--- a/doc/update/6.4-to-6.5.md
+++ b/doc/update/6.4-to-6.5.md
@@ -46,13 +46,28 @@ sudo -u git -H git checkout v1.8.0
 ```bash
 cd /home/git/gitlab
 
-# MySQL installations (note: the line below states '--without ... postgres')
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
+# MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-# PostgreSQL installations (note: the line below states '--without ... mysql')
-sudo -u git -H bundle install --without development test mysql --deployment
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
 
+# Install libs (with deployment this time)
+sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 # Run database migrations
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
diff --git a/doc/update/6.5-to-6.6.md b/doc/update/6.5-to-6.6.md
index c24e83eb006562fb4257bc931d516ee1e3797347..bbed2b302152861d1bf6250a70eb23a7ae7e6be1 100644
--- a/doc/update/6.5-to-6.6.md
+++ b/doc/update/6.5-to-6.6.md
@@ -46,12 +46,28 @@ sudo -u git -H git checkout v1.8.0
 ```bash
 cd /home/git/gitlab
 
-# MySQL installations (note: the line below states '--without ... postgres')
+# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0
+# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to
+# `gem "modernizr-rails", "2.7.1"``
+sudo -u git -H vim Gemfile
+
+# MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-# PostgreSQL installations (note: the line below states '--without ... mysql')
+# PostgreSQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 
 # Run database migrations
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
diff --git a/doc/update/6.6-to-6.7.md b/doc/update/6.6-to-6.7.md
index b4298c93429b2d28e34e5ac74ad278edf3e90065..8e82942a1a0fce54472fd5e91182033b56ab6e90 100644
--- a/doc/update/6.6-to-6.7.md
+++ b/doc/update/6.6-to-6.7.md
@@ -46,13 +46,23 @@ sudo -u git -H git checkout v1.9.1
 ```bash
 cd /home/git/gitlab
 
-# MySQL installations (note: the line below states '--without ... postgres')
+# MySQL
+
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test postgres --no-deployment
+
+# Install libs (with deployment this time)
 sudo -u git -H bundle install --without development test postgres --deployment
 
-# PostgreSQL installations (note: the line below states '--without ... mysql')
-sudo -u git -H bundle install --without development test mysql --deployment
+# PostgreSQL
 
+# Run a bundle install without deployment to generate the new Gemfile
+sudo -u git -H bundle install --without development test mysql --no-deployment
+
+# Install libs (with deployment this time)
+sudo -u git -H bundle install --without development test mysql --deployment
 
+# Both MySQL and PostgreSQL
 # Run database migrations
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 
diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md
index c45fc9340ea38c95c45d7c9817085cb9e7285eb5..f170a0021b7d2b5b70ecc3dea0504deb31627612 100644
--- a/doc/update/6.x-or-7.x-to-7.14.md
+++ b/doc/update/6.x-or-7.x-to-7.14.md
@@ -147,12 +147,15 @@ sudo -u git -H bundle install --without development test postgres --deployment
 # PostgreSQL installations (note: the line below states '--without ... mysql')
 sudo -u git -H bundle install --without development test mysql --deployment
 
-# Run database migrations
-sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+# Run database migrations from 6.0 to 6.1
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production VERSION=20130909132950
 
 # Enable internal issue IDs (introduced in GitLab 6.1)
 sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
 
+# Run left database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
 # Clean up assets and cache
 sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
 
diff --git a/doc/update/8.8-to-8.9.md b/doc/update/8.8-to-8.9.md
index f14046bb4be5595069833c8e475375bb930078be..423140a92c7bd515c1b496d93ffd46d8c4a762d7 100644
--- a/doc/update/8.8-to-8.9.md
+++ b/doc/update/8.8-to-8.9.md
@@ -122,6 +122,19 @@ via [/etc/default/gitlab].
 [Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
 [/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/lib/support/init.d/gitlab.default.example#L37
 
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/v8.9.0/config/initializers/smtp_settings.rb.sample#L13
+
 #### Init script
 
 Ensure you're still up-to-date with the latest init script changes:
diff --git a/doc/user/project/highlighting.md b/doc/user/project/highlighting.md
new file mode 100644
index 0000000000000000000000000000000000000000..73a2d176b54959b1cd6802fbf55ca124faf4ad12
--- /dev/null
+++ b/doc/user/project/highlighting.md
@@ -0,0 +1,31 @@
+[Rouge]: https://rubygems.org/gems/rouge
+
+# Syntax Highlighting
+
+GitLab provides syntax highlighting on all files and snippets through the [Rouge][] rubygem. It will try to guess what language to use based on the file extension, which most of the time is sufficient.
+
+If GitLab is guessing wrong, you can override its choice of language using the `gitlab-language` attribute in `.gitattributes`. For example, if you are working in a Prolog project and using the `.pl` file extension (which would normally be highlighted as Perl), you can add the following to your `.gitattributes` file:
+
+``` conf
+*.pl gitlab-language=prolog
+```
+
+When you check in and push that change, all `*.pl` files in your project will be highlighted as Prolog.
+
+The paths here are simply git's builtin [`.gitattributes` interface](https://git-scm.com/docs/gitattributes).  So, if you were to invent a file format called a `Nicefile` at the root of your project that used ruby syntax, all you need is:
+
+``` conf
+/Nicefile gitlab-language=ruby
+```
+
+To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shenanigans are available through CGI options, such as:
+
+``` conf
+# json with erb in it
+/my-cool-file gitlab-language=erb?parent=json
+
+# an entire file of highlighting errors!
+/other-file gitlab-language=text?token=Error
+```
+
+Please note that these configurations will only take effect when the `.gitattributes` file is in your default branch (usually `master`).
diff --git a/doc/workflow/img/todo_list_item.png b/doc/workflow/img/todo_list_item.png
new file mode 100644
index 0000000000000000000000000000000000000000..884ba1d22a330f8deb448c31fe4012cf05648df7
Binary files /dev/null and b/doc/workflow/img/todo_list_item.png differ
diff --git a/doc/workflow/img/todos_add_todo_sidebar.png b/doc/workflow/img/todos_add_todo_sidebar.png
new file mode 100644
index 0000000000000000000000000000000000000000..126ecc2c82f57de11f78b16ee67f505b8eeb3e11
Binary files /dev/null and b/doc/workflow/img/todos_add_todo_sidebar.png differ
diff --git a/doc/workflow/img/todos_icon.png b/doc/workflow/img/todos_icon.png
index 879b3b51c2123fc5bf38158af8649eb19eb59108..a63bad0c258a6734172ccca23c487ea38c35ca45 100644
Binary files a/doc/workflow/img/todos_icon.png and b/doc/workflow/img/todos_icon.png differ
diff --git a/doc/workflow/img/todos_mark_done_sidebar.png b/doc/workflow/img/todos_mark_done_sidebar.png
new file mode 100644
index 0000000000000000000000000000000000000000..f449f977dd656de8fea0592556f8e23c584d75a7
Binary files /dev/null and b/doc/workflow/img/todos_mark_done_sidebar.png differ
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 5f440fdafdd4b0d5bb9c752949d57235a83e545f..9524ffd54200ad960e287a7c725b084816435fcb 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -1,4 +1,4 @@
-# GitLab ToDos
+# GitLab Todos
 
 >**Note:** This feature was [introduced][ce-2817] in GitLab 8.5.
 
@@ -14,8 +14,9 @@ in a simple dashboard.
 
 ---
 
-You can access quickly your Todos dashboard by clicking the round gray icon
-next to the search bar in the upper right corner.
+You can quickly access the Todos dashboard using the bell icon next to the
+search bar in the upper right corner. The number in blue is the number of Todos
+you still have open.
 
 ![Todos icon](img/todos_icon.png)
 
@@ -29,45 +30,61 @@ A Todo appears in your Todos dashboard when:
 
 >**Note:** Commenting on a commit will _not_ trigger a Todo.
 
-## How a Todo is marked as Done
+### Manually creating a Todo
+
+You can also add an issue or merge request to your Todos dashboard by clicking
+the "Add Todo" button in the issue or merge request sidebar.
+
+![Adding a Todo from the issuable sidebar](img/todos_add_todo_sidebar.png)
+
+## Marking a Todo as done
 
 Any action to the corresponding issue or merge request will mark your Todo as
-**Done**. This action can include:
+**Done**. Actions that dismiss Todos include:
 
 - changing the assignee
 - changing the milestone
 - adding/removing a label
 - commenting on the issue
 
-In case where you think no action is needed, you can manually mark the todo as
-done by clicking the corresponding **Done** button, and it will disappear from
-your Todos list. If you want to mark all your Todos as done, just click on the
-**Mark all as done** button.
-
 ---
 
-In order for a Todo to be marked as done, the action must be coming from you.
-So, if you close the related issue or merge the merge request yourself, and you
-had a Todo for that, it will automatically get marked as done. On the other
-hand, if someone else closes, merges or takes action on the issue or merge
-request, your Todo will remain pending. This makes sense because you may need
-to give attention to an issue even if it has been resolved.
+Todos are personal, and they're only marked as done if the action is coming from
+you. If you close the issue or merge request, your Todo will automatically
+be marked as done.
+
+If someone else closes, merges, or takes action on the issue or merge
+request, your Todo will remain pending. This prevents other users from closing issues without you being notified.
 
 There is just one Todo per issue or merge request, so mentioning a user a
 hundred times in an issue will only trigger one Todo.
 
+---
+
+If no action is needed, you can manually mark the Todo as done by clicking the
+corresponding **Done** button, and it will disappear from your Todo list.
+
+![A Todo in the Todos dashboard](img/todo_list_item.png)
+
+A Todo can also be marked as done from the issue or merge request sidebar using
+the "Mark Done" button.
+
+![Mark Done from the issuable sidebar](img/todos_mark_done_sidebar.png)
+
+You can mark all your Todos as done at once by clicking on the **Mark all as
+done** button.
+
 ## Filtering your Todos
 
-In general, there are four kinds of filters you can use on your Todos
-dashboard:
+There are four kinds of filters you can use on your Todos dashboard.
 
-| Filter | Description |
-| ------ | ----------- |
+| Filter  | Description |
+| ------- | ----------- |
 | Project | Filter by project |
 | Author  | Filter by the author that triggered the Todo |
 | Type    | Filter by issue or merge request |
 | Action  | Filter by the action that triggered the Todo (Assigned or Mentioned)|
 
-You can choose more than one filters at the same time.
+You can also filter by more than one of these at the same time.
 
 [ce-2817]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2817
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index 29e6b9f1a016284ec0563d899b22f6ab6e83b002..31f8924c38c38c04e563bfb01a154240b1146d09 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -10,7 +10,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
   end
 
   step 'I see "New Project" page' do
-    expect(page).to have_content('Project owner')
+    expect(page).to have_content('Project path')
     expect(page).to have_content('Project name')
   end
 
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index 1b14659b4dfb7ccdedba8a383e13c46c731bf6b3..1498f899cf588f2531d04f2fde4d354d8445838f 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -81,9 +81,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
   end
 
   step 'I search "hand"' do
-    page.within('.emoji-menu-content') do
-      fill_in 'emoji_search', with: 'hand'
-    end
+    fill_in 'emoji_search', with: 'hand'
   end
 
   step 'I see search result for "hand"' do
diff --git a/lib/api/api.rb b/lib/api/api.rb
index f8f680a6311f087584f7cd649c43b687d50a96b8..c3fff8b2f8f11de5349e1af8e069fe0583d1b198 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -39,7 +39,7 @@ class API < Grape::API
     mount ::API::Issues
     mount ::API::Keys
     mount ::API::Labels
-    mount ::API::Licenses
+    mount ::API::LicenseTemplates
     mount ::API::MergeRequests
     mount ::API::Milestones
     mount ::API::Namespaces
diff --git a/lib/api/builds.rb b/lib/api/builds.rb
index 979328efe0edb2f9e2bdd37009263ad2db08c3ee..086d8511e8ffafd3da92cb007058719701eb79be 100644
--- a/lib/api/builds.rb
+++ b/lib/api/builds.rb
@@ -33,10 +33,10 @@ class Builds < Grape::API
       get ':id/repository/commits/:sha/builds' do
         authorize_read_builds!
 
-        commit = user_project.pipelines.find_by_sha(params[:sha])
-        return not_found! unless commit
+        return not_found! unless user_project.commit(params[:sha])
 
-        builds = commit.builds.order('id DESC')
+        pipelines = user_project.pipelines.where(sha: params[:sha])
+        builds = user_project.builds.where(pipeline: pipelines).order('id DESC')
         builds = filter_builds(builds, params[:scope])
 
         present paginate(builds), with: Entities::Build,
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 4c43257c48a75dffb7ea70f999d59e91669a23b5..8a03a41e9c592345685ce44e96375a82923387f6 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -59,6 +59,41 @@ def create_spam_log(project, current_user, attrs)
       end
     end
 
+    resource :groups do
+      # Get a list of group issues
+      #
+      # Parameters:
+      #   id (required) - The ID of a group
+      #   state (optional) - Return "opened" or "closed" issues
+      #   labels (optional) - Comma-separated list of label names
+      #   milestone (optional) - Milestone title
+      #   order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
+      #   sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
+      #
+      # Example Requests:
+      #   GET /groups/:id/issues
+      #   GET /groups/:id/issues?state=opened
+      #   GET /groups/:id/issues?state=closed
+      #   GET /groups/:id/issues?labels=foo
+      #   GET /groups/:id/issues?labels=foo,bar
+      #   GET /groups/:id/issues?labels=foo,bar&state=opened
+      #   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])
+
+        params[:state] ||= 'opened'
+        params[:group_id] = group.id
+        params[:milestone_title] = params.delete(:milestone)
+        params[:label_name] = params.delete(:labels)
+        params[:sort] = "#{params.delete(:order_by)}_#{params.delete(:sort)}" if params[:order_by] && params[:sort]
+
+        issues = IssuesFinder.new(current_user, params).execute
+
+        present paginate(issues), with: Entities::Issue, current_user: current_user
+      end
+    end
+
     resource :projects do
       # Get a list of project issues
       #
diff --git a/lib/api/licenses.rb b/lib/api/license_templates.rb
similarity index 96%
rename from lib/api/licenses.rb
rename to lib/api/license_templates.rb
index be0e113fbcb8519e6b3798022a0fecfff8f89cd3..d0552299ed0788d6b19ff97bbea54f7793efce18 100644
--- a/lib/api/licenses.rb
+++ b/lib/api/license_templates.rb
@@ -1,6 +1,6 @@
 module API
-  # Licenses API
-  class Licenses < Grape::API
+  # License Templates API
+  class LicenseTemplates < Grape::API
     PROJECT_TEMPLATE_REGEX =
       /[\<\{\[]
         (project|description|
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
index ccd106860bd276ef21e2204bf7b0ebd167b1b396..8aa6f8f124a8e0ccbad7493a7a80c78390f5e2cf 100644
--- a/lib/banzai/filter/image_link_filter.rb
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -9,6 +9,11 @@ class ImageLinkFilter < HTML::Pipeline::Filter
       def call
         doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |img|
 
+          div = doc.document.create_element(
+            'div',
+            class: 'image-container'
+          )
+
           link = doc.document.create_element(
             'a',
             class: 'no-attachment-icon',
@@ -17,7 +22,10 @@ def call
           )
 
           link.children = img.clone
-          img.replace(link)
+
+          div.children = link
+
+          img.replace(div)
         end
 
         doc
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 2614261f9ebc99fd9a6d93bc35a4212f4d281d32..5351272f42d0b4ce514d9f06a17e0968e90f767e 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -31,10 +31,14 @@ def issues_per_project
           projects_per_reference.each do |path, project|
             issue_ids = references_per_project[path]
 
-            next unless project.default_issues_tracker?
+            if project.default_issues_tracker?
+              issues = project.issues.where(iid: issue_ids.to_a)
+            else
+              issues = issue_ids.map { |id| ExternalIssue.new(id, project) }
+            end
 
-            project.issues.where(iid: issue_ids.to_a).each do |issue|
-              hash[project][issue.iid] = issue
+            issues.each do |issue|
+              hash[project][issue.iid.to_i] = issue
             end
           end
 
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index c753a84a20d9a2d556cfc857d46f8f507d6ce2b4..c59a80dd1c7388441099cf9b14e6e05cf6cfdce6 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -7,40 +7,13 @@ module Filter
     #
     class RedactorFilter < HTML::Pipeline::Filter
       def call
-        nodes = Querying.css(doc, 'a.gfm[data-reference-type]')
-        visible = nodes_visible_to_user(nodes)
-
-        nodes.each do |node|
-          unless visible.include?(node)
-            # The reference should be replaced by the original text,
-            # which is not always the same as the rendered text.
-            text = node.attr('data-original') || node.text
-            node.replace(text)
-          end
-        end
+        Redactor.new(project, current_user).redact([doc])
 
         doc
       end
 
       private
 
-      def nodes_visible_to_user(nodes)
-        per_type = Hash.new { |h, k| h[k] = [] }
-        visible = Set.new
-
-        nodes.each do |node|
-          per_type[node.attr('data-reference-type')] << node
-        end
-
-        per_type.each do |type, nodes|
-          parser = Banzai::ReferenceParser[type].new(project, current_user)
-
-          visible.merge(parser.nodes_visible_to_user(current_user, nodes))
-        end
-
-        visible
-      end
-
       def current_user
         context[:current_user]
       end
diff --git a/lib/banzai/note_renderer.rb b/lib/banzai/note_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bab6a9934d171ca4213f42e3db37ed79d7b8a0c7
--- /dev/null
+++ b/lib/banzai/note_renderer.rb
@@ -0,0 +1,22 @@
+module Banzai
+  module NoteRenderer
+    # Renders a collection of Note instances.
+    #
+    # notes - The notes to render.
+    # project - The project to use for rendering/redacting.
+    # user - The user viewing the notes.
+    # path - The request path.
+    # wiki - The project's wiki.
+    # git_ref - The current Git reference.
+    def self.render(notes, project, user = nil, path = nil, wiki = nil, git_ref = nil)
+      renderer = ObjectRenderer.new(project,
+                                    user,
+                                    requested_path: path,
+                                    project_wiki: wiki,
+                                    ref: git_ref,
+                                    pipeline: :note)
+
+      renderer.render(notes, :note)
+    end
+  end
+end
diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f0e4f28bf12312c67fa12472a5e9f7e8876a642b
--- /dev/null
+++ b/lib/banzai/object_renderer.rb
@@ -0,0 +1,85 @@
+module Banzai
+  # Class for rendering multiple objects (e.g. Note instances) in a single pass.
+  #
+  # Rendered Markdown is stored in an attribute in every object based on the
+  # name of the attribute containing the Markdown. For example, when the
+  # attribute `note` is rendered the HTML is stored in `note_html`.
+  class ObjectRenderer
+    attr_reader :project, :user
+
+    # Make sure to set the appropriate pipeline in the `raw_context` attribute
+    # (e.g. `:note` for Note instances).
+    #
+    # project - A Project to use for rendering and redacting Markdown.
+    # user - The user viewing the Markdown/HTML documents, if any.
+    # context - A Hash containing extra attributes to use in the rendering
+    #           pipeline.
+    def initialize(project, user = nil, raw_context = {})
+      @project = project
+      @user = user
+      @raw_context = raw_context
+    end
+
+    # Renders and redacts an Array of objects.
+    #
+    # objects - The objects to render
+    # attribute - The attribute containing the raw Markdown to render.
+    #
+    # Returns the same input objects.
+    def render(objects, attribute)
+      documents = render_objects(objects, attribute)
+      redacted = redact_documents(documents)
+
+      objects.each_with_index do |object, index|
+        object.__send__("#{attribute}_html=", redacted.fetch(index))
+      end
+
+      objects
+    end
+
+    # Renders the attribute of every given object.
+    def render_objects(objects, attribute)
+      objects.map do |object|
+        render_attribute(object, attribute)
+      end
+    end
+
+    # Redacts the list of documents.
+    #
+    # Returns an Array containing the redacted documents.
+    def redact_documents(documents)
+      redactor = Redactor.new(project, user)
+
+      redactor.redact(documents).map do |document|
+        document.to_html.html_safe
+      end
+    end
+
+    # Returns a Banzai context for the given object and attribute.
+    def context_for(object, attribute)
+      context = base_context.merge(cache_key: [object, attribute])
+
+      if object.respond_to?(:author)
+        context[:author] = object.author
+      end
+
+      context
+    end
+
+    # Renders the attribute of an object.
+    #
+    # Returns a `Nokogiri::HTML::Document`.
+    def render_attribute(object, attribute)
+      context = context_for(object, attribute)
+
+      string = object.__send__(attribute)
+      html = Banzai.render(string, context)
+
+      Banzai::Pipeline[:relative_link].to_document(html, context)
+    end
+
+    def base_context
+      @base_context ||= @raw_context.merge(current_user: user, project: project)
+    end
+  end
+end
diff --git a/lib/banzai/pipeline/relative_link_pipeline.rb b/lib/banzai/pipeline/relative_link_pipeline.rb
new file mode 100644
index 0000000000000000000000000000000000000000..270990e7ab4ee5666ccf95b7361b18f54a392414
--- /dev/null
+++ b/lib/banzai/pipeline/relative_link_pipeline.rb
@@ -0,0 +1,11 @@
+module Banzai
+  module Pipeline
+    class RelativeLinkPipeline < BasePipeline
+      def self.filters
+        FilterArray[
+          Filter::RelativeLinkFilter
+        ]
+      end
+    end
+  end
+end
diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ffd267d5e9ae48421f8868c1099d73fc0d670363
--- /dev/null
+++ b/lib/banzai/redactor.rb
@@ -0,0 +1,69 @@
+module Banzai
+  # Class for removing Markdown references a certain user is not allowed to
+  # view.
+  class Redactor
+    attr_reader :user, :project
+
+    # project - A Project to use for redacting links.
+    # user - The currently logged in user (if any).
+    def initialize(project, user = nil)
+      @project = project
+      @user = user
+    end
+
+    # Redacts the references in the given Array of documents.
+    #
+    # This method modifies the given documents in-place.
+    #
+    # documents - A list of HTML documents containing references to redact.
+    #
+    # Returns the documents passed as the first argument.
+    def redact(documents)
+      nodes = documents.flat_map do |document|
+        Querying.css(document, 'a.gfm[data-reference-type]')
+      end
+
+      redact_nodes(nodes)
+
+      documents
+    end
+
+    # Redacts the given nodes
+    #
+    # nodes - An Array of HTML nodes to redact.
+    def redact_nodes(nodes)
+      visible = nodes_visible_to_user(nodes)
+
+      nodes.each do |node|
+        unless visible.include?(node)
+          # The reference should be replaced by the original text,
+          # which is not always the same as the rendered text.
+          text = node.attr('data-original') || node.text
+          node.replace(text)
+        end
+      end
+    end
+
+    # Returns the nodes visible to the current user.
+    #
+    # nodes - The input nodes to check.
+    #
+    # Returns a new Array containing the visible nodes.
+    def nodes_visible_to_user(nodes)
+      per_type = Hash.new { |h, k| h[k] = [] }
+      visible = Set.new
+
+      nodes.each do |node|
+        per_type[node.attr('data-reference-type')] << node
+      end
+
+      per_type.each do |type, nodes|
+        parser = Banzai::ReferenceParser[type].new(project, user)
+
+        visible.merge(parser.nodes_visible_to_user(user, nodes))
+      end
+
+      visible
+    end
+  end
+end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index ed86de819eb7c5c8add2031cfc6a06facdf597c3..c52d4d6338209d5c1cb05f6ad247b929649e7072 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -2,7 +2,7 @@ module Ci
   class GitlabCiYamlProcessor
     class ValidationError < StandardError; end
 
-    include Gitlab::Ci::Config::Node::ValidationHelpers
+    include Gitlab::Ci::Config::Node::LegacyValidationHelpers
 
     DEFAULT_STAGES = %w(build test deploy)
     DEFAULT_STAGE = 'test'
diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb
index 997a22779a0406ba08e9a749dcb1ab54c38b8488..d62bc50ce78e06536cb276d3432fe024d88837b2 100644
--- a/lib/gitlab/blame.rb
+++ b/lib/gitlab/blame.rb
@@ -41,7 +41,8 @@ def blame
 
     def highlighted_lines
       @blob.load_all_data!(repository)
-      @highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines
+      @highlighted_lines ||=
+        Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines
     end
 
     def project
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index b48d3592f1699c6473700703b299b3ccd3e6968c..adfd097736e3e0347dd6dd889ed13b4aa38b658e 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -4,8 +4,6 @@ module Ci
     # Base GitLab CI Configuration facade
     #
     class Config
-      delegate :valid?, :errors, to: :@global
-
       ##
       # Temporary delegations that should be removed after refactoring
       #
@@ -18,6 +16,14 @@ def initialize(config)
         @global.process!
       end
 
+      def valid?
+        @global.valid?
+      end
+
+      def errors
+        @global.errors
+      end
+
       def to_hash
         @config
       end
diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/node/configurable.rb
index d60f87f3f94d5e127932c410600aa2183c89a3bc..374ff71d0f5544e40bc0ac9eeb04a164d000da57 100644
--- a/lib/gitlab/ci/config/node/configurable.rb
+++ b/lib/gitlab/ci/config/node/configurable.rb
@@ -15,27 +15,24 @@ module Node
         #
         module Configurable
           extend ActiveSupport::Concern
+          include Validatable
 
-          def allowed_nodes
-            self.class.allowed_nodes || {}
+          included do
+            validations do
+              validates :config, hash: true
+            end
           end
 
           private
 
-          def prevalidate!
-            unless @value.is_a?(Hash)
-              @errors << 'should be a configuration entry with hash value'
-            end
-          end
-
           def create_node(key, factory)
-            factory.with(value: @value[key])
-            factory.nullify! unless @value.has_key?(key)
+            factory.with(value: @config[key], key: key)
+            factory.nullify! unless @config.has_key?(key)
             factory.create!
           end
 
           class_methods do
-            def allowed_nodes
+            def nodes
               Hash[@allowed_nodes.map { |key, factory| [key, factory.dup] }]
             end
 
@@ -47,7 +44,6 @@ def allow_node(symbol, entry_class, metadata)
 
               define_method(symbol) do
                 raise Entry::InvalidError unless valid?
-
                 @nodes[symbol].try(:value)
               end
 
diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/node/entry.rb
index 52758a962f3c79989cd36582be5dc86114049853..f044ef965e9c1a19b100a2f9bd9d55caf96a724a 100644
--- a/lib/gitlab/ci/config/node/entry.rb
+++ b/lib/gitlab/ci/config/node/entry.rb
@@ -8,14 +8,14 @@ module Node
         class Entry
           class InvalidError < StandardError; end
 
-          attr_accessor :description
+          attr_reader :config
+          attr_accessor :key, :description
 
-          def initialize(value)
-            @value = value
+          def initialize(config)
+            @config = config
             @nodes = {}
-            @errors = []
-
-            prevalidate!
+            @validator = self.class.validator.new(self)
+            @validator.validate
           end
 
           def process!
@@ -23,50 +23,54 @@ def process!
             return unless valid?
 
             compose!
-
-            nodes.each(&:process!)
-            nodes.each(&:validate!)
+            process_nodes!
           end
 
           def nodes
             @nodes.values
           end
 
-          def valid?
-            errors.none?
-          end
-
           def leaf?
-            allowed_nodes.none?
+            self.class.nodes.none?
           end
 
-          def errors
-            @errors + nodes.map(&:errors).flatten
+          def key
+            @key || self.class.name.demodulize.underscore
           end
 
-          def allowed_nodes
-            {}
+          def valid?
+            errors.none?
           end
 
-          def validate!
-            raise NotImplementedError
+          def errors
+            @validator.full_errors +
+              nodes.map(&:errors).flatten
           end
 
           def value
             raise NotImplementedError
           end
 
-          private
+          def self.nodes
+            {}
+          end
 
-          def prevalidate!
+          def self.validator
+            Validator
           end
 
+          private
+
           def compose!
-            allowed_nodes.each do |key, essence|
+            self.class.nodes.each do |key, essence|
               @nodes[key] = create_node(key, essence)
             end
           end
 
+          def process_nodes!
+            nodes.each(&:process!)
+          end
+
           def create_node(key, essence)
             raise NotImplementedError
           end
diff --git a/lib/gitlab/ci/config/node/factory.rb b/lib/gitlab/ci/config/node/factory.rb
index 787ca006f5abb8291ee4a034a56348db121aa06f..025ae40ef944ebb5a8ea4ccbe5d8a7949f11142a 100644
--- a/lib/gitlab/ci/config/node/factory.rb
+++ b/lib/gitlab/ci/config/node/factory.rb
@@ -30,6 +30,7 @@ def create!
 
             @entry_class.new(@attributes[:value]).tap do |entry|
               entry.description = @attributes[:description]
+              entry.key = @attributes[:key]
             end
           end
         end
diff --git a/lib/gitlab/ci/config/node/validation_helpers.rb b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb
similarity index 97%
rename from lib/gitlab/ci/config/node/validation_helpers.rb
rename to lib/gitlab/ci/config/node/legacy_validation_helpers.rb
index 72f648975dcf1c3ee2f6249ef31499ad535309ae..4d9a508796abff70c3731d0a2248998e7d5a83ab 100644
--- a/lib/gitlab/ci/config/node/validation_helpers.rb
+++ b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb
@@ -2,7 +2,7 @@ module Gitlab
   module Ci
     class Config
       module Node
-        module ValidationHelpers
+        module LegacyValidationHelpers
           private
 
           def validate_duration(value)
diff --git a/lib/gitlab/ci/config/node/script.rb b/lib/gitlab/ci/config/node/script.rb
index 5072bf0db7d56ef4016629cfb229ffa8a6b0fb5c..c044f5c5e717b38edb77c22b8fcee2f9978065c0 100644
--- a/lib/gitlab/ci/config/node/script.rb
+++ b/lib/gitlab/ci/config/node/script.rb
@@ -11,16 +11,14 @@ module Node
         # implementation in Runner.
         #
         class Script < Entry
-          include ValidationHelpers
+          include Validatable
 
-          def value
-            @value.join("\n")
+          validations do
+            validates :config, array_of_strings: true
           end
 
-          def validate!
-            unless validate_array_of_strings(@value)
-              @errors << 'before_script should be an array of strings'
-            end
+          def value
+            @config.join("\n")
           end
         end
       end
diff --git a/lib/gitlab/ci/config/node/validatable.rb b/lib/gitlab/ci/config/node/validatable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f6e2896dfb23ce2760a62aa46f50a1dbc5ba15e6
--- /dev/null
+++ b/lib/gitlab/ci/config/node/validatable.rb
@@ -0,0 +1,29 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        module Validatable
+          extend ActiveSupport::Concern
+
+          class_methods do
+            def validator
+              validator = Class.new(Node::Validator)
+
+              if defined?(@validations)
+                @validations.each { |rules| validator.class_eval(&rules) }
+              end
+
+              validator
+            end
+
+            private
+
+            def validations(&block)
+              (@validations ||= []).append(block)
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/validator.rb b/lib/gitlab/ci/config/node/validator.rb
new file mode 100644
index 0000000000000000000000000000000000000000..02edc9219c395330ee2fc6eac9bfbceb6373858c
--- /dev/null
+++ b/lib/gitlab/ci/config/node/validator.rb
@@ -0,0 +1,27 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        class Validator < SimpleDelegator
+          include ActiveModel::Validations
+          include Node::Validators
+
+          def initialize(node)
+            super(node)
+            @node = node
+          end
+
+          def full_errors
+            errors.full_messages.map do |error|
+              "#{@node.key} #{error}".humanize
+            end
+          end
+
+          def self.name
+            'Validator'
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/validators.rb b/lib/gitlab/ci/config/node/validators.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dc9cdb9a2205038fc928b8a8f06662e55f649476
--- /dev/null
+++ b/lib/gitlab/ci/config/node/validators.rb
@@ -0,0 +1,27 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        module Validators
+          class ArrayOfStringsValidator < ActiveModel::EachValidator
+            include LegacyValidationHelpers
+
+            def validate_each(record, attribute, value)
+              unless validate_array_of_strings(value)
+                record.errors.add(attribute, 'should be an array of strings')
+              end
+            end
+          end
+
+          class HashValidator < ActiveModel::EachValidator
+            def validate_each(record, attribute, value)
+              unless value.is_a?(Hash)
+                record.errors.add(attribute, 'should be a configuration entry hash')
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index f751a3a12fdf90c46533a4e9d2fe8c50a66a0a37..d4f12cb1df9716cc86c7b115049ec03f9a370ee6 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -3,7 +3,6 @@ module GonHelper
     def add_gon_variables
       gon.api_version            = API::API.version
       gon.default_avatar_url     = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
-      gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
       gon.max_file_size          = current_application_settings.max_attachment_size
       gon.relative_url_root      = Gitlab.config.gitlab.relative_url_root
       gon.shortcuts_path         = help_shortcuts_path
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 280120b0f9eb04a04aee5b18849a384229009930..41296415e35700619c36d02ba5982d1c4018e0a5 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -1,7 +1,7 @@
 module Gitlab
   class Highlight
-    def self.highlight(blob_name, blob_content, nowrap: true, plain: false)
-      new(blob_name, blob_content, nowrap: nowrap).
+    def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false)
+      new(blob_name, blob_content, nowrap: nowrap, repository: repository).
         highlight(blob_content, continue: false, plain: plain)
     end
 
@@ -10,12 +10,21 @@ def self.highlight_lines(repository, ref, file_name)
       return [] unless blob
 
       blob.load_all_data!(repository)
-      highlight(file_name, blob.data).lines.map!(&:html_safe)
+      highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
     end
 
-    def initialize(blob_name, blob_content, nowrap: true)
+    attr_reader :lexer
+    def initialize(blob_name, blob_content, repository: nil, nowrap: true)
+      @blob_name = blob_name
+      @blob_content = blob_content
+      @repository = repository
       @formatter = rouge_formatter(nowrap: nowrap)
-      @lexer = Rouge::Lexer.guess(filename: blob_name, source: blob_content).new rescue Rouge::Lexers::PlainText
+
+      @lexer = custom_language || begin
+        Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
+      rescue Rouge::Lexer::AmbiguousGuess => e
+        e.alternatives.sort_by(&:tag).first
+      end
     end
 
     def highlight(text, continue: true, plain: false)
@@ -30,6 +39,14 @@ def highlight(text, continue: true, plain: false)
 
     private
 
+    def custom_language
+      language_name = @repository && @repository.gitattribute(@blob_name, 'gitlab-language')
+
+      return nil unless language_name
+
+      Rouge::Lexer.find_fancy(language_name)
+    end
+
     def rouge_formatter(options = {})
       options = options.reverse_merge(
         nowrap: true,
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 0e70d9282d50d1b6228d6e5e2818c9a06befbc9c..82d1e1805c5a2b585a2d17a5bbf082e21724f0e4 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -23,7 +23,11 @@ def import
       private
 
       def decompress_archive
-        untar_zxf(archive: @archive_file, dir: @shared.export_path)
+        result = untar_zxf(archive: @archive_file, dir: @shared.export_path)
+
+        raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result
+
+        true
       end
     end
   end
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index d209e04f7be5f735113b513e7743fdc0aa70b3ed..595b20a09bd34911a5f79c1d1f823ffd6fffd624 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -10,17 +10,22 @@ def initialize(project)
       end
 
       def execute
-        Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file,
-                                                  shared: @shared)
-        if check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
+        if import_file && check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
           project_tree.restored_project
         else
           raise Projects::ImportService::Error.new(@shared.errors.join(', '))
         end
+
+        remove_import_file
       end
 
       private
 
+      def import_file
+        Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file,
+                                                  shared: @shared)
+      end
+
       def check_version!
         Gitlab::ImportExport::VersionChecker.check!(shared: @shared)
       end
@@ -59,6 +64,10 @@ def repo_path
       def wiki_repo_path
         File.join(@shared.export_path, 'project.wiki.bundle')
       end
+
+      def remove_import_file
+        FileUtils.rm_rf(@archive_file)
+      end
     end
   end
 end
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index b872780f20aba50143af8cb9c8bc933b303a8a70..92bf7e0a2fced86608459b2a6ccbcdad35aa176a 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -12,6 +12,8 @@ class RelationFactory
 
       USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze
 
+      BUILD_MODELS = %w[Ci::Build commit_status].freeze
+
       def self.create(*args)
         new(*args).create
       end
@@ -70,7 +72,7 @@ def missing_author_note(updated_at, author_name)
       end
 
       def generate_imported_object
-        if @relation_sym == 'commit_status' # call #trace= method after assigning the other attributes
+        if BUILD_MODELS.include?(@relation_name) # call #trace= method after assigning the other attributes
           trace = @relation_hash.delete('trace')
           imported_object do |object|
             object.trace = trace
diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb
index faf0d9b6318f8a9da2f260ce9827e1f1597cb0af..c048fe20ba7c8e0bbcddebf1d733f60ed90fb8b0 100644
--- a/lib/gitlab/metrics/method_call.rb
+++ b/lib/gitlab/metrics/method_call.rb
@@ -18,12 +18,12 @@ def initialize(name, series)
 
       # Measures the real and CPU execution time of the supplied block.
       def measure
-        start_real = Time.now
+        start_real = System.monotonic_time
         start_cpu = System.cpu_time
         retval = yield
 
-        @real_time += (Time.now - start_real) * 1000.0
-        @cpu_time += System.cpu_time.to_f - start_cpu
+        @real_time += System.monotonic_time - start_real
+        @cpu_time += System.cpu_time - start_cpu
         @call_count += 1
 
         retval
diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb
index 1cd1ca30f701b28580bfa13b997eef4f2f2ccfda..f23d67e1e3849f4ab512c2d390110605009988d9 100644
--- a/lib/gitlab/metrics/metric.rb
+++ b/lib/gitlab/metrics/metric.rb
@@ -4,16 +4,15 @@ module Metrics
     class Metric
       JITTER_RANGE = 0.000001..0.001
 
-      attr_reader :series, :values, :tags, :created_at
+      attr_reader :series, :values, :tags
 
       # series - The name of the series (as a String) to store the metric in.
       # values - A Hash containing the values to store.
       # tags   - A Hash containing extra tags to add to the metrics.
       def initialize(series, values, tags = {})
-        @values     = values
-        @series     = series
-        @tags       = tags
-        @created_at = Time.now.utc
+        @values = values
+        @series = series
+        @tags   = tags
       end
 
       # Returns a Hash in a format that can be directly written to InfluxDB.
@@ -27,20 +26,20 @@ def to_hash
         #
         # Due to the way InfluxDB is set up there's no solution to this problem,
         # all we can do is lower the amount of collisions. We do this by using
-        # Time#to_f which returns the seconds as a Float providing greater
-        # accuracy. We then add a small random value that is large enough to
-        # distinguish most timestamps but small enough to not alter the amount
-        # of seconds.
+        # System.real_time which returns the nanoseconds as a Float providing
+        # greater accuracy. We then add a small random value that is large
+        # enough to distinguish most timestamps but small enough to not alter
+        # the timestamp significantly.
         #
         # See https://gitlab.com/gitlab-com/operations/issues/175 for more
         # information.
-        time = @created_at.to_f + rand(JITTER_RANGE)
+        time = System.real_time(:nanosecond) + rand(JITTER_RANGE)
 
         {
           series:    @series,
           tags:      @tags,
           values:    @values,
-          timestamp: (time * 1_000_000_000).to_i
+          timestamp: time.to_i
         }
       end
     end
diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb
index fd98aa3412e265b0b301bf1f8dac839724491843..a1240fd33eee739d2e62990f72e161f5cb6a0c13 100644
--- a/lib/gitlab/metrics/sidekiq_middleware.rb
+++ b/lib/gitlab/metrics/sidekiq_middleware.rb
@@ -8,6 +8,8 @@ def call(worker, message, queue)
         trans = Transaction.new("#{worker.class.name}#perform")
 
         begin
+          # Old gitlad-shell messages don't provide enqueued_at/created_at attributes
+          trans.set(:sidekiq_queue_duration, Time.now.to_f - (message['enqueued_at'] || message['created_at'] || 0))
           trans.run { yield }
         ensure
           trans.finish
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index a7d183b2f94a801f9c8f44d72034e35cf3edd312..82c18bb108b6364d69ccba84af54ecc17986a4a0 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -34,13 +34,29 @@ def self.file_descriptor_count
       # THREAD_CPUTIME is not supported on OS X
       if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID)
         def self.cpu_time
-          Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
+          Process.
+            clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond).to_f
         end
       else
         def self.cpu_time
-          Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
+          Process.
+            clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond).to_f
         end
       end
+
+      # Returns the current real time in a given precision.
+      #
+      # Returns the time as a Float.
+      def self.real_time(precision = :millisecond)
+        Process.clock_gettime(Process::CLOCK_REALTIME, precision).to_f
+      end
+
+      # Returns the current monotonic clock time in a given precision.
+      #
+      # Returns the time as a Float.
+      def self.monotonic_time(precision = :millisecond)
+        Process.clock_gettime(Process::CLOCK_MONOTONIC, precision).to_f
+      end
     end
   end
 end
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index 4bc5081aa0361e7c2879b39fc3c3120cefd95363..bded245da43de27432ecd1773ce9e8ad36cce053 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -30,7 +30,7 @@ def initialize(action = nil)
       end
 
       def duration
-        @finished_at ? (@finished_at - @started_at) * 1000.0 : 0.0
+        @finished_at ? (@finished_at - @started_at) : 0.0
       end
 
       def allocated_memory
@@ -41,12 +41,12 @@ def run
         Thread.current[THREAD_KEY] = self
 
         @memory_before = System.memory_usage
-        @started_at    = Time.now
+        @started_at    = System.monotonic_time
 
         yield
       ensure
         @memory_after = System.memory_usage
-        @finished_at  = Time.now
+        @finished_at  = System.monotonic_time
 
         Thread.current[THREAD_KEY] = nil
       end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index 78f3ecb4cb4b1453aea0b4127f3526b93c35d628..7af75a9cc4cabe419865a160daec7c19fa9e5937 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -74,7 +74,7 @@ def find_or_create_ldap_user
         if user
           # Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account.
           log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity."
-          user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
+          user.identities.find_or_initialize_by(extern_uid: auth_hash.uid, provider: auth_hash.provider)
         else
           log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account."
           user = find_by_uid_and_provider
diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb
index ae85b294d31526c1304259980ae583755d275655..4831c46c4becd74c200bad4a8ece91a897f03763 100644
--- a/lib/gitlab/sidekiq_middleware/memory_killer.rb
+++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb
@@ -25,7 +25,7 @@ def call(worker, job, queue)
 
           Sidekiq.logger.warn "current RSS #{current_rss} exceeds maximum RSS "\
             "#{MAX_RSS}"
-          Sidekiq.logger.warn "this thread will shut down PID #{Process.pid} "\
+          Sidekiq.logger.warn "this thread will shut down PID #{Process.pid} - Worker #{worker.class} - JID-#{job['jid']}"\
             "in #{GRACE_TIME} seconds"
           sleep(GRACE_TIME)
 
@@ -36,7 +36,7 @@ def call(worker, job, queue)
             "#{SHUTDOWN_SIGNAL} to PID #{Process.pid}"
           sleep(SHUTDOWN_WAIT)
 
-          Sidekiq.logger.warn "sending #{SHUTDOWN_SIGNAL} to PID #{Process.pid}"
+          Sidekiq.logger.warn "sending #{SHUTDOWN_SIGNAL} to PID #{Process.pid} - Worker #{worker.class} - JID-#{job['jid']}"
           Process.kill(SHUTDOWN_SIGNAL, Process.pid)
         end
       end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index 40e8299c36b36d5ba5e430eb11371c1d786e66be..ef1241f8600a883399df294662e592414fa34cc4 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -52,6 +52,19 @@ def send_git_diff(repository, diff_refs)
         ]
       end
 
+      def send_git_patch(repository, from, to)
+        params = {
+          'RepoPath'  => repository.path_to_repo,
+          'ShaFrom'   => from,
+          'ShaTo'     => to
+        }
+
+        [
+          SEND_DATA_HEADER,
+          "git-format-patch:#{encode(params)}"
+        ]
+      end
+
       protected
 
       def encode(hash)
diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb
index eb82476b179b56db1f455015863d52be2a94a76d..d5f0b289b5b64993b7b4bec4f58bacfc5e48d015 100644
--- a/spec/controllers/admin/impersonations_controller_spec.rb
+++ b/spec/controllers/admin/impersonations_controller_spec.rb
@@ -22,7 +22,7 @@
         it "responds with status 404" do
           delete :destroy
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
 
         it "doesn't sign us in" do
@@ -46,7 +46,7 @@
           it "responds with status 404" do
             delete :destroy
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
 
           it "doesn't sign us in as the impersonator" do
@@ -65,7 +65,7 @@
             it "responds with status 404" do
               delete :destroy
 
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
 
             it "doesn't sign us in as the impersonator" do
diff --git a/spec/controllers/admin/spam_logs_controller_spec.rb b/spec/controllers/admin/spam_logs_controller_spec.rb
index b51b303a714c4cdcba23e4bd1c230f66fd1e7873..520a4f6f9c594f4a3197a59da40f247390cc52a6 100644
--- a/spec/controllers/admin/spam_logs_controller_spec.rb
+++ b/spec/controllers/admin/spam_logs_controller_spec.rb
@@ -14,7 +14,7 @@
     it 'lists all spam logs' do
       get :index
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -22,14 +22,14 @@
     it 'removes only the spam log when removing log' do
       expect { delete :destroy, id: first_spam.id }.to change { SpamLog.count }.by(-1)
       expect(User.find(user.id)).to be_truthy
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it 'removes user and his spam logs when removing the user' do
       delete :destroy, id: first_spam.id, remove_user: true
 
       expect(flash[:notice]).to eq "User #{user.username} was successfully removed."
-      expect(response.status).to eq(302)
+      expect(response).to have_http_status(302)
       expect(SpamLog.count).to eq(0)
       expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
     end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 6caf37ddc2c3057b71ca0fc4ab4e060ea80926a5..ab9aa65f7b98aa88b14bc1213917831815d1b0f6 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -17,7 +17,7 @@
 
     it 'deletes user' do
       delete :destroy, id: user.username, format: :json
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect { User.find(user.id) }.to raise_exception(ActiveRecord::RecordNotFound)
     end
   end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index ff5b3916273a74da46495cffafce8a5ebbae476c..10824c20c8724809ee4ecc2c9f5141574c7605be 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -44,7 +44,7 @@ def index
       context "when the 'private_token' param is populated with the private token" do
         it "logs the user in" do
           get :index, private_token: user.private_token
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response.body).to eq("authenticated")
         end
       end
@@ -54,7 +54,7 @@ def index
         it "logs the user in" do
           @request.headers['PRIVATE-TOKEN'] = user.private_token
           get :index
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response.body).to eq("authenticated")
         end
       end
@@ -80,7 +80,7 @@ def index
       context "when the 'personal_access_token' param is populated with the personal access token" do
         it "logs the user in" do
           get :index, private_token: personal_access_token.token
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response.body).to eq('authenticated')
         end
       end
@@ -89,7 +89,7 @@ def index
         it "logs the user in" do
           @request.headers["PRIVATE-TOKEN"] = personal_access_token.token
           get :index
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response.body).to eq('authenticated')
         end
       end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 28cf804c1b2dfc11acd057eeb8561729e996720c..60c654f622d33d46731c24c7a42e37d74591f725 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -29,7 +29,7 @@
         get(:users, project_id: 'unknown')
       end
 
-      it { expect(response.status).to eq(404) }
+      it { expect(response).to have_http_status(404) }
     end
   end
 
@@ -58,7 +58,7 @@
         get(:users, group_id: 'unknown')
       end
 
-      it { expect(response.status).to eq(404) }
+      it { expect(response).to have_http_status(404) }
     end
   end
 
@@ -114,7 +114,7 @@
         get(:users, project_id: project.id)
       end
 
-      it { expect(response.status).to eq(404) }
+      it { expect(response).to have_http_status(404) }
     end
 
     describe 'GET #users with unknown project' do
@@ -122,7 +122,7 @@
         get(:users, project_id: 'unknown')
       end
 
-      it { expect(response.status).to eq(404) }
+      it { expect(response).to have_http_status(404) }
     end
 
     describe 'GET #users with inaccessible group' do
@@ -131,7 +131,7 @@
         get(:users, group_id: user.namespace.id)
       end
 
-      it { expect(response.status).to eq(404) }
+      it { expect(response).to have_http_status(404) }
     end
 
     describe 'GET #users with no project' do
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
index cf5c606c7236865a8e19aa5a52cd4ce84b1e7269..a3a3309e15ea3ca6556c71cc4ad85e34791b2eed 100644
--- a/spec/controllers/commit_controller_spec.rb
+++ b/spec/controllers/commit_controller_spec.rb
@@ -155,7 +155,7 @@
             id: commit.id)
 
         expect(response).not_to be_success
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -204,7 +204,7 @@
             id: master_pickable_commit.id)
 
         expect(response).not_to be_success
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index c8601341d54457927894554320a8b3eb672f6a3e..ddc54108a7b195d9f419b3057cf8e0d33859a8f7 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -13,7 +13,7 @@
     it 'renders index with group members' do
       get :index, group_id: group
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(response).to render_template(:index)
     end
   end
@@ -26,7 +26,7 @@
         delete :destroy, group_id: group,
                          id: 42
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -48,7 +48,7 @@
           delete :destroy, group_id: group,
                            id: member
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
           expect(group.users).to include group_user
         end
       end
@@ -89,7 +89,7 @@
       it 'returns 403' do
         delete :leave, group_id: group
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -118,7 +118,7 @@
         it 'cannot removes himself from the group' do
           delete :leave, group_id: group
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
@@ -166,7 +166,7 @@
         post :approve_access_request, group_id: group,
                                       id: 42
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -188,7 +188,7 @@
           post :approve_access_request, group_id: group,
                                         id: member
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
           expect(group.users).not_to include group_requester
         end
       end
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
index 0d8a68bb51a7cc2a05ace569fd892a7d58fb24d0..56ecf2bb644c4164a718a03afcabd2c158fdb912 100644
--- a/spec/controllers/health_check_controller_spec.rb
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -65,21 +65,21 @@
       it 'supports passing the token in the header' do
         request.headers['TOKEN'] = token
         get :index
-        expect(response.status).to eq(500)
+        expect(response).to have_http_status(500)
         expect(response.content_type).to eq 'text/plain'
         expect(response.body).to include('The server is on fire')
       end
 
       it 'supports failure plaintest response' do
         get :index, token: token
-        expect(response.status).to eq(500)
+        expect(response).to have_http_status(500)
         expect(response.content_type).to eq 'text/plain'
         expect(response.body).to include('The server is on fire')
       end
 
       it 'supports failure json response' do
         get :index, token: token, format: :json
-        expect(response.status).to eq(500)
+        expect(response).to have_http_status(500)
         expect(response.content_type).to eq 'application/json'
         expect(json_response['healthy']).to be false
         expect(json_response['message']).to include('The server is on fire')
@@ -87,7 +87,7 @@
 
       it 'supports failure xml response' do
         get :index, token: token, format: :xml
-        expect(response.status).to eq(500)
+        expect(response).to have_http_status(500)
         expect(response.content_type).to eq 'application/xml'
         expect(xml_response['healthy']).to be false
         expect(xml_response['message']).to include('The server is on fire')
@@ -95,7 +95,7 @@
 
       it 'supports failure responses for specific checks' do
         get :index, token: token, checks: 'email', format: :json
-        expect(response.status).to eq(500)
+        expect(response).to have_http_status(500)
         expect(response.content_type).to eq 'application/json'
         expect(json_response['healthy']).to be false
         expect(json_response['message']).to include('Email is on fire')
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
index 3c6e54839b5eb1d0ef5d76211aa9dfa81493c04d..e478a253b3fc633a5a6671843cd690968c93c078 100644
--- a/spec/controllers/invites_controller_spec.rb
+++ b/spec/controllers/invites_controller_spec.rb
@@ -15,7 +15,7 @@
       get :accept, id: token
       member.reload
 
-      expect(response.status).to eq(302)
+      expect(response).to have_http_status(302)
       expect(member.user).to eq(user)
       expect(flash[:notice]).to include 'You have been granted'
     end
@@ -26,7 +26,7 @@
       get :decline, id: token
       expect{member.reload}.to raise_error ActiveRecord::RecordNotFound
 
-      expect(response.status).to eq(302)
+      expect(response).to have_http_status(302)
       expect(flash[:notice]).to include 'You have declined the invitation to join'
     end
   end
diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb
index 27e9afe582e806f30d5147056e99bbe2e2d06ad0..2b334ed11725833867af52d1bd3927b1ab9dc477 100644
--- a/spec/controllers/namespaces_controller_spec.rb
+++ b/spec/controllers/namespaces_controller_spec.rb
@@ -86,7 +86,7 @@
             it "responds with status 404" do
               get :show, id: group.path
 
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
           end
         end
@@ -102,7 +102,7 @@
         it "responds with status 404" do
           get :show, id: "doesntexist"
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
 
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
index 07734a2dc192b404a1daef41fd7d109d7200ea06..79b819a137706e4d6584481b3319426aba6ba1c7 100644
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ b/spec/controllers/notification_settings_controller_spec.rb
@@ -101,7 +101,7 @@
              project_id: private_project.id,
              notification_setting: { level: :participating }
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -159,7 +159,7 @@
             id: notification_setting,
             notification_setting: { level: :participating }
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb
index af3783048931c255bf070582412e8bf22df7b427..552899eb36ce4f360455556fb21bbd94b4fc16c4 100644
--- a/spec/controllers/oauth/applications_controller_spec.rb
+++ b/spec/controllers/oauth/applications_controller_spec.rb
@@ -12,7 +12,7 @@
       it 'shows list of applications' do
         get :index
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'redirects back to profile page if OAuth applications are disabled' do
@@ -21,7 +21,7 @@
 
         get :index
 
-        expect(response.status).to eq(302)
+        expect(response).to have_http_status(302)
         expect(response).to redirect_to(profile_path)
       end
     end
diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb
index 4eafc11abaaa4400fa9fdccf5065df2f9d7b3383..2dc9adfd60ca8046369dc4629b259ce80ff24e39 100644
--- a/spec/controllers/profiles/accounts_controller_spec.rb
+++ b/spec/controllers/profiles/accounts_controller_spec.rb
@@ -13,7 +13,7 @@
     delete :unlink, provider: 'saml'
     updated_user = User.find(user.id)
 
-    expect(response.status).to eq(302)
+    expect(response).to have_http_status(302)
     expect(updated_user.identities.size).to eq(1)
     expect(updated_user.identities).to include(identity)
   end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9444a50b1ce772635c4ef57d791fabae91cc1026
--- /dev/null
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -0,0 +1,40 @@
+require 'rails_helper'
+
+describe Projects::BlobController do
+  let(:project) { create(:project) }
+  let(:user)    { create(:user) }
+
+  before do
+    user = create(:user)
+    project.team << [user, :master]
+
+    sign_in(user)
+  end
+
+  describe 'GET diff' do
+    render_views
+
+    def do_get(opts = {})
+      params = { namespace_id: project.namespace.to_param,
+                 project_id: project.to_param,
+                 id: 'master/CHANGELOG' }
+      get :diff, params.merge(opts)
+    end
+
+    context 'when essential params are missing' do
+      it 'renders nothing' do
+        do_get
+
+        expect(response.body).to be_blank
+      end
+    end
+
+    context 'when essential params are present' do
+      it 'renders the diff content' do
+        do_get(since: 1, to: 5, offset: 10)
+
+        expect(response.body).to be_present
+      end
+    end
+  end
+end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index c4b4a888b4ed4d010fe89e911aebc3d782963af0..f59d4937157c97812e3ddfcc38eb5c3d460ed5a0 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -103,7 +103,7 @@
            namespace_id: project.namespace.to_param,
            project_id: project.to_param
 
-      expect(response.status).to eq(303)
+      expect(response).to have_http_status(303)
     end
   end
 
@@ -121,24 +121,24 @@
     context "valid branch name, valid source" do
       let(:branch) { "feature" }
 
-      it { expect(response.status).to eq(200) }
+      it { expect(response).to have_http_status(200) }
     end
 
     context "valid branch name with unencoded slashes" do
       let(:branch) { "improve/awesome" }
 
-      it { expect(response.status).to eq(200) }
+      it { expect(response).to have_http_status(200) }
     end
 
     context "valid branch name with encoded slashes" do
       let(:branch) { "improve%2Fawesome" }
 
-      it { expect(response.status).to eq(200) }
+      it { expect(response).to have_http_status(200) }
     end
     context "invalid branch name, valid ref" do
       let(:branch) { "no-branch" }
 
-      it { expect(response.status).to eq(404) }
+      it { expect(response).to have_http_status(404) }
     end
   end
 end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index cbaa3e0b7b2a537b405b201083cfc037576d0855..7cf09fa4a4a4687173eb1a0fe80151c017f9b85e 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -14,7 +14,7 @@
     it "returns index" do
       get :index, namespace_id: project.namespace.path, project_id: project.path
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "return 301 if request path doesn't match project path" do
@@ -28,7 +28,7 @@
       project.save
 
       get :index, namespace_id: project.namespace.path, project_id: project.path
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "returns 404 when external issue tracker is enabled" do
@@ -36,7 +36,7 @@
       allow(project).to receive(:default_issues_tracker?).and_return(false)
 
       get :index, namespace_id: project.namespace.path, project_id: project.path
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -248,7 +248,7 @@ def go(id:)
       before { sign_in(user) }
       it "rejects a developer to destroy an issue" do
         delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -262,7 +262,7 @@ def go(id:)
       it "deletes the issue" do
         delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
 
-        expect(response.status).to eq(302)
+        expect(response).to have_http_status(302)
         expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now
       end
     end
@@ -280,7 +280,7 @@ def go(id:)
                                   project_id: project.path, id: issue.iid, name: "thumbsup")
       end.to change { issue.award_emoji.count }.by(1)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 4b408c03703ca32e79156478f75ed7f834b126fa..74c050f48f1b80e4af72bb9048e3ae498fc65be4 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -96,26 +96,14 @@
     end
 
     describe "as patch" do
-      include_examples "export merge as", :patch
-      let(:format) { :patch }
-
-      it "should really be a git email patch with commit" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: merge_request.iid, format: format)
-
-        expect(response.body[0..100]).to start_with("From #{merge_request.commits.last.id}")
-      end
-
-      it "should contain git diffs" do
+      it 'triggers workhorse to serve the request' do
         get(:show,
             namespace_id: project.namespace.to_param,
             project_id: project.to_param,
             id: merge_request.iid,
-            format: format)
+            format: :patch)
 
-        expect(response.body).to match(/^diff --git/)
+        expect(response.headers['Gitlab-Workhorse-Send-Data']).to start_with("git-format-patch:")
       end
     end
   end
@@ -264,6 +252,18 @@ def merge_when_build_succeeds
 
           merge_when_build_succeeds
         end
+
+        context 'when project.only_allow_merge_if_build_succeeds? is true' do
+          before do
+            project.update_column(:only_allow_merge_if_build_succeeds, true)
+          end
+
+          it 'returns :merge_when_build_succeeds' do
+            merge_when_build_succeeds
+
+            expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+          end
+        end
       end
     end
   end
@@ -272,7 +272,7 @@ def merge_when_build_succeeds
     it "denies access to users unless they're admin or project owner" do
       delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     context "when the user is owner" do
@@ -285,7 +285,7 @@ def merge_when_build_succeeds
       it "deletes the merge request" do
         delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
 
-        expect(response.status).to eq(302)
+        expect(response).to have_http_status(302)
         expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now
       end
     end
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 00bc38b60714e5a33da21d61c532551d311a746b..75590c1ed4ff97b5ec296c2dd8d2ef2a0f04eab7 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -18,7 +18,7 @@
                                   project_id: project.path, id: note.id, name: "thumbsup")
       end.to change { note.award_emoji.count }.by(1)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "removes the already awarded emoji" do
@@ -30,7 +30,7 @@
                                   project_id: project.path, id: note.id, name: "thumbsup")
       end.to change { AwardEmoji.count }.by(-1)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index e5e750c855f258c924fd35d4e9bae7fe6fb0e30d..29aaceb23025a42b964a4633afa217022a2c6a0b 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -58,7 +58,7 @@
         get :index, namespace_id: project.namespace, project_id: project
       end
 
-      it { expect(response.status).to eq(200) }
+      it { expect(response).to have_http_status(200) }
     end
   end
 
@@ -71,7 +71,7 @@
                          project_id: project,
                          id: 42
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -94,7 +94,7 @@
                            project_id: project,
                            id: member
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
           expect(project.users).to include team_user
         end
       end
@@ -139,7 +139,7 @@
         delete :leave, namespace_id: project.namespace,
                        project_id: project
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -171,7 +171,7 @@
           delete :leave, namespace_id: project.namespace,
                          project_id: project
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
@@ -224,7 +224,7 @@
                                       project_id: project,
                                       id: 42
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -247,7 +247,7 @@
                                         project_id: project,
                                         id: member
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
           expect(project.users).not_to include team_requester
         end
       end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 33c35161da3fbf3a8468af40291c77169ad4a033..48f799d8ca1f4cd1cb0579ace17e0d5a3ed437f3 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -13,7 +13,7 @@
             project_id: public_project.to_param,
             id: id)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
         expect(response.header['Content-Disposition']).
             to eq("inline")
@@ -30,7 +30,7 @@
             project_id: public_project.to_param,
             id: id)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(response.header['Content-Type']).to eq('image/jpeg')
         expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
       end
@@ -54,7 +54,7 @@
               project_id: public_project.to_param,
               id: id)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -65,7 +65,7 @@
               project_id: public_project.to_param,
               id: id)
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index aad62cf20e3e187e2c0a8656ff0edc1987c7b7b2..ee905d11fb2878ec631c81f96eaa7fe22ac7f5e5 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -36,7 +36,7 @@
         it "renders Not Found" do
           get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 0f32a30f18b874184b3f082b9699afa790755e0b..b8a28f4370716091c782dee31ea1692fcbf6349e 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -19,7 +19,7 @@
           get :index, namespace_id: project.namespace.path, project_id: project.path
 
           expect(assigns(:snippets)).not_to include(project_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -30,7 +30,7 @@
           get :index, namespace_id: project.namespace.path, project_id: project.path
 
           expect(assigns(:snippets)).to include(project_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -41,7 +41,7 @@
           get :index, namespace_id: project.namespace.path, project_id: project.path
 
           expect(assigns(:snippets)).to include(project_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
     end
@@ -56,7 +56,7 @@
           it 'responds with status 404' do
             get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
 
@@ -67,7 +67,7 @@
             get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param
 
             expect(assigns(:snippet)).to eq(project_snippet)
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -78,7 +78,7 @@
             get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param
 
             expect(assigns(:snippet)).to eq(project_snippet)
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -88,7 +88,7 @@
           it 'responds with status 404' do
             get action, namespace_id: project.namespace.path, project_id: project.path, id: 42
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
 
@@ -98,7 +98,7 @@
           it 'responds with status 404' do
             get action, namespace_id: project.namespace.path, project_id: project.path, id: 42
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
       end
diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb
index 40a3403b660594ab3061991cde6f888a37ea3fe9..5a8bba285942ce7215b694e8b9e76e0edacbe8ed 100644
--- a/spec/controllers/projects/todo_controller_spec.rb
+++ b/spec/controllers/projects/todo_controller_spec.rb
@@ -22,7 +22,7 @@
                           issuable_type: 'issue')
           end.to change { user.todos.count }.by(1)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -36,7 +36,7 @@
                           issuable_type: 'issue')
           end.to change { user.todos.count }.by(0)
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
 
         it 'should not create todo for issue when user not logged in' do
@@ -47,7 +47,7 @@
                           issuable_type: 'issue')
           end.to change { user.todos.count }.by(0)
 
-          expect(response.status).to eq(302)
+          expect(response).to have_http_status(302)
         end
       end
     end
@@ -69,7 +69,7 @@
                           issuable_type: 'merge_request')
           end.to change { user.todos.count }.by(1)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -83,7 +83,7 @@
                           issuable_type: 'merge_request')
           end.to change { user.todos.count }.by(0)
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
 
         it 'should not create todo for merge request user has no access to' do
@@ -94,7 +94,7 @@
                           issuable_type: 'merge_request')
           end.to change { user.todos.count }.by(0)
 
-          expect(response.status).to eq(302)
+          expect(response).to have_http_status(302)
         end
       end
     end
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index e74731c9ed82b07093aa3718306dbeac5ce9bebe..4e3a2bdb19e95e43c53a3f26d868d00415e47517 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -64,7 +64,7 @@
 
     context "valid SHA commit ID with path" do
       let(:id) { '6d39438/.gitignore' }
-      it { expect(response.status).to eq(302) }
+      it { expect(response).to have_http_status(302) }
     end
 
   end
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index 93c4494c6607ef084889c7b8faab31158d4bd433..0893ee89f6af5cdd084198aca9a0132bcd280c88 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -18,7 +18,7 @@
           namespace_id: project.namespace.to_param,
           project_id: project.to_param, 
           format: :json
-        expect(response.status).to eq(422)
+        expect(response).to have_http_status(422)
       end
     end
 
@@ -79,7 +79,7 @@
           it "responds with status 200" do
             go
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -87,7 +87,7 @@
           it "responds with status 404" do
             go
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
       end
@@ -106,7 +106,7 @@
           it "responds with status 200" do
             go
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -114,7 +114,7 @@
           it "responds with status 404" do
             go
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
       end
@@ -140,7 +140,7 @@
             it "responds with status 200" do
               go
 
-              expect(response.status).to eq(200)
+              expect(response).to have_http_status(200)
             end
           end
 
@@ -192,7 +192,7 @@
                 it "responds with status 200" do
                   go
 
-                  expect(response.status).to eq(200)
+                  expect(response).to have_http_status(200)
                 end
               end
 
@@ -224,7 +224,7 @@
               it "responds with status 200" do
                 go
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
             end
 
@@ -232,7 +232,7 @@
               it "responds with status 404" do
                 go
 
-                expect(response.status).to eq(404)
+                expect(response).to have_http_status(404)
               end
             end
           end
@@ -253,7 +253,7 @@
               it "responds with status 200" do
                 go
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
             end
 
@@ -261,7 +261,7 @@
               it "responds with status 404" do
                 go
 
-                expect(response.status).to eq(404)
+                expect(response).to have_http_status(404)
               end
             end
           end
@@ -270,7 +270,7 @@
             it "responds with status 404" do
               go
 
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
           end
         end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 146b2c2e131e822c43be6bb582a5da1a67f13381..d60579030c070659216546e1c13e71f3bcfe9c40 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -77,7 +77,7 @@
           get :show, namespace_id: public_project.namespace.path, id: public_project.path
 
           expect(assigns(:project)).to eq(public_project)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -101,7 +101,7 @@
               get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
 
               expect(assigns(:project)).to eq(other_project)
-              expect(response.status).to eq(200)
+              expect(response).to have_http_status(200)
             end
           end
         end
@@ -146,7 +146,7 @@
 
       expect(project.repository.path).to include(new_path)
       expect(assigns(:repository).path).to eq(project.repository.path)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -161,7 +161,7 @@
       delete :destroy, namespace_id: project.namespace.path, id: project.path
 
       expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound)
-      expect(response.status).to eq(302)
+      expect(response).to have_http_status(302)
       expect(response).to redirect_to(dashboard_projects_path)
     end
   end
@@ -234,7 +234,7 @@
       delete(:remove_fork,
           namespace_id: project.namespace.to_param,
           id: project.to_param, format: :js)
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
   end
 
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index b3dcb52c5003bcd79bc187805f0766c7e7ca4992..2a89159c0706d272f42ed43f8810f1a5f6f6334f 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -19,7 +19,7 @@
           it 'responds with status 404' do
             get :show, id: other_personal_snippet.to_param
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
 
@@ -28,7 +28,7 @@
             get :show, id: personal_snippet.to_param
 
             expect(assigns(:snippet)).to eq(personal_snippet)
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -54,7 +54,7 @@
           get :show, id: personal_snippet.to_param
 
           expect(assigns(:snippet)).to eq(personal_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -79,7 +79,7 @@
           get :show, id: personal_snippet.to_param
 
           expect(assigns(:snippet)).to eq(personal_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -88,7 +88,7 @@
           get :show, id: personal_snippet.to_param
 
           expect(assigns(:snippet)).to eq(personal_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
     end
@@ -102,7 +102,7 @@
         it 'responds with status 404' do
           get :show, id: 'doesntexist'
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
 
@@ -110,7 +110,7 @@
         it 'responds with status 404' do
           get :show, id: 'doesntexist'
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
@@ -134,7 +134,7 @@
           it 'responds with status 404' do
             get :raw, id: other_personal_snippet.to_param
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
 
@@ -143,7 +143,7 @@
             get :raw, id: personal_snippet.to_param
 
             expect(assigns(:snippet)).to eq(personal_snippet)
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -169,7 +169,7 @@
           get :raw, id: personal_snippet.to_param
 
           expect(assigns(:snippet)).to eq(personal_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -194,7 +194,7 @@
           get :raw, id: personal_snippet.to_param
 
           expect(assigns(:snippet)).to eq(personal_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -203,7 +203,7 @@
           get :raw, id: personal_snippet.to_param
 
           expect(assigns(:snippet)).to eq(personal_snippet)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
     end
@@ -217,7 +217,7 @@
         it 'responds with status 404' do
           get :raw, id: 'doesntexist'
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
 
@@ -225,7 +225,7 @@
         it 'responds with status 404' do
           get :raw, id: 'doesntexist'
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 73858e6f0637de5c7127798abe757a9e4c42e601..69124ab06bfac740b15aa86cd429a3a7cafc66b9 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -26,7 +26,7 @@
           it "responds with status 200" do
             get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -35,7 +35,7 @@
         it "responds with status 200" do
           get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
     end
@@ -52,7 +52,7 @@
           it "responds with status 200" do
             get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -64,7 +64,7 @@
           it "responds with status 200" do
             get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -109,7 +109,7 @@
               it "responds with status 200" do
                 get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
             end
           end
@@ -118,7 +118,7 @@
             it "responds with status 404" do
               get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
 
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
           end
         end
@@ -133,7 +133,7 @@
           it "responds with status 200" do
             get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -145,7 +145,7 @@
           it "responds with status 200" do
             get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -181,7 +181,7 @@
               it "responds with status 200" do
                 get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
             end
           end
@@ -190,7 +190,7 @@
             it "responds with status 404" do
               get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
 
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
           end
         end
@@ -210,7 +210,7 @@
           it "responds with status 200" do
             get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -222,7 +222,7 @@
           it "responds with status 200" do
             get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
       end
@@ -267,7 +267,7 @@
               it "responds with status 200" do
                 get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
             end
           end
@@ -276,7 +276,7 @@
             it "responds with status 404" do
               get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
 
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
           end
         end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index c61ec174665d2aaa7ea4ac4c90c6048641c5677a..8d6f486efddbe30607b7177a27f78e7706c29e5b 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -33,7 +33,7 @@
         it 'renders the show template' do
           get :show, username: user.username
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response).to render_template('show')
         end
       end
@@ -47,7 +47,7 @@
       context 'when logged out' do
         it 'renders 404' do
           get :show, username: user.username
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
 
@@ -56,7 +56,7 @@
 
         it 'renders show' do
           get :show, username: user.username
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response).to render_template('show')
         end
       end
@@ -121,7 +121,7 @@
     context 'format html' do
       it 'renders snippets page' do
         get :snippets, username: user.username
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(response).to render_template('show')
       end
     end
@@ -129,7 +129,7 @@
     context 'format json' do
       it 'response with snippets json data' do
         get :snippets, username: user.username, format: :json
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(JSON.parse(response.body)).to have_key('html')
       end
     end
diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dbc1d829b674a8b45e15e160db3f3d54f063c2a4
--- /dev/null
+++ b/spec/features/admin/admin_system_info_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe 'Admin System Info' do
+  before do
+    login_as :admin
+  end
+
+  describe 'GET /admin/system_info' do
+    it 'shows system info page' do
+      visit admin_system_info_path
+
+      expect(page).to have_content 'CPU'
+      expect(page).to have_content 'Memory'
+      expect(page).to have_content 'Disk'
+    end
+  end
+end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index de6aed74fb41df5850beafff15f81a31e23640e8..91704377a07a47c708fa95c5c7626019be94278f 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -61,7 +61,7 @@
       end
 
       it 'should have XHTML summaries in merge request descriptions' do
-        expect(body).to match /Here is the fix: <a[^>]*><img[^>]*\/><\/a>/
+        expect(body).to match /Here is the fix: <\/p><div[^>]*><a[^>]*><img[^>]*\/><\/a><\/div>/
       end
     end
   end
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
index 1ea607cbca065b1178ddfc704a20a4243a1925be..4944301c938c61dfdbcb93535488c3451222c831 100644
--- a/spec/features/groups/members/user_requests_access_spec.rb
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -4,6 +4,7 @@
   let(:user) { create(:user) }
   let(:owner) { create(:user) }
   let(:group) { create(:group, :public) }
+  let!(:project) { create(:project, :private, namespace: group) }
 
   background do
     group.add_owner(owner)
@@ -24,6 +25,20 @@
     expect(page).not_to have_content 'Leave Group'
   end
 
+  scenario 'user does not see private projects' do
+    perform_enqueued_jobs { click_link 'Request Access' }
+
+    expect(page).not_to have_content project.name
+  end
+
+  scenario 'user does not see group in the Dashboard > Groups page' do
+    perform_enqueued_jobs { click_link 'Request Access' }
+
+    visit dashboard_groups_path
+
+    expect(page).not_to have_content group.name
+  end
+
   scenario 'user is not listed in the group members page' do
     click_link 'Request Access'
 
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 7143d0e40f38b4c6d15d5fefb22eb871c805d3ac..afc093cc1f5626c8d943b82b0e84451f2131fbcd 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -10,7 +10,7 @@
   let!(:bug)      { create(:label, project: project, title: 'bug') }
   let!(:feature)  { create(:label, project: project, title: 'feature') }
 
-  context 'as a allowed user', js: true do
+  context 'as an allowed user', js: true do
     before do
       project.team << [user, :master]
 
@@ -164,6 +164,133 @@
         end
       end
     end
+
+    context 'toggling a milestone' do
+      let!(:milestone) { create(:milestone, project: project, title: 'First Release') }
+
+      context 'setting a milestone' do
+        before do
+          issue1.labels << bug
+          issue2.labels << feature
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'labels are kept' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+
+          check 'check_all_issues'
+          open_milestone_dropdown(['First Release'])
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+        end
+      end
+
+      context 'setting a milestone and adding another label' do
+        before do
+          issue1.labels << bug
+
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'existing label is kept and new label is present' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+
+          check 'check_all_issues'
+          open_milestone_dropdown ['First Release']
+          open_labels_dropdown ['feature']
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+        end
+      end
+
+      context 'setting a milestone and removing existing label' do
+        before do
+          issue1.labels << bug
+          issue1.labels << feature
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'existing label is kept and new label is present' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+
+          check 'check_all_issues'
+          open_milestone_dropdown ['First Release']
+          unmark_labels_in_dropdown ['feature']
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'feature'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+        end
+      end
+
+      context 'unsetting a milestone' do
+        before do
+          issue1.milestone = milestone
+          issue2.milestone = milestone
+          issue1.save
+          issue2.save
+          issue1.labels << bug
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+        end
+
+        it 'labels are kept' do
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).to have_content 'First Release'
+
+          check 'check_all_issues'
+          open_milestone_dropdown(['No Milestone'])
+          update_issues
+
+          expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'First Release'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'First Release'
+        end
+      end
+    end
+
+    context 'toggling checked issues' do
+      before do
+        issue1.labels << bug
+
+        visit namespace_project_issues_path(project.namespace, project)
+      end
+
+      it do
+        expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+
+        check_issue issue1
+        open_labels_dropdown ['feature']
+        uncheck_issue issue1
+        check_issue issue1
+        update_issues
+        sleep 1 # needed
+
+        expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+        expect(find("#issue_#{issue1.id}")).not_to have_content 'feature'
+      end
+    end
   end
 
   context 'as a guest' do
@@ -181,6 +308,16 @@
     end
   end
 
+  def open_milestone_dropdown(items = [])
+    page.within('.issues_bulk_update') do
+      click_button 'Milestone'
+      wait_for_ajax
+      items.map do |item|
+        click_link item
+      end
+    end
+  end
+
   def open_labels_dropdown(items = [], unmark = false)
     page.within('.issues_bulk_update') do
       click_button 'Label'
@@ -201,12 +338,20 @@ def unmark_labels_in_dropdown(items = [])
     open_labels_dropdown(items, true)
   end
 
-  def check_issue(issue)
+  def check_issue(issue, uncheck = false)
     page.within('.issues-list') do
-      check "selected_issue_#{issue.id}"
+      if uncheck
+        uncheck "selected_issue_#{issue.id}"
+      else
+        check "selected_issue_#{issue.id}"
+      end
     end
   end
 
+  def uncheck_issue(issue)
+    check_issue(issue, true)
+  end
+
   def update_issues
     click_button 'Update issues'
     wait_for_ajax
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index 073a83b68964315ce1df32ade55a42cfe4db5717..9ebef505b92e3f86fe19bd8dd9efc3c3f5b8c205 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -24,6 +24,7 @@
     end
     wait_for_ajax
 
+    expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Rails')
     expect(page).to have_content('/.bundle')
     expect(page).to have_content('# Gemfile.lock, .ruby-version, .ruby-gemset')
   end
diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
index d516e8ce55a5ce648877b8bd52e627b472281c63..b8c06c383fbcdd9737de7358cf486de758487b2e 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -24,6 +24,7 @@
     end
     wait_for_ajax
 
+    expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'jekyll')
     expect(page).to have_content('This file is a template, and might need editing before it works on your project')
     expect(page).to have_content('jekyll build -d test')
   end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 8625ea6bc10a79ba7ff83499de242b6e253076df..13d980a326f512f9656c16e6a1dc3e0e808b9e2f 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -288,4 +288,142 @@
     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 }
+  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 }
+  end
+
+  describe "GET /:project_path/builds" do
+    subject { namespace_project_builds_path(project.namespace, project) }
+
+    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 }
+    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 }
+    end
+  end
+
+  describe "GET /:project_path/builds/:id" do
+    let(:pipeline) { create(:ci_pipeline, project: project) }
+    let(:build) { create(:ci_build, pipeline: pipeline) }
+    subject { namespace_project_build_path(project.namespace, project, build.id) }
+
+    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 }
+    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 }
+    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 }
+  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 }
+  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 }
+  end
 end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index 544270b40376ed3a0f13eb02472a0bfd8bd85ae0..ac9690cc12796f7001a98cfe7efd2df49229018f 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -260,4 +260,106 @@
     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 }
+  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 }
+  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 }
+  end
+
+  describe "GET /:project_path/builds/:id" do
+    let(:pipeline) { create(:ci_pipeline, project: project) }
+    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 }
+  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 }
+  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 }
+  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 }
+  end
 end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index f6c6687e162a26e41f2736b1f11dc7a7ce5597ea..737897de52b694b006a7043b5053be28206569ec 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -109,6 +109,35 @@
     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 }
+  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 }
+  end
+
   describe "GET /:project_path/builds" do
     subject { namespace_project_builds_path(project.namespace, project) }
 
@@ -191,7 +220,7 @@
 
   describe "GET /:project_path/environments/:id" do
     let(:environment) { create(:environment, project: project) }
-    subject { namespace_project_environments_path(project.namespace, project, environment) }
+    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 }
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index ff98249570d1074349c4c4d632bcdf55db7ce4b1..5e7594170c5240c2ad859d83c34ab0bc22d8b6d3 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -1,11 +1,6 @@
 require 'spec_helper'
 
 describe VisibilityLevelHelper do
-  include Haml::Helpers
-
-  before :all do
-    init_haml_helpers
-  end
 
   let(:project)          { build(:project) }
   let(:group)            { build(:group) }
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
index e58f2c80e957dc0dbef9861f9992da0a1d455881..1bcae8a27dbfd851e283e088f7a3a8ad4b47b66c 100644
--- a/spec/initializers/settings_spec.rb
+++ b/spec/initializers/settings_spec.rb
@@ -1,3 +1,4 @@
+require 'spec_helper'
 require_relative '../../config/initializers/1_settings'
 
 describe Settings, lib: true do
diff --git a/spec/javascripts/application_spec.js.coffee b/spec/javascripts/application_spec.js.coffee
index 8af39c41f2f5a93d718050ad83e38973c37dad89..4b6a2bb544073f19e1250533ff7a504d3579568c 100644
--- a/spec/javascripts/application_spec.js.coffee
+++ b/spec/javascripts/application_spec.js.coffee
@@ -1,4 +1,4 @@
-#= require lib/common_utils
+#= require lib/utils/common_utils
 
 describe 'Application', ->
   describe 'disable buttons', ->
diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee
index ba191199dc7176102ce472cffbbb9496535d96a1..d7f9c6fc076fc828b38b2c951c1b84be23d902af 100644
--- a/spec/javascripts/awards_handler_spec.js.coffee
+++ b/spec/javascripts/awards_handler_spec.js.coffee
@@ -160,7 +160,6 @@ describe 'AwardsHandler', ->
       expect($('[data-emoji=angel]').is(':visible')).toBe no
       expect($('[data-emoji=anger]').is(':visible')).toBe no
       expect($('[data-emoji=alien]').is(':visible')).toBe yes
-      expect($('h5.emoji-search').is(':visible')).toBe yes
 
 
   describe 'emoji menu', ->
diff --git a/spec/javascripts/fixtures/emoji_menu.coffee b/spec/javascripts/fixtures/emoji_menu.coffee
index e529dd5f1cd2d6b9df8ca4cc360c9dd214991cf4..ce1a41390d20a0fbda5967712c2c176dec1794b2 100644
--- a/spec/javascripts/fixtures/emoji_menu.coffee
+++ b/spec/javascripts/fixtures/emoji_menu.coffee
@@ -1,7 +1,7 @@
 window.emojiMenu = """
   <div class='emoji-menu'>
+    <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" />
     <div class='emoji-menu-content'>
-      <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" />
       <h5 class='emoji-menu-title'>
       Emoticons
       </h5>
diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee
index 71f0c1076c5fceddd7c9e9afa3ad0e0196624c39..d84d80f266bc950a7f203b8d73f440601391c942 100644
--- a/spec/javascripts/issue_spec.js.coffee
+++ b/spec/javascripts/issue_spec.js.coffee
@@ -1,4 +1,4 @@
-#= require lib/text_utility
+#= require lib/utils/text_utility
 #= require issue
 
 describe 'Issue', ->
diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee
index 9be29097f4c6ee3af480d2b54804ada3710d30c1..f0d26fb54462e686b6fa143093265697d19d5fde 100644
--- a/spec/javascripts/project_title_spec.js.coffee
+++ b/spec/javascripts/project_title_spec.js.coffee
@@ -1,6 +1,6 @@
 #= require bootstrap
 #= require select2
-#= require lib/type_utility
+#= require lib/utils/type_utility
 #= require gl_dropdown
 #= require api
 #= require project_select
diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee
index e77177783a7656019d2902e2dab2d0c4163ba3a9..1c1faca3333334f19a0b01ec35c2faeb95aaf6f7 100644
--- a/spec/javascripts/search_autocomplete_spec.js.coffee
+++ b/spec/javascripts/search_autocomplete_spec.js.coffee
@@ -1,8 +1,8 @@
 #= require gl_dropdown
 #= require search_autocomplete
 #= require jquery
-#= require lib/common_utils
-#= require lib/type_utility
+#= require lib/utils/common_utils
+#= require lib/utils/type_utility
 #= require fuzzaldrin-plus
 
 
diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb
index dd5594750c800e27350ef0e8eeb9d910a0b35d25..a2a1ed58d1bda4bfaf54e2ac5da96c80c422b528 100644
--- a/spec/lib/banzai/filter/image_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_link_filter_spec.rb
@@ -21,4 +21,9 @@ def image(path)
     doc = filter(image('https://i.imgur.com/DfssX9C.jpg'))
     expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href']
   end
+
+  it 'wraps the image with a link and a div' do
+    doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+    expect(doc.to_html).to include('<div class="image-container">')
+  end
 end
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 5b63c946114a407a12a4522cfdfb6b86f2905a60..8d6ce114aa90be3ee4daf3b652e8dbe727d2f255 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -198,4 +198,40 @@ def helper
       expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
     end
   end
+
+  describe '#issues_per_Project' do
+    context 'using an internal issue tracker' do
+      it 'returns a Hash containing the issues per project' do
+        doc = Nokogiri::HTML.fragment('')
+        filter = described_class.new(doc, project: project)
+
+        expect(filter).to receive(:projects_per_reference).
+          and_return({ project.path_with_namespace => project })
+
+        expect(filter).to receive(:references_per_project).
+          and_return({ project.path_with_namespace => Set.new([issue.iid]) })
+
+        expect(filter.issues_per_project).
+          to eq({ project => { issue.iid => issue } })
+      end
+    end
+
+    context 'using an external issue tracker' do
+      it 'returns a Hash containing the issues per project' do
+        doc = Nokogiri::HTML.fragment('')
+        filter = described_class.new(doc, project: project)
+
+        expect(project).to receive(:default_issues_tracker?).and_return(false)
+
+        expect(filter).to receive(:projects_per_reference).
+          and_return({ project.path_with_namespace => project })
+
+        expect(filter).to receive(:references_per_project).
+          and_return({ project.path_with_namespace => Set.new([1]) })
+
+        expect(filter.issues_per_project[project][1]).
+          to be_an_instance_of(ExternalIssue)
+      end
+    end
+  end
 end
diff --git a/spec/lib/banzai/note_renderer_spec.rb b/spec/lib/banzai/note_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..98f76f36fd50349fe383e1142e7104912c265a1c
--- /dev/null
+++ b/spec/lib/banzai/note_renderer_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Banzai::NoteRenderer do
+  describe '.render' do
+    it 'renders a Note' do
+      note = double(:note)
+      project = double(:project)
+      wiki = double(:wiki)
+      user = double(:user)
+
+      expect(Banzai::ObjectRenderer).to receive(:new).
+        with(project, user,
+             requested_path: 'foo',
+             project_wiki: wiki,
+             ref: 'bar',
+             pipeline: :note).
+        and_call_original
+
+      expect_any_instance_of(Banzai::ObjectRenderer).
+        to receive(:render).with([note], :note)
+
+      described_class.render([note], project, user, 'foo', wiki, 'bar')
+    end
+  end
+end
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..44256b32bdc7768b58a475fb227f8821314f4c1d
--- /dev/null
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe Banzai::ObjectRenderer do
+  let(:project) { create(:empty_project) }
+  let(:user) { project.owner }
+
+  describe '#render' do
+    it 'renders and redacts an Array of objects' do
+      renderer = described_class.new(project, user)
+      object = double(:object, note: 'hello', note_html: nil)
+
+      expect(renderer).to receive(:render_objects).with([object], :note).
+        and_call_original
+
+      expect(renderer).to receive(:redact_documents).
+        with(an_instance_of(Array)).
+        and_call_original
+
+      expect(object).to receive(:note_html=).with('<p>hello</p>')
+
+      renderer.render([object], :note)
+    end
+  end
+
+  describe '#render_objects' do
+    it 'renders an Array of objects' do
+      object = double(:object, note: 'hello')
+      renderer = described_class.new(project, user)
+
+      expect(renderer).to receive(:render_attribute).with(object, :note).
+        and_call_original
+
+      rendered = renderer.render_objects([object], :note)
+
+      expect(rendered).to be_an_instance_of(Array)
+      expect(rendered[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
+    end
+  end
+
+  describe '#redact_documents' do
+    it 'redacts a set of documents and returns them as an Array of Strings' do
+      doc = Nokogiri::HTML.fragment('<p>hello</p>')
+      renderer = described_class.new(project, user)
+
+      expect_any_instance_of(Banzai::Redactor).to receive(:redact).
+        with([doc]).
+        and_call_original
+
+      redacted = renderer.redact_documents([doc])
+
+      expect(redacted).to eq(['<p>hello</p>'])
+    end
+  end
+
+  describe '#context_for' do
+    let(:object) { double(:object, note: 'hello') }
+    let(:renderer) { described_class.new(project, user) }
+
+    it 'returns a Hash' do
+      expect(renderer.context_for(object, :note)).to be_an_instance_of(Hash)
+    end
+
+    it 'includes the cache key' do
+      context = renderer.context_for(object, :note)
+
+      expect(context[:cache_key]).to eq([object, :note])
+    end
+
+    context 'when the object responds to "author"' do
+      it 'includes the author in the context' do
+        expect(object).to receive(:author).and_return('Alice')
+
+        context = renderer.context_for(object, :note)
+
+        expect(context[:author]).to eq('Alice')
+      end
+    end
+
+    context 'when the object does not respond to "author"' do
+      it 'does not include the author in the context' do
+        context = renderer.context_for(object, :note)
+
+        expect(context.key?(:author)).to eq(false)
+      end
+    end
+  end
+
+  describe '#render_attribute' do
+    it 'renders the attribute of an object' do
+      object = double(:doc, note: 'hello')
+      renderer = described_class.new(project, user, pipeline: :note)
+      doc = renderer.render_attribute(object, :note)
+
+      expect(doc).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
+      expect(doc.to_html).to eq('<p>hello</p>')
+    end
+  end
+
+  describe '#base_context' do
+    let(:context) do
+      described_class.new(project, user, pipeline: :note).base_context
+    end
+
+    it 'returns a Hash' do
+      expect(context).to be_an_instance_of(Hash)
+    end
+
+    it 'includes the custom attributes' do
+      expect(context[:pipeline]).to eq(:note)
+    end
+
+    it 'includes the current user' do
+      expect(context[:current_user]).to eq(user)
+    end
+
+    it 'includes the current project' do
+      expect(context[:project]).to eq(project)
+    end
+  end
+end
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..488f465bcdaf181f3257bcafdbfd4a2a9ad3416f
--- /dev/null
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe Banzai::Redactor do
+  let(:user) { build(:user) }
+  let(:project) { build(:empty_project) }
+  let(:redactor) { described_class.new(project, user) }
+
+  describe '#redact' do
+    it 'redacts an Array of documents' do
+      doc1 = Nokogiri::HTML.
+        fragment('<a class="gfm" data-reference-type="issue">foo</a>')
+
+      doc2 = Nokogiri::HTML.
+        fragment('<a class="gfm" data-reference-type="issue">bar</a>')
+
+      expect(redactor).to receive(:nodes_visible_to_user).and_return([])
+
+      expect(redactor.redact([doc1, doc2])).to eq([doc1, doc2])
+
+      expect(doc1.to_html).to eq('foo')
+      expect(doc2.to_html).to eq('bar')
+    end
+  end
+
+  describe '#redact_nodes' do
+    it 'redacts an Array of nodes' do
+      doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
+      node = doc.children[0]
+
+      expect(redactor).to receive(:nodes_visible_to_user).
+        with([node]).
+        and_return(Set.new)
+
+      redactor.redact_nodes([node])
+
+      expect(doc.to_html).to eq('foo')
+    end
+  end
+
+  describe '#nodes_visible_to_user' do
+    it 'returns a Set containing the visible nodes' do
+      doc = Nokogiri::HTML.fragment('<a data-reference-type="issue"></a>')
+      node = doc.children[0]
+
+      expect_any_instance_of(Banzai::ReferenceParser::IssueParser).
+        to receive(:nodes_visible_to_user).
+        with(user, [node]).
+        and_return([node])
+
+      expect(redactor.nodes_visible_to_user([node])).to eq(Set.new([node]))
+    end
+  end
+end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index d562d8b25ea4f8ed00a13e9ce866cab2afaae142..2ca9f554b07d5dd21298a4fbfcb026412f90e4ea 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -951,7 +951,7 @@ module Ci
         config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } })
         expect do
           GitlabCiYamlProcessor.new(config, path)
-        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings")
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Before script config should be an array of strings")
       end
 
       it "returns errors if job before_script parameter is not an array of strings" do
@@ -1206,5 +1206,17 @@ module Ci
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: dependencies parameter should be an array of strings")
       end
     end
+
+    describe "Validate configuration templates" do
+      templates = Dir.glob("#{Rails.root.join('vendor/gitlab-ci-yml')}/**/*.gitlab-ci.yml")
+
+      templates.each do |file|
+        it "does not return errors for #{file}" do
+          file = File.read(file)
+
+          expect { GitlabCiYamlProcessor.new(file) }.not_to raise_error
+        end
+      end
+    end
   end
 end
diff --git a/spec/lib/gitlab/ci/config/node/configurable_spec.rb b/spec/lib/gitlab/ci/config/node/configurable_spec.rb
index 47c68f96dc8ab603743f67ee38aa0e0b8dff4742..9bbda6e73967ba358f44808a5cf1bca7784210f5 100644
--- a/spec/lib/gitlab/ci/config/node/configurable_spec.rb
+++ b/spec/lib/gitlab/ci/config/node/configurable_spec.rb
@@ -7,26 +7,26 @@
     node.include(described_class)
   end
 
-  describe 'allowed nodes' do
+  describe 'configured nodes' do
     before do
       node.class_eval do
         allow_node :object, Object, description: 'test object'
       end
     end
 
-    describe '#allowed_nodes' do
-      it 'has valid allowed nodes' do
-        expect(node.allowed_nodes).to include :object
+    describe '.nodes' do
+      it 'has valid nodes' do
+        expect(node.nodes).to include :object
       end
 
       it 'creates a node factory' do
-        expect(node.allowed_nodes[:object])
+        expect(node.nodes[:object])
           .to be_an_instance_of Gitlab::Ci::Config::Node::Factory
       end
 
       it 'returns a duplicated factory object' do
-        first_factory = node.allowed_nodes[:object]
-        second_factory = node.allowed_nodes[:object]
+        first_factory = node.nodes[:object]
+        second_factory = node.nodes[:object]
 
         expect(first_factory).not_to be_equal(second_factory)
       end
diff --git a/spec/lib/gitlab/ci/config/node/factory_spec.rb b/spec/lib/gitlab/ci/config/node/factory_spec.rb
index d681aa32456fbf43a1f8ebb84876525260a8cc1d..01a707a6bd49846bd865ada732d6b02c294cd33f 100644
--- a/spec/lib/gitlab/ci/config/node/factory_spec.rb
+++ b/spec/lib/gitlab/ci/config/node/factory_spec.rb
@@ -25,6 +25,16 @@
           expect(entry.description).to eq 'test description'
         end
       end
+
+      context 'when setting key' do
+        it 'creates entry with custom key' do
+          entry = factory
+            .with(value: ['ls', 'pwd'], key: 'test key')
+            .create!
+
+          expect(entry.key).to eq 'test key'
+        end
+      end
     end
 
     context 'when not setting value' do
diff --git a/spec/lib/gitlab/ci/config/node/global_spec.rb b/spec/lib/gitlab/ci/config/node/global_spec.rb
index b1972172435a29f0950d665f63138860438a2b7b..fddd53a2b57508c713f14072eb21611f555bc96e 100644
--- a/spec/lib/gitlab/ci/config/node/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/node/global_spec.rb
@@ -3,13 +3,19 @@
 describe Gitlab::Ci::Config::Node::Global do
   let(:global) { described_class.new(hash) }
 
-  describe '#allowed_nodes' do
+  describe '.nodes' do
     it 'can contain global config keys' do
-      expect(global.allowed_nodes).to include :before_script
+      expect(described_class.nodes).to include :before_script
     end
 
     it 'returns a hash' do
-      expect(global.allowed_nodes).to be_a Hash
+      expect(described_class.nodes).to be_a Hash
+    end
+  end
+
+  describe '#key' do
+    it 'returns underscored class name' do
+      expect(global.key).to eq 'global'
     end
   end
 
@@ -79,7 +85,7 @@
     describe '#errors' do
       it 'reports errors from child nodes' do
         expect(global.errors)
-          .to include 'before_script should be an array of strings'
+          .to include 'Before script config should be an array of strings'
       end
     end
 
diff --git a/spec/lib/gitlab/ci/config/node/script_spec.rb b/spec/lib/gitlab/ci/config/node/script_spec.rb
index e4d6481f8a5711bacfe6705aa082d50d7b369c1d..6af6aa15eef2dbe618bc20bdda64b9cfb3531d1b 100644
--- a/spec/lib/gitlab/ci/config/node/script_spec.rb
+++ b/spec/lib/gitlab/ci/config/node/script_spec.rb
@@ -1,13 +1,13 @@
 require 'spec_helper'
 
 describe Gitlab::Ci::Config::Node::Script do
-  let(:entry) { described_class.new(value) }
+  let(:entry) { described_class.new(config) }
 
-  describe '#validate!' do
-    before { entry.validate! }
+  describe '#process!' do
+    before { entry.process! }
 
-    context 'when entry value is correct' do
-      let(:value) { ['ls', 'pwd'] }
+    context 'when entry config value is correct' do
+      let(:config) { ['ls', 'pwd'] }
 
       describe '#value' do
         it 'returns concatenated command' do
@@ -29,12 +29,12 @@
     end
 
     context 'when entry value is not correct' do
-      let(:value) { 'ls' }
+      let(:config) { 'ls' }
 
       describe '#errors' do
         it 'saves errors' do
           expect(entry.errors)
-            .to include /should be an array of strings/
+            .to include 'Script config should be an array of strings'
         end
       end
 
diff --git a/spec/lib/gitlab/ci/config/node/validatable_spec.rb b/spec/lib/gitlab/ci/config/node/validatable_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..10cd01afcd1da4e16314af3ab8a0ca7f34a493ff
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/validatable_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Validatable do
+  let(:node) { Class.new }
+
+  before do
+    node.include(described_class)
+  end
+
+  describe '.validator' do
+    before do
+      node.class_eval do
+        attr_accessor :test_attribute
+
+        validations do
+          validates :test_attribute, presence: true
+        end
+      end
+    end
+
+    it 'returns validator' do
+      expect(node.validator.superclass)
+        .to be Gitlab::Ci::Config::Node::Validator
+    end
+
+    context 'when validating node instance' do
+      let(:node_instance) { node.new }
+
+      context 'when attribute is valid' do
+        before do
+          node_instance.test_attribute = 'valid'
+        end
+
+        it 'instance of validator is valid' do
+          expect(node.validator.new(node_instance)).to be_valid
+        end
+      end
+
+      context 'when attribute is not valid' do
+        before do
+          node_instance.test_attribute = nil
+        end
+
+        it 'instance of validator is invalid' do
+          expect(node.validator.new(node_instance)).to be_invalid
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/node/validator_spec.rb b/spec/lib/gitlab/ci/config/node/validator_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ad875d553840bf6b0b0797e8d2c645fd8ae35edd
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/validator_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Validator do
+  let(:validator) { Class.new(described_class) }
+  let(:validator_instance) { validator.new(node) }
+  let(:node) { spy('node') }
+
+  shared_examples 'delegated validator' do
+    context 'when node is valid' do
+      before do
+        allow(node).to receive(:test_attribute).and_return('valid value')
+      end
+
+      it 'validates attribute in node' do
+        expect(node).to receive(:test_attribute)
+        expect(validator_instance).to be_valid
+      end
+
+      it 'returns no errors' do
+        validator_instance.validate
+
+        expect(validator_instance.full_errors).to be_empty
+      end
+    end
+
+    context 'when node is invalid' do
+      before do
+        allow(node).to receive(:test_attribute).and_return(nil)
+      end
+
+      it 'validates attribute in node' do
+        expect(node).to receive(:test_attribute)
+        expect(validator_instance).to be_invalid
+      end
+
+      it 'returns errors' do
+        validator_instance.validate
+
+        expect(validator_instance.full_errors).not_to be_empty
+      end
+    end
+  end
+
+  describe 'attributes validations' do
+    before do
+      validator.class_eval do
+        validates :test_attribute, presence: true
+      end
+    end
+
+    it_behaves_like 'delegated validator'
+  end
+
+  describe 'interface validations' do
+    before do
+      validator.class_eval do
+        validate do
+          unless @node.test_attribute == 'valid value'
+            errors.add(:test_attribute, 'invalid value')
+          end
+        end
+      end
+    end
+
+    it_behaves_like 'delegated validator'
+  end
+end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 3871d939feb62c16ebfaef7d32c06aef82524a2a..2a5d132db7bb6f218df734d21f3227e1d7e84afd 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -67,6 +67,12 @@
             expect(config.errors).not_to be_empty
           end
         end
+
+        describe '#errors' do
+          it 'returns an array of strings' do
+            expect(config.errors).to all(be_an_instance_of(String))
+          end
+        end
       end
     end
   end
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index 1620eb6c60adb507920d56cd7cbf47b8828961a5..364532e94e30956eb37967dee45f6b85cdacc545 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -4,6 +4,7 @@
   include RepoHelpers
 
   let(:project) { create(:project) }
+  let(:repository) { project.repository }
   let(:commit) { project.commit(sample_commit.id) }
 
   describe '.highlight_lines' do
@@ -18,4 +19,30 @@
     end
   end
 
+  describe 'custom highlighting from .gitattributes' do
+    let(:branch) { 'gitattributes' }
+    let(:blob) { repository.blob_at_branch(branch, path) }
+
+    let(:highlighter) do
+      Gitlab::Highlight.new(blob.path, blob.data, repository: repository)
+    end
+
+    before { project.change_head('gitattributes') }
+
+    describe 'basic language selection' do
+      let(:path) { 'custom-highlighting/test.gitlab-custom' }
+      it 'highlights as ruby' do
+        expect(highlighter.lexer.tag).to eq 'ruby'
+      end
+    end
+
+    describe 'cgi options' do
+      let(:path) { 'custom-highlighting/test.gitlab-cgi' }
+
+      it 'highlights as json with erb' do
+        expect(highlighter.lexer.tag).to eq 'erb'
+        expect(highlighter.lexer.parent.tag).to eq 'json'
+      end
+    end
+  end
 end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 400d44ac162c81adc7ec0cb94811da123da7db31..403bd582ef302ac05db0f2837f25986e9538276d 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -4894,6 +4894,29 @@
       "started_at": null,
       "finished_at": null,
       "duration": null,
+      "notes": [
+        {
+          "id": 999,
+          "note": "Natus rerum qui dolorem dolorum voluptas.",
+          "noteable_type": "Commit",
+          "author_id": 1,
+          "created_at": "2016-03-22T15:19:59.469Z",
+          "updated_at": "2016-03-22T15:19:59.469Z",
+          "project_id": 5,
+          "attachment": {
+            "url": null
+          },
+          "line_code": null,
+          "commit_id": "be93687618e4b132087f430a4d8fc3a609c9b77c",
+          "noteable_id": 36,
+          "system": false,
+          "st_diff": null,
+          "updated_by_id": null,
+          "author": {
+            "name": "Administrator"
+          }
+        }
+      ],
       "statuses": [
         {
           "id": 71,
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 7a40a43f8ae5300c9ae712190efe146aead7da93..23036ab8108c1e378c5363b753da8c8c6b3154f9 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -18,6 +18,12 @@
       it 'restores models based on JSON' do
         expect(restored_project_json).to be true
       end
+
+      it 'creates a valid pipeline note' do
+        restored_project_json
+
+        expect(Ci::Pipeline.first.notes).not_to be_empty
+      end
     end
   end
 end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index e520a9689999a8c297950d94e1011cc4b87a3bae..4d2aa03e722cbac5d5d2c8eef9cc39980ccf28a1 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -2,6 +2,7 @@
 
 describe Gitlab::Metrics::SidekiqMiddleware do
   let(:middleware) { described_class.new }
+  let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } }
 
   describe '#call' do
     it 'tracks the transaction' do
@@ -11,9 +12,23 @@
         with('TestWorker#perform').
         and_call_original
 
+      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float))
       expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
 
-      middleware.call(worker, 'test', :test) { nil }
+      middleware.call(worker, message, :test) { nil }
+    end
+
+    it 'tracks the transaction (for messages without `enqueued_at`)' do
+      worker = double(:worker, class: double(:class, name: 'TestWorker'))
+
+      expect(Gitlab::Metrics::Transaction).to receive(:new).
+        with('TestWorker#perform').
+        and_call_original
+
+      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float))
+      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
+
+      middleware.call(worker, {}, :test) { nil }
     end
   end
 end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index d6ae54e25e859830a534a11ed47eea2dc8f23f32..cf0e282c2fb17391368e6f0ebcbd3999ce8e277a 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -28,8 +28,20 @@
   end
 
   describe '.cpu_time' do
-    it 'returns a Fixnum' do
-      expect(described_class.cpu_time).to be_an_instance_of(Fixnum)
+    it 'returns a Float' do
+      expect(described_class.cpu_time).to be_an_instance_of(Float)
+    end
+  end
+
+  describe '.real_time' do
+    it 'returns a Float' do
+      expect(described_class.real_time).to be_an_instance_of(Float)
+    end
+  end
+
+  describe '.monotonic_time' do
+    it 'returns a Float' do
+      expect(described_class.monotonic_time).to be_an_instance_of(Float)
     end
   end
 end
diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb
index 84c21ceefd978766c07bb2c5963d14ab735a9e26..2753aecc1f48c7484a722cc75aca9a982e5ab245 100644
--- a/spec/lib/gitlab/saml/user_spec.rb
+++ b/spec/lib/gitlab/saml/user_spec.rb
@@ -164,7 +164,14 @@ def stub_saml_group_config(groups)
             end
 
             context 'and LDAP user has an account already' do
-              let!(:existing_user) { create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
+              before do
+                create(:omniauth_user,
+                       email: 'john@mail.com',
+                       extern_uid: 'uid=user1,ou=People,dc=example',
+                       provider: 'ldapmain',
+                       username: 'john')
+              end
+
               it 'adds the omniauth identity to the LDAP account' do
                 saml_user.save
 
@@ -177,6 +184,15 @@ def stub_saml_group_config(groups)
                                                             { provider: 'saml', extern_uid: uid }
                                                           ])
               end
+
+              it 'saves successfully on subsequent tries, when both identities are present' do
+                saml_user.save
+                local_saml_user = described_class.new(auth_hash)
+                local_saml_user.save
+
+                expect(local_saml_user.gl_user).to be_valid
+                expect(local_saml_user.gl_user).to be_persisted
+              end
             end
 
             context 'user has SAML user, and wants to add their LDAP identity' do
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index efbcbf72f76d3c264e837b484da5974b98e7aa3f..89730ab8eb8179b4ed89d206e9ed542d6a7ed541 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -154,6 +154,20 @@
         expect(issues).to match_array([issue1, issue2, issue, issue3])
       end
     end
+
+    context 'when all of the results are level on the sort key' do
+      let!(:issues) do
+        10.times { create(:issue, project: project) }
+      end
+
+      it 'has no duplicates across pages' do
+        sorted_issue_ids = 1.upto(10).map do |i|
+          project.issues.sort('milestone_due_desc').page(i).per(1).first.id
+        end
+
+        expect(sorted_issue_ids).to eq(sorted_issue_ids.uniq)
+      end
+    end
   end
 
 
diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a6d9717ccb52038af69880c59c1b4787cef9e834
--- /dev/null
+++ b/spec/models/project_services/bugzilla_service_spec.rb
@@ -0,0 +1,49 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id                    :integer          not null, primary key
+#  type                  :string(255)
+#  title                 :string(255)
+#  project_id            :integer
+#  created_at            :datetime
+#  updated_at            :datetime
+#  active                :boolean          default(FALSE), not null
+#  properties            :text
+#  template              :boolean          default(FALSE)
+#  push_events           :boolean          default(TRUE)
+#  issues_events         :boolean          default(TRUE)
+#  merge_requests_events :boolean          default(TRUE)
+#  tag_push_events       :boolean          default(TRUE)
+#  note_events           :boolean          default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe BugzillaService, models: true do
+  describe 'Associations' do
+    it { is_expected.to belong_to :project }
+    it { is_expected.to have_one :service_hook }
+  end
+
+  describe 'Validations' do
+    context 'when service is active' do
+      before { subject.active = true }
+
+      it { is_expected.to validate_presence_of(:project_url) }
+      it { is_expected.to validate_presence_of(:issues_url) }
+      it { is_expected.to validate_presence_of(:new_issue_url) }
+      it_behaves_like 'issue tracker service URL attribute', :project_url
+      it_behaves_like 'issue tracker service URL attribute', :issues_url
+      it_behaves_like 'issue tracker service URL attribute', :new_issue_url
+    end
+
+    context 'when service is inactive' do
+      before { subject.active = false }
+
+      it { is_expected.not_to validate_presence_of(:project_url) }
+      it { is_expected.not_to validate_presence_of(:issues_url) }
+      it { is_expected.not_to validate_presence_of(:new_issue_url) }
+    end
+  end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 53c8408633c2d8eb24a69170aefd806f9cdfaaee..d305cd9ff1e39e4a77a065c932af9bfc1a3d79fe 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -63,6 +63,27 @@
       expect(project2).not_to be_valid
       expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/)
     end
+
+    describe 'wiki path conflict' do
+      context "when the new path has been used by the wiki of other Project" do
+        it 'should have an error on the name attribute' do
+          new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
+
+          expect(new_project).not_to be_valid
+          expect(new_project.errors[:name].first).to eq('has already been taken')
+        end
+      end
+
+      context "when the new wiki path has been used by the path of other Project" do
+        it 'should have an error on the name attribute' do
+          project_with_wiki_suffix = create(:project, path: 'foo.wiki')
+          new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
+
+          expect(new_project).not_to be_valid
+          expect(new_project.errors[:name].first).to eq('has already been taken')
+        end
+      end
+    end
   end
 
   describe 'default_scope' do
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 789816bf2c799d572612ef5f4dd7172dd22f6406..0621c6a06ce68adef67afdbca84c9643992fe8cd 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -72,7 +72,7 @@
     end
   end
 
-  describe '#search_code' do
+  describe '.search_code' do
     let(:snippet) { create(:snippet, content: 'class Foo; end') }
 
     it 'returns snippets with matching content' do
@@ -88,6 +88,46 @@
     end
   end
 
+  describe '.accessible_to' do
+    let(:author)  { create(:author) }
+    let(:project) { create(:empty_project) }
+
+    let!(:public_snippet)   { create(:snippet, :public) }
+    let!(:internal_snippet) { create(:snippet, :internal) }
+    let!(:private_snippet)  { create(:snippet, :private, author: author) }
+
+    let!(:project_public_snippet)   { create(:snippet, :public, project: project) }
+    let!(:project_internal_snippet) { create(:snippet, :internal, project: project) }
+    let!(:project_private_snippet)  { create(:snippet, :private, project: project) }
+
+    it 'returns only public snippets when user is blank' do
+      expect(described_class.accessible_to(nil)).to match_array [public_snippet, project_public_snippet]
+    end
+
+    it 'returns only public, and internal snippets for regular users' do
+      user = create(:user)
+
+      expect(described_class.accessible_to(user)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet]
+    end
+
+    it 'returns public, internal snippets and project private snippets for project members' do
+      member = create(:user)
+      project.team << [member, :developer]
+
+      expect(described_class.accessible_to(member)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
+    end
+
+    it 'returns private snippets where the user is the author' do
+      expect(described_class.accessible_to(author)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet]
+    end
+
+    it 'returns all snippets when for admins' do
+      admin = create(:admin)
+
+      expect(described_class.accessible_to(admin)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
+    end
+  end
+
   describe '#participants' do
     let(:project) { create(:project, :public) }
     let(:snippet) { create(:snippet, content: 'foo', project: project) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 73bee535fe37ba1fc8530289efb3226f34045091..328254ed56b738256423bb22fb1abe2dc8087658 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -31,6 +31,26 @@
     it { is_expected.to have_many(:spam_logs).dependent(:destroy) }
     it { is_expected.to have_many(:todos).dependent(:destroy) }
     it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
+
+    describe '#group_members' do
+      it 'does not include group memberships for which user is a requester' do
+        user = create(:user)
+        group = create(:group, :public)
+        group.request_access(user)
+
+        expect(user.group_members).to be_empty
+      end
+    end
+
+    describe '#project_members' do
+      it 'does not include project memberships for which user is a requester' do
+        user = create(:user)
+        project = create(:project, :public)
+        project.request_access(user)
+
+        expect(user.project_members).to be_empty
+      end
+    end
   end
 
   describe 'validations' do
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 2e65e7f1920bcf4f6cd52b5c13a4bf189bb03211..ed78d582bd0446e0421641515506e2d92626f6c3 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -17,7 +17,7 @@
       it "returns an array of award_emoji" do
         get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['name']).to eq(award_emoji.name)
       end
@@ -25,7 +25,7 @@
       it "should return a 404 error when issue id not found" do
         get api("/projects/#{project.id}/issues/12345/award_emoji", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -33,7 +33,7 @@
       it "returns an array of award_emoji" do
         get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['name']).to eq(downvote.name)
       end
@@ -45,7 +45,7 @@
 
         get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -56,7 +56,7 @@
     it 'returns an array of award emoji' do
       get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.first['name']).to eq(rocket.name)
     end
@@ -68,7 +68,7 @@
       it "returns the award emoji" do
         get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq(award_emoji.name)
         expect(json_response['awardable_id']).to eq(issue.id)
         expect(json_response['awardable_type']).to eq("Issue")
@@ -77,7 +77,7 @@
       it "returns a 404 error if the award is not found" do
         get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -85,7 +85,7 @@
       it 'returns the award emoji' do
         get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq(downvote.name)
         expect(json_response['awardable_id']).to eq(merge_request.id)
         expect(json_response['awardable_type']).to eq("MergeRequest")
@@ -98,7 +98,7 @@
 
         get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -109,7 +109,7 @@
     it 'returns an award emoji' do
       get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).not_to be_an Array
       expect(json_response['name']).to eq(rocket.name)
     end
@@ -120,7 +120,7 @@
       it "creates a new award emoji" do
         post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish'
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq('blowfish')
         expect(json_response['user']['username']).to eq(user.username)
       end
@@ -128,13 +128,13 @@
       it "should return a 400 bad request error if the name is not given" do
         post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return a 401 unauthorized error if the user is not authenticated" do
         post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji"), name: 'thumbsup'
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -145,7 +145,7 @@
         post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
       end.to change { note.award_emoji.count }.from(0).to(1)
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['user']['username']).to eq(user.username)
     end
   end
@@ -157,13 +157,13 @@
           delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user)
         end.to change { issue.award_emoji.count }.from(1).to(0)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'returns a 404 error when the award emoji can not be found' do
         delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -173,13 +173,13 @@
           delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user)
         end.to change { merge_request.award_emoji.count }.from(1).to(0)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'returns a 404 error when note id not found' do
         delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -192,7 +192,7 @@
         delete api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user)
       end.to change { note.award_emoji.count }.from(1).to(0)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 55582aa53d26481830d0ef26ead2f9b828981ba2..b11ca26ee68563c35302d14fad5fcb0318c94e20 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -17,7 +17,7 @@
       project.repository.expire_cache
 
       get api("/projects/#{project.id}/repository/branches", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       branch_names = json_response.map { |x| x['name'] }
       expect(branch_names).to match_array(project.repository.branch_names)
@@ -27,7 +27,7 @@
   describe "GET /projects/:id/repository/branches/:branch" do
     it "should return the branch information for a single branch" do
       get api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['name']).to eq(branch_name)
       expect(json_response['commit']['id']).to eq(branch_sha)
@@ -36,19 +36,19 @@
 
     it "should return a 403 error if guest" do
       get api("/projects/#{project.id}/repository/branches", user2)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     it "should return a 404 error if branch is not available" do
       get api("/projects/#{project.id}/repository/branches/unknown", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
   describe "PUT /projects/:id/repository/branches/:branch/protect" do
     it "should protect a single branch" do
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['name']).to eq(branch_name)
       expect(json_response['commit']['id']).to eq(branch_sha)
@@ -57,25 +57,25 @@
 
     it "should return a 404 error if branch not found" do
       put api("/projects/#{project.id}/repository/branches/unknown/protect", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return a 403 error if guest" do
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     it "should return success when protect branch again" do
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
   describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
     it "should unprotect a single branch" do
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['name']).to eq(branch_name)
       expect(json_response['commit']['id']).to eq(branch_sha)
@@ -84,13 +84,13 @@
 
     it "should return success when unprotect branch" do
       put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return success when unprotect branch again" do
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
       put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -100,7 +100,7 @@
            branch_name: 'feature1',
            ref: branch_sha
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
 
       expect(json_response['name']).to eq('feature1')
       expect(json_response['commit']['id']).to eq(branch_sha)
@@ -110,14 +110,14 @@
       post api("/projects/#{project.id}/repository/branches", user2),
            branch_name: branch_name,
            ref: branch_sha
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     it 'should return 400 if branch name is invalid' do
       post api("/projects/#{project.id}/repository/branches", user),
            branch_name: 'new design',
            ref: branch_sha
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Branch name is invalid')
     end
 
@@ -125,12 +125,12 @@
       post api("/projects/#{project.id}/repository/branches", user),
            branch_name: 'new_design1',
            ref: branch_sha
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
 
       post api("/projects/#{project.id}/repository/branches", user),
            branch_name: 'new_design1',
            ref: branch_sha
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Branch already exists')
     end
 
@@ -138,7 +138,7 @@
       post api("/projects/#{project.id}/repository/branches", user),
            branch_name: 'new_design3',
            ref: 'foo'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Invalid reference name')
     end
   end
@@ -150,25 +150,25 @@
 
     it "should remove branch" do
       delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['branch_name']).to eq(branch_name)
     end
 
     it 'should return 404 if branch not exists' do
       delete api("/projects/#{project.id}/repository/branches/foobar", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should remove protected branch" do
       project.protected_branches.create(name: branch_name)
       delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
       expect(json_response['message']).to eq('Protected branch cant be removed')
     end
 
     it "should not remove HEAD branch" do
       delete api("/projects/#{project.id}/repository/branches/master", user)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
       expect(json_response['message']).to eq('Cannot remove HEAD branch')
     end
   end
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index 47e9253a10cac9df5271fef581408546f4a14bb6..f5b39c3d69857663d00bc57e201d2eb76ad882a8 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -19,7 +19,7 @@
 
     context 'authorized user' do
       it 'should return project builds' do
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
       end
 
@@ -32,7 +32,7 @@
         let(:query) { 'scope=pending' }
 
         it do
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
         end
       end
@@ -41,7 +41,7 @@
         let(:query) { 'scope[0]=pending&scope[1]=running' }
 
         it do
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
         end
       end
@@ -49,7 +49,7 @@
       context 'respond 400 when scope contains invalid state' do
         let(:query) { 'scope[0]=pending&scope[1]=unknown_status' }
 
-        it { expect(response.status).to eq(400) }
+        it { expect(response).to have_http_status(400) }
       end
     end
 
@@ -57,29 +57,66 @@
       let(:api_user) { nil }
 
       it 'should not return project builds' do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
 
   describe 'GET /projects/:id/repository/commits/:sha/builds' do
-    before do
-      project.ensure_pipeline(pipeline.sha, 'master')
-      get api("/projects/#{project.id}/repository/commits/#{pipeline.sha}/builds", api_user)
-    end
+    context 'when commit does not exist in repository' do
+      before do
+        get api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user)
+      end
 
-    context 'authorized user' do
-      it 'should return project builds for specific commit' do
-        expect(response.status).to eq(200)
-        expect(json_response).to be_an Array
+      it 'responds with 404' do
+        expect(response).to have_http_status(404)
       end
     end
 
-    context 'unauthorized user' do
-      let(:api_user) { nil }
+    context 'when commit exists in repository' do
+      context 'when user is authorized' do
+        context 'when pipeline has builds' do
+          before do
+            create(:ci_pipeline, project: project, sha: project.commit.id)
+            create(:ci_build, pipeline: pipeline)
+            create(:ci_build)
+
+            get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user)
+          end
+
+          it 'should return project builds for specific commit' do
+            expect(response).to have_http_status(200)
+            expect(json_response).to be_an Array
+            expect(json_response.size).to eq 2
+          end
+        end
 
-      it 'should not return project builds' do
-        expect(response.status).to eq(401)
+        context 'when pipeline has no builds' do
+          before do
+            branch_head = project.commit('feature').id
+            get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user)
+          end
+
+          it 'returns an empty array' do
+            expect(response).to have_http_status(200)
+            expect(json_response).to be_an Array
+            expect(json_response).to be_empty
+          end
+        end
+      end
+
+      context 'when user is not authorized' do
+        before do
+          create(:ci_pipeline, project: project, sha: project.commit.id)
+          create(:ci_build, pipeline: pipeline)
+
+          get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil)
+        end
+
+        it 'should not return project builds' do
+          expect(response).to have_http_status(401)
+          expect(json_response.except('message')).to be_empty
+        end
       end
     end
   end
@@ -89,7 +126,7 @@
 
     context 'authorized user' do
       it 'should return specific build data' do
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq('test')
       end
     end
@@ -98,7 +135,7 @@
       let(:api_user) { nil }
 
       it 'should not return specific build data' do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -116,7 +153,7 @@
         end
 
         it 'should return specific build artifacts' do
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(response.headers).to include(download_headers)
         end
       end
@@ -125,13 +162,13 @@
         let(:api_user) { nil }
 
         it 'should not return specific build artifacts' do
-          expect(response.status).to eq(401)
+          expect(response).to have_http_status(401)
         end
       end
     end
 
     it 'should not return build artifacts if not uploaded' do
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -142,7 +179,7 @@
 
     context 'authorized user' do
       it 'should return specific build trace' do
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(response.body).to eq(build.trace)
       end
     end
@@ -151,7 +188,7 @@
       let(:api_user) { nil }
 
       it 'should not return specific build trace' do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -162,7 +199,7 @@
     context 'authorized user' do
       context 'user with :update_build persmission' do
         it 'should cancel running or pending build' do
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           expect(project.builds.first.status).to eq('canceled')
         end
       end
@@ -171,7 +208,7 @@
         let(:api_user) { user2 }
 
         it 'should not cancel build' do
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
     end
@@ -180,7 +217,7 @@
       let(:api_user) { nil }
 
       it 'should not cancel build' do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -193,7 +230,7 @@
     context 'authorized user' do
       context 'user with :update_build permission' do
         it 'should retry non-running build' do
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           expect(project.builds.first.status).to eq('canceled')
           expect(json_response['status']).to eq('pending')
         end
@@ -203,7 +240,7 @@
         let(:api_user) { user2 }
 
         it 'should not retry build' do
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
     end
@@ -212,7 +249,7 @@
       let(:api_user) { nil }
 
       it 'should not retry build' do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 298cdbad329dd3811045167d01008c5979c8dd37..6668f3543f6f7312f75244aaa7d36022635272a4 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -41,7 +41,7 @@ def create_status(commit, opts = {})
           before { get api(get_url, reporter) }
 
           it 'returns latest commit statuses' do
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
 
             expect(json_response).to be_an Array
             expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id)
@@ -54,7 +54,7 @@ def create_status(commit, opts = {})
           before { get api(get_url, reporter), all: 1 }
 
           it 'returns all commit statuses' do
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
 
             expect(json_response).to be_an Array
             expect(statuses_id).to contain_exactly(status1.id, status2.id,
@@ -67,7 +67,7 @@ def create_status(commit, opts = {})
           before { get api(get_url, reporter), ref: 'develop' }
 
           it 'returns latest commit statuses for specific ref' do
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
 
             expect(json_response).to be_an Array
             expect(statuses_id).to contain_exactly(status3.id, status5.id)
@@ -78,7 +78,7 @@ def create_status(commit, opts = {})
           before { get api(get_url, reporter), name: 'coverage' }
 
           it 'return latest commit statuses for specific name' do
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
 
             expect(json_response).to be_an Array
             expect(statuses_id).to contain_exactly(status4.id, status5.id)
@@ -101,7 +101,7 @@ def create_status(commit, opts = {})
       before { get api(get_url, guest) }
 
       it "should not return project commits" do
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -109,7 +109,7 @@ def create_status(commit, opts = {})
       before { get api(get_url) }
 
       it "should not return project commits" do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -122,7 +122,7 @@ def create_status(commit, opts = {})
         before { post api(post_url, developer), state: 'success' }
 
         it 'creates commit status' do
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           expect(json_response['sha']).to eq(commit.id)
           expect(json_response['status']).to eq('success')
           expect(json_response['name']).to eq('default')
@@ -141,7 +141,7 @@ def create_status(commit, opts = {})
         end
 
         it 'creates commit status' do
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           expect(json_response['sha']).to eq(commit.id)
           expect(json_response['status']).to eq('success')
           expect(json_response['name']).to eq('coverage')
@@ -155,7 +155,7 @@ def create_status(commit, opts = {})
         before { post api(post_url, developer), state: 'invalid' }
 
         it 'does not create commit status' do
-          expect(response.status).to eq(400)
+          expect(response).to have_http_status(400)
         end
       end
 
@@ -163,7 +163,7 @@ def create_status(commit, opts = {})
         before { post api(post_url, developer) }
 
         it 'does not create commit status' do
-          expect(response.status).to eq(400)
+          expect(response).to have_http_status(400)
         end
       end
 
@@ -172,7 +172,7 @@ def create_status(commit, opts = {})
         before { post api(post_url, developer), state: 'running' }
 
         it 'returns not found error' do
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
@@ -181,7 +181,7 @@ def create_status(commit, opts = {})
       before { post api(post_url, reporter) }
 
       it 'should not create commit status' do
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -189,7 +189,7 @@ def create_status(commit, opts = {})
       before { post api(post_url, guest) }
 
       it 'should not create commit status' do
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -197,7 +197,7 @@ def create_status(commit, opts = {})
       before { post api(post_url) }
 
       it 'should not create commit status' do
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 6fc38f537d3df10afb28cd58550b8337f2b9c174..5219c8087915f787b1c95a86a6d8a0c17c3b8e21 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -19,7 +19,7 @@
 
       it "should return project commits" do
         get api("/projects/#{project.id}/repository/commits", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
 
         expect(json_response).to be_an Array
         expect(json_response.first['id']).to eq(project.repository.commit.id)
@@ -29,7 +29,7 @@
     context "unauthorized user" do
       it "should not return project commits" do
         get api("/projects/#{project.id}/repository/commits")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -63,7 +63,7 @@
       it "should return an invalid parameter error message" do
         get api("/projects/#{project.id}/repository/commits?since=invalid-date", user)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format"
       end
     end
@@ -73,26 +73,26 @@
     context "authorized user" do
       it "should return a commit by sha" do
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['id']).to eq(project.repository.commit.id)
         expect(json_response['title']).to eq(project.repository.commit.title)
       end
 
       it "should return a 404 error if not found" do
         get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should return nil for commit without CI" do
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['status']).to be_nil
       end
 
       it "should return status for CI" do
         pipeline = project.ensure_pipeline(project.repository.commit.sha, 'master')
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['status']).to eq(pipeline.status)
       end
     end
@@ -100,7 +100,7 @@
     context "unauthorized user" do
       it "should not return the selected commit" do
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -111,7 +111,7 @@
 
       it "should return the diff of the selected commit" do
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
 
         expect(json_response).to be_an Array
         expect(json_response.length).to be >= 1
@@ -120,14 +120,14 @@
 
       it "should return a 404 error if invalid commit" do
         get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context "unauthorized user" do
       it "should not return the diff of the selected commit" do
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -136,7 +136,7 @@
     context 'authorized user' do
       it 'should return merge_request comments' do
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(2)
         expect(json_response.first['note']).to eq('a comment on a commit')
@@ -145,14 +145,14 @@
 
       it 'should return a 404 error if merge_request_id not found' do
         get api("/projects/#{project.id}/repository/commits/1234ab/comments", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context 'unauthorized user' do
       it 'should not return the diff of the selected commit' do
         get api("/projects/#{project.id}/repository/commits/1234ab/comments")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -161,7 +161,7 @@
     context 'authorized user' do
       it 'should return comment' do
         post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment'
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['note']).to eq('My comment')
         expect(json_response['path']).to be_nil
         expect(json_response['line']).to be_nil
@@ -170,7 +170,7 @@
 
       it 'should return the inline comment' do
         post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.diffs.first.new_path, line: 7, line_type: 'new'
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['note']).to eq('My comment')
         expect(json_response['path']).to eq(project.repository.commit.diffs.first.new_path)
         expect(json_response['line']).to eq(7)
@@ -179,19 +179,19 @@
 
       it 'should return 400 if note is missing' do
         post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user)
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it 'should return 404 if note is attached to non existent commit' do
         post api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment'
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context 'unauthorized user' do
       it 'should not return the diff of the selected commit' do
         post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb
index 0afc3e7933956e2aa3910f02da9fa2b1f152d125..881b818b5e9dd5c625bb2bce269c6f2d1673678a 100644
--- a/spec/requests/api/doorkeeper_access_spec.rb
+++ b/spec/requests/api/doorkeeper_access_spec.rb
@@ -11,21 +11,21 @@
   describe "when unauthenticated" do
     it "returns authentication success" do
       get api("/user"), access_token: token.token
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
   describe "when token invalid" do
     it "returns authentication error" do
       get api("/user"), access_token: "123a"
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
   end
 
   describe "authorization by private token" do
     it "returns authentication success" do
       get api("/user", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 8efa09f75fd4ded6af034ada5266416df1af486b..2e5448143d5ad16722b39907f64cb555229eab0f 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -16,7 +16,7 @@
       }
 
       get api("/projects/#{project.id}/repository/files", user), params
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['file_path']).to eq(file_path)
       expect(json_response['file_name']).to eq('popen.rb')
       expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
@@ -25,7 +25,7 @@
 
     it "should return a 400 bad request if no params given" do
       get api("/projects/#{project.id}/repository/files", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 404 if such file does not exist" do
@@ -35,7 +35,7 @@
       }
 
       get api("/projects/#{project.id}/repository/files", user), params
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -51,13 +51,13 @@
 
     it "should create a new file in project repo" do
       post api("/projects/#{project.id}/repository/files", user), valid_params
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['file_path']).to eq('newfile.rb')
     end
 
     it "should return a 400 bad request if no params given" do
       post api("/projects/#{project.id}/repository/files", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 400 if editor fails to create file" do
@@ -65,7 +65,7 @@
         and_return(false)
 
       post api("/projects/#{project.id}/repository/files", user), valid_params
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
@@ -81,13 +81,13 @@
 
     it "should update existing file in project repo" do
       put api("/projects/#{project.id}/repository/files", user), valid_params
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['file_path']).to eq(file_path)
     end
 
     it "should return a 400 bad request if no params given" do
       put api("/projects/#{project.id}/repository/files", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
@@ -102,20 +102,20 @@
 
     it "should delete existing file in project repo" do
       delete api("/projects/#{project.id}/repository/files", user), valid_params
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['file_path']).to eq(file_path)
     end
 
     it "should return a 400 bad request if no params given" do
       delete api("/projects/#{project.id}/repository/files", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 400 if fails to create file" do
       allow_any_instance_of(Repository).to receive(:remove_file).and_return(false)
 
       delete api("/projects/#{project.id}/repository/files", user), valid_params
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
@@ -143,7 +143,7 @@
 
     it "remains unchanged" do
       get api("/projects/#{project.id}/repository/files", user), get_params
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['file_path']).to eq(file_path)
       expect(json_response['file_name']).to eq(file_path)
       expect(json_response['content']).to eq(put_params[:content])
diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb
index fa94e03ec32204153f83d579f2e92d56942196c6..a9f5aa924b71a7495f22009fade6fd23941d0299 100644
--- a/spec/requests/api/fork_spec.rb
+++ b/spec/requests/api/fork_spec.rb
@@ -22,7 +22,7 @@
     context 'when authenticated' do
       it 'should fork if user has sufficient access to project' do
         post api("/projects/fork/#{project.id}", user2)
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq(project.name)
         expect(json_response['path']).to eq(project.path)
         expect(json_response['owner']['id']).to eq(user2.id)
@@ -32,7 +32,7 @@
 
       it 'should fork if user is admin' do
         post api("/projects/fork/#{project.id}", admin)
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq(project.name)
         expect(json_response['path']).to eq(project.path)
         expect(json_response['owner']['id']).to eq(admin.id)
@@ -42,20 +42,20 @@
 
       it 'should fail on missing project access for the project to fork' do
         post api("/projects/fork/#{project.id}", user3)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 Project Not Found')
       end
 
       it 'should fail if forked project exists in the user namespace' do
         post api("/projects/fork/#{project.id}", user)
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         expect(json_response['message']['name']).to eq(['has already been taken'])
         expect(json_response['message']['path']).to eq(['has already been taken'])
       end
 
       it 'should fail if project to fork from does not exist' do
         post api('/projects/fork/424242', user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 Project Not Found')
       end
     end
@@ -63,7 +63,7 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         post api("/projects/fork/#{project.id}")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
         expect(json_response['message']).to eq('401 Unauthorized')
       end
     end
diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb
index 02553d0f8e2ac453903b057f2dd6c2acb027df1e..52f9e7d4681cc71b796c6b33db658a31c5b5693c 100644
--- a/spec/requests/api/group_members_spec.rb
+++ b/spec/requests/api/group_members_spec.rb
@@ -31,7 +31,7 @@
       it "each user: should return an array of members groups of group3" do
         [owner, master, developer, reporter, guest].each do |user|
           get api("/groups/#{group_with_members.id}/members", user)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.size).to eq(5)
           expect(json_response.find { |e| e['id'] == owner.id }['access_level']).to eq(GroupMember::OWNER)
@@ -45,7 +45,7 @@
       it 'users not part of the group should get access error' do
         get api("/groups/#{group_with_members.id}/members", stranger)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -54,7 +54,7 @@
     context "when not a member of the group" do
       it "should not add guest as member of group_no_members when adding being done by person outside the group" do
         post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -66,7 +66,7 @@
           post api("/groups/#{group_no_members.id}/members", owner), user_id: new_user.id, access_level: GroupMember::MASTER
         end.to change { group_no_members.members.count }.by(1)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq(new_user.name)
         expect(json_response['access_level']).to eq(GroupMember::MASTER)
       end
@@ -78,27 +78,27 @@
           post api("/groups/#{group_with_members.id}/members", guest), user_id: new_user.id, access_level: GroupMember::MASTER
         end.not_to change { group_with_members.members.count }
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it "should return error if member already exists" do
         post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
       end
 
       it "should return a 400 error when user id is not given" do
         post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return a 400 error when access level is not given" do
         post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return a 422 error when access level is not known" do
         post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234
-        expect(response.status).to eq(422)
+        expect(response).to have_http_status(422)
       end
     end
   end
@@ -110,7 +110,7 @@
           api("/groups/#{group_no_members.id}/members/#{developer.id}",
               owner), access_level: GroupMember::MASTER
         )
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -122,7 +122,7 @@
           access_level: GroupMember::MASTER
         )
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
 
         get api("/groups/#{group_with_members.id}/members", owner)
         json_reporter = json_response.find do |e|
@@ -139,7 +139,7 @@
           access_level: GroupMember::MASTER
         )
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
 
         get api("/groups/#{group_with_members.id}/members", owner)
         json_developer = json_response.find do |e|
@@ -153,7 +153,7 @@
         put(
           api("/groups/#{group_with_members.id}/members/#{master.id}", owner)
         )
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it 'should return a 422 error when access level is not known' do
@@ -161,7 +161,7 @@
           api("/groups/#{group_with_members.id}/members/#{master.id}", owner),
           access_level: 1234
         )
-        expect(response.status).to eq(422)
+        expect(response).to have_http_status(422)
       end
     end
   end
@@ -172,7 +172,7 @@
         random_user = create(:user)
         delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -182,17 +182,17 @@
           delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner)
         end.to change { group_with_members.members.count }.by(-1)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it "should return a 404 error when user id is not known" do
         delete api("/groups/#{group_with_members.id}/members/1328", owner)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should not allow guest to modify group members" do
         delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
   end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 7ecefce80d60f74bf29672c966d3700f7cdcc411..04141a45031539de0b524e3b5cf246b42e320196 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -23,14 +23,14 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/groups")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context "when authenticated as user" do
       it "normal user: should return an array of groups of user1" do
         get api("/groups", user1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['name']).to eq(group1.name)
@@ -40,7 +40,7 @@
     context "when authenticated as  admin" do
       it "admin: should return an array of all groups" do
         get api("/groups", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(2)
       end
@@ -51,51 +51,51 @@
     context "when authenticated as user" do
       it "should return one of user1's groups" do
         get api("/groups/#{group1.id}", user1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         json_response['name'] == group1.name
       end
 
       it "should not return a non existing group" do
         get api("/groups/1328", user1)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should not return a group not attached to user1" do
         get api("/groups/#{group2.id}", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context "when authenticated as admin" do
       it "should return any existing group" do
         get api("/groups/#{group2.id}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq(group2.name)
       end
 
       it "should not return a non existing group" do
         get api("/groups/1328", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context 'when using group path in URL' do
       it 'should return any existing group' do
         get api("/groups/#{group1.path}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq(group1.name)
       end
 
       it 'should not return a non existing group' do
         get api('/groups/unknown', admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should not return a group not attached to user1' do
         get api("/groups/#{group2.path}", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -107,14 +107,14 @@
       it 'updates the group' do
         put api("/groups/#{group1.id}", user1), name: new_group_name
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq(new_group_name)
       end
 
       it 'returns 404 for a non existing group' do
         put api('/groups/1328', user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -122,7 +122,7 @@
       it 'updates the group' do
         put api("/groups/#{group1.id}", admin), name: new_group_name
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['name']).to eq(new_group_name)
       end
     end
@@ -131,7 +131,7 @@
       it 'does not updates the group' do
         put api("/groups/#{group1.id}", user2), name: new_group_name
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -139,7 +139,7 @@
       it 'returns 404 when trying to update the group' do
         put api("/groups/#{group2.id}", user1), name: new_group_name
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -149,7 +149,7 @@
       it "should return the group's projects" do
         get api("/groups/#{group1.id}/projects", user1)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response.length).to eq(2)
         project_names = json_response.map { |proj| proj['name' ] }
         expect(project_names).to match_array([project1.name, project3.name])
@@ -157,13 +157,13 @@
 
       it "should not return a non existing group" do
         get api("/groups/1328/projects", user1)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should not return a group not attached to user1" do
         get api("/groups/#{group2.id}/projects", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should only return projects to which user has access" do
@@ -171,7 +171,7 @@
 
         get api("/groups/#{group1.id}/projects", user3)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response.length).to eq(1)
         expect(json_response.first['name']).to eq(project3.name)
       end
@@ -180,14 +180,14 @@
     context "when authenticated as admin" do
       it "should return any existing group" do
         get api("/groups/#{group2.id}/projects", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response.length).to eq(1)
         expect(json_response.first['name']).to eq(project2.name)
       end
 
       it "should not return a non existing group" do
         get api("/groups/1328/projects", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -195,20 +195,20 @@
       it 'should return any existing group' do
         get api("/groups/#{group1.path}/projects", admin)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_names = json_response.map { |proj| proj['name' ] }
         expect(project_names).to match_array([project1.name, project3.name])
       end
 
       it 'should not return a non existing group' do
         get api('/groups/unknown/projects', admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should not return a group not attached to user1' do
         get api("/groups/#{group2.path}/projects", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -217,30 +217,30 @@
     context "when authenticated as user without group permissions" do
       it "should not create group" do
         post api("/groups", user1), attributes_for(:group)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
     context "when authenticated as user with group permissions" do
       it "should create group" do
         post api("/groups", user3), attributes_for(:group)
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
       end
 
       it "should not create group, duplicate" do
         post api("/groups", user3), { name: 'Duplicate Test', path: group2.path }
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(response.message).to eq("Bad Request")
       end
 
       it "should return 400 bad request error if name not given" do
         post api("/groups", user3), { path: group2.path }
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return 400 bad request error if path not given" do
         post api("/groups", user3), { name: 'test' }
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
     end
   end
@@ -249,37 +249,37 @@
     context "when authenticated as user" do
       it "should remove group" do
         delete api("/groups/#{group1.id}", user1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it "should not remove a group if not an owner" do
         user4 = create(:user)
         group1.add_master(user4)
         delete api("/groups/#{group1.id}", user3)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it "should not remove a non existing group" do
         delete api("/groups/1328", user1)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should not remove a group not attached to user1" do
         delete api("/groups/#{group2.id}", user1)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context "when authenticated as admin" do
       it "should remove any existing group" do
         delete api("/groups/#{group2.id}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it "should not remove a non existing group" do
         delete api("/groups/1328", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -295,14 +295,14 @@
     context "when authenticated as user" do
       it "should not transfer project to group" do
         post api("/groups/#{group1.id}/projects/#{project.id}", user2)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
     context "when authenticated as admin" do
       it "should transfer project to group" do
         post api("/groups/#{group1.id}/projects/#{project.id}", admin)
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
       end
     end
   end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 22802dd0e05104ab36d0a9ad81a03099c5d7f5e6..437c89c35776ab35a7424a8bd58cd5253072bb6d 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -11,7 +11,7 @@
     it do
       get api("/internal/check"), secret_token: secret_token
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['api_version']).to eq(API::API.version)
     end
   end
@@ -23,7 +23,7 @@
       it do
         get api("/internal/broadcast_message"), secret_token: secret_token
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response["message"]).to eq(broadcast_message.message)
       end
     end
@@ -32,7 +32,7 @@
       it do
         get api("/internal/broadcast_message"), secret_token: secret_token
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_empty
       end
     end
@@ -42,7 +42,7 @@
     it do
       get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['name']).to eq(user.name)
     end
@@ -61,7 +61,7 @@
 
           push(key, project_wiki)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_truthy
         end
       end
@@ -70,7 +70,7 @@
         it do
           pull(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_truthy
         end
       end
@@ -79,7 +79,7 @@
         it do
           push(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_truthy
         end
       end
@@ -94,7 +94,7 @@
         it do
           pull(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_falsey
         end
       end
@@ -103,7 +103,7 @@
         it do
           push(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_falsey
         end
       end
@@ -120,7 +120,7 @@
         it do
           pull(key, personal_project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_falsey
         end
       end
@@ -129,7 +129,7 @@
         it do
           push(key, personal_project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_falsey
         end
       end
@@ -147,7 +147,7 @@
         it do
           pull(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_truthy
         end
       end
@@ -156,7 +156,7 @@
         it do
           push(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_falsey
         end
       end
@@ -173,7 +173,7 @@
         it do
           archive(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_truthy
         end
       end
@@ -182,7 +182,7 @@
         it do
           archive(key, project)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_falsey
         end
       end
@@ -192,7 +192,7 @@
       it do
         pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists'))
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response["status"]).to be_falsey
       end
     end
@@ -201,7 +201,7 @@
       it do
         pull(OpenStruct.new(id: 0), project)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response["status"]).to be_falsey
       end
     end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 59e557c5b2a21d3c25359462fc947c7408bffc6e..2cf130df328fc8e8b4c9d1fc6625eb26730fbdf2 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -51,14 +51,14 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/issues")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context "when authenticated" do
       it "should return an array of issues" do
         get api("/issues", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['title']).to eq(issue.title)
       end
@@ -72,7 +72,7 @@
 
       it 'should return an array of closed issues' do
         get api('/issues?state=closed', user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['id']).to eq(closed_issue.id)
@@ -80,7 +80,7 @@
 
       it 'should return an array of opened issues' do
         get api('/issues?state=opened', user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['id']).to eq(issue.id)
@@ -88,7 +88,7 @@
 
       it 'should return an array of all issues' do
         get api('/issues?state=all', user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(2)
         expect(json_response.first['id']).to eq(issue.id)
@@ -97,7 +97,7 @@
 
       it 'should return an array of labeled issues' do
         get api("/issues?labels=#{label.title}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['labels']).to eq([label.title])
@@ -105,7 +105,7 @@
 
       it 'should return an array of labeled issues when at least one label matches' do
         get api("/issues?labels=#{label.title},foo,bar", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['labels']).to eq([label.title])
@@ -113,14 +113,14 @@
 
       it 'should return an empty array if no issue matches labels' do
         get api('/issues?labels=foo,bar', user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(0)
       end
 
       it 'should return an array of labeled issues matching given state' do
         get api("/issues?labels=#{label.title}&state=opened", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['labels']).to eq([label.title])
@@ -129,20 +129,162 @@
 
       it 'should return an empty array if no issue matches labels and state filters' do
         get api("/issues?labels=#{label.title}&state=closed", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(0)
       end
     end
   end
 
+  describe "GET /groups/:id/issues" do
+    let!(:group)            { create(:group) }
+    let!(:group_project)    { create(:project, :public, creator_id: user.id, namespace: group) }
+    let!(:group_closed_issue) do
+      create :closed_issue,
+             author: user,
+             assignee: user,
+             project: group_project,
+             state: :closed,
+             milestone: group_milestone
+    end
+    let!(:group_confidential_issue) do
+      create :issue,
+             :confidential,
+             project: group_project,
+             author: author,
+             assignee: assignee
+    end
+    let!(:group_issue) do
+      create :issue,
+             author: user,
+             assignee: user,
+             project: group_project,
+             milestone: group_milestone
+    end
+    let!(:group_label) do
+      create(:label, title: 'group_lbl', color: '#FFAABB', project: group_project)
+    end
+    let!(:group_label_link) { create(:label_link, label: group_label, target: group_issue) }
+    let!(:group_milestone) { create(:milestone, title: '3.0.0', project: group_project) }
+    let!(:group_empty_milestone) do
+      create(:milestone, title: '4.0.0', project: group_project)
+    end
+    let!(:group_note) { create(:note_on_issue, author: user, project: group_project, noteable: group_issue) }
+
+    before do
+      group_project.team << [user, :reporter]
+    end
+    let(:base_url) { "/groups/#{group.id}/issues" }
+
+    it 'returns group issues without confidential issues for non project members' do
+      get api(base_url, non_member)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(1)
+      expect(json_response.first['title']).to eq(group_issue.title)
+    end
+
+    it 'returns group confidential issues for author' do
+      get api(base_url, author)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(2)
+    end
+
+    it 'returns group confidential issues for assignee' do
+      get api(base_url, assignee)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(2)
+    end
+
+    it 'returns group issues with confidential issues for project members' do
+      get api(base_url, user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(2)
+    end
+
+    it 'returns group confidential issues for admin' do
+      get api(base_url, admin)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(2)
+    end
+
+    it 'returns an array of labeled group issues' do
+      get api("#{base_url}?labels=#{group_label.title}", user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(1)
+      expect(json_response.first['labels']).to eq([group_label.title])
+    end
+
+    it 'returns an array of labeled group issues where all labels match' do
+      get api("#{base_url}?labels=#{group_label.title},foo,bar", user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(0)
+    end
+
+    it 'returns an empty array if no group issue matches labels' do
+      get api("#{base_url}?labels=foo,bar", user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(0)
+    end
+
+    it 'returns an empty array if no issue matches milestone' do
+      get api("#{base_url}?milestone=#{group_empty_milestone.title}", user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(0)
+    end
+
+    it 'returns an empty array if milestone does not exist' do
+      get api("#{base_url}?milestone=foo", user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(0)
+    end
+
+    it 'returns an array of issues in given milestone' do
+      get api("#{base_url}?milestone=#{group_milestone.title}", user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(1)
+      expect(json_response.first['id']).to eq(group_issue.id)
+    end
+
+    it 'returns an array of issues matching state in milestone' do
+      get api("#{base_url}?milestone=#{group_milestone.title}"\
+              '&state=closed', user)
+
+      expect(response).to have_http_status(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(1)
+      expect(json_response.first['id']).to eq(group_closed_issue.id)
+    end
+  end
+
   describe "GET /projects/:id/issues" do
     let(:base_url) { "/projects/#{project.id}" }
     let(:title) { milestone.title }
 
     it 'should return project issues without confidential issues for non project members' do
       get api("#{base_url}/issues", non_member)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(2)
       expect(json_response.first['title']).to eq(issue.title)
@@ -150,7 +292,7 @@
 
     it 'should return project issues without confidential issues for project members with guest role' do
       get api("#{base_url}/issues", guest)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(2)
       expect(json_response.first['title']).to eq(issue.title)
@@ -158,7 +300,7 @@
 
     it 'should return project confidential issues for author' do
       get api("#{base_url}/issues", author)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(3)
       expect(json_response.first['title']).to eq(issue.title)
@@ -166,7 +308,7 @@
 
     it 'should return project confidential issues for assignee' do
       get api("#{base_url}/issues", assignee)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(3)
       expect(json_response.first['title']).to eq(issue.title)
@@ -174,7 +316,7 @@
 
     it 'should return project issues with confidential issues for project members' do
       get api("#{base_url}/issues", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(3)
       expect(json_response.first['title']).to eq(issue.title)
@@ -182,7 +324,7 @@
 
     it 'should return project confidential issues for admin' do
       get api("#{base_url}/issues", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(3)
       expect(json_response.first['title']).to eq(issue.title)
@@ -190,7 +332,7 @@
 
     it 'should return an array of labeled project issues' do
       get api("#{base_url}/issues?labels=#{label.title}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['labels']).to eq([label.title])
@@ -198,7 +340,7 @@
 
     it 'should return an array of labeled project issues when at least one label matches' do
       get api("#{base_url}/issues?labels=#{label.title},foo,bar", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['labels']).to eq([label.title])
@@ -206,28 +348,28 @@
 
     it 'should return an empty array if no project issue matches labels' do
       get api("#{base_url}/issues?labels=foo,bar", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(0)
     end
 
     it 'should return an empty array if no issue matches milestone' do
       get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(0)
     end
 
     it 'should return an empty array if milestone does not exist' do
       get api("#{base_url}/issues?milestone=foo", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(0)
     end
 
     it 'should return an array of issues in given milestone' do
       get api("#{base_url}/issues?milestone=#{title}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(2)
       expect(json_response.first['id']).to eq(issue.id)
@@ -237,7 +379,7 @@
     it 'should return an array of issues matching state in milestone' do
       get api("#{base_url}/issues?milestone=#{milestone.title}"\
               '&state=closed', user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['id']).to eq(closed_issue.id)
@@ -248,7 +390,7 @@
     it 'exposes known attributes' do
       get api("/projects/#{project.id}/issues/#{issue.id}", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['id']).to eq(issue.id)
       expect(json_response['iid']).to eq(issue.iid)
       expect(json_response['project_id']).to eq(issue.project.id)
@@ -266,7 +408,7 @@
     it "should return a project issue by id" do
       get api("/projects/#{project.id}/issues/#{issue.id}", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq(issue.title)
       expect(json_response['iid']).to eq(issue.iid)
     end
@@ -281,44 +423,44 @@
 
     it "should return 404 if issue id not found" do
       get api("/projects/#{project.id}/issues/54321", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     context 'confidential issues' do
       it "should return 404 for non project members" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should return 404 for project members with guest role" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should return confidential issue for project members" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq(confidential_issue.title)
         expect(json_response['iid']).to eq(confidential_issue.iid)
       end
 
       it "should return confidential issue for author" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", author)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq(confidential_issue.title)
         expect(json_response['iid']).to eq(confidential_issue.iid)
       end
 
       it "should return confidential issue for assignee" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", assignee)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq(confidential_issue.title)
         expect(json_response['iid']).to eq(confidential_issue.iid)
       end
 
       it "should return confidential issue for admin" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq(confidential_issue.title)
         expect(json_response['iid']).to eq(confidential_issue.iid)
       end
@@ -329,7 +471,7 @@
     it "should create a new project issue" do
       post api("/projects/#{project.id}/issues", user),
         title: 'new issue', labels: 'label, label2'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['title']).to eq('new issue')
       expect(json_response['description']).to be_nil
       expect(json_response['labels']).to eq(['label', 'label2'])
@@ -337,21 +479,21 @@
 
     it "should return a 400 bad request if title not given" do
       post api("/projects/#{project.id}/issues", user), labels: 'label, label2'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 on invalid label names' do
       post api("/projects/#{project.id}/issues", user),
            title: 'new issue',
            labels: 'label, ?'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
     end
 
     it 'should return 400 if title is too long' do
       post api("/projects/#{project.id}/issues", user),
            title: 'g' * 256
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['title']).to eq([
         'is too long (maximum is 255 characters)'
       ])
@@ -363,7 +505,7 @@
         post api("/projects/#{project.id}/issues", user),
           title: 'new issue', labels: 'label, label2', created_at: creation_time
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
       end
     end
@@ -387,7 +529,7 @@
 
     it "should not create a new project issue" do
       expect { post api("/projects/#{project.id}/issues", user), params }.not_to change(Issue, :count)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq({ "error" => "Spam detected" })
 
       spam_logs = SpamLog.all
@@ -404,7 +546,7 @@
     it "should update a project issue" do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
         title: 'updated title'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['title']).to eq('updated title')
     end
@@ -412,14 +554,14 @@
     it "should return 404 error if issue id not found" do
       put api("/projects/#{project.id}/issues/44444", user),
         title: 'updated title'
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it 'should return 400 on invalid label names' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           title: 'updated title',
           labels: 'label, ?'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
     end
 
@@ -427,33 +569,33 @@
       it "should return 403 for non project members" do
         put api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member),
           title: 'updated title'
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it "should return 403 for project members with guest role" do
         put api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest),
           title: 'updated title'
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it "should update a confidential issue for project members" do
         put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
           title: 'updated title'
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq('updated title')
       end
 
       it "should update a confidential issue for author" do
         put api("/projects/#{project.id}/issues/#{confidential_issue.id}", author),
           title: 'updated title'
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq('updated title')
       end
 
       it "should update a confidential issue for admin" do
         put api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin),
           title: 'updated title'
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq('updated title')
       end
     end
@@ -466,21 +608,21 @@
     it 'should not update labels if not present' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           title: 'updated title'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['labels']).to eq([label.title])
     end
 
     it 'should remove all labels' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           labels: ''
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['labels']).to eq([])
     end
 
     it 'should update labels' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           labels: 'foo,bar'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['labels']).to include 'foo'
       expect(json_response['labels']).to include 'bar'
     end
@@ -488,14 +630,14 @@
     it 'should return 400 on invalid label names' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           labels: 'label, ?'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
     end
 
     it 'should allow special label names' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           labels: 'label:foo, label-bar,label_bar,label/bar'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['labels']).to include 'label:foo'
       expect(json_response['labels']).to include 'label-bar'
       expect(json_response['labels']).to include 'label_bar'
@@ -505,7 +647,7 @@
     it 'should return 400 if title is too long' do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
           title: 'g' * 256
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['title']).to eq([
         'is too long (maximum is 255 characters)'
       ])
@@ -516,7 +658,7 @@
     it "should update a project issue" do
       put api("/projects/#{project.id}/issues/#{issue.id}", user),
         labels: 'label2', state_event: "close"
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['labels']).to include 'label2'
       expect(json_response['state']).to eq "closed"
@@ -527,7 +669,7 @@
         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.status).to eq(200)
+        expect(response).to have_http_status(200)
 
         expect(json_response['labels']).to include 'label3'
         expect(Time.parse(json_response['updated_at'])).to be_within(1.second).of(update_time)
@@ -538,12 +680,12 @@
   describe "DELETE /projects/:id/issues/:issue_id" do
     it "rejects a non member from deleting an issue" do
       delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
-      expect(response.status).to be(403)
+      expect(response).to have_http_status(403)
     end
 
     it "rejects a developer from deleting an issue" do
       delete api("/projects/#{project.id}/issues/#{issue.id}", author)
-      expect(response.status).to be(403)
+      expect(response).to have_http_status(403)
     end
 
     context "when the user is project owner" do
@@ -552,7 +694,7 @@
 
       it "deletes the issue if an admin requests it" do
         delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['state']).to eq 'opened'
       end
     end
@@ -566,7 +708,7 @@
       post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                to_project_id: target_project.id
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['project_id']).to eq(target_project.id)
     end
 
@@ -575,7 +717,7 @@
         post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                  to_project_id: project.id
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']).to eq('Cannot move issue to project it originates from!')
       end
     end
@@ -585,7 +727,7 @@
         post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                  to_project_id: target_project2.id
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']).to eq('Cannot move issue due to insufficient permissions!')
       end
     end
@@ -594,7 +736,7 @@
       post api("/projects/#{project.id}/issues/#{issue.id}/move", admin),
                to_project_id: target_project2.id
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['project_id']).to eq(target_project2.id)
     end
 
@@ -603,7 +745,7 @@
         post api("/projects/#{project.id}/issues/123/move", user),
                  to_project_id: target_project.id
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -612,7 +754,7 @@
         post api("/projects/123/issues/#{issue.id}/move", user),
                  to_project_id: target_project.id
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -621,7 +763,7 @@
         post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
                  to_project_id: 123
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -630,26 +772,26 @@
     it 'subscribes to an issue' do
       post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2)
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['subscribed']).to eq(true)
     end
 
     it 'returns 304 if already subscribed' do
       post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user)
 
-      expect(response.status).to eq(304)
+      expect(response).to have_http_status(304)
     end
 
     it 'returns 404 if the issue is not found' do
       post api("/projects/#{project.id}/issues/123/subscription", user)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it 'returns 404 if the issue is confidential' do
       post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -657,26 +799,26 @@
     it 'unsubscribes from an issue' do
       delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['subscribed']).to eq(false)
     end
 
     it 'returns 304 if not subscribed' do
       delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2)
 
-      expect(response.status).to eq(304)
+      expect(response).to have_http_status(304)
     end
 
     it 'returns 404 if the issue is not found' do
       delete api("/projects/#{project.id}/issues/123/subscription", user)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it 'returns 404 if the issue is confidential' do
       delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 end
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index d2b87f887128f66d84bbbbe58c688ca996ca78ed..1861882d59ecb8ec3be42d817aa0a036061e2f83 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -14,14 +14,14 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         get api("/keys/#{key.id}")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context 'when authenticated' do
       it 'should return 404 for non-existing key' do
         get api('/keys/999999', admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 Not found')
       end
 
@@ -29,7 +29,7 @@
         user.keys << key
         user.save
         get api("/keys/#{key.id}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq(key.title)
         expect(json_response['user']['id']).to eq(user.id)
         expect(json_response['user']['username']).to eq(user.username)
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index b2c7f8d9acb886061760605f36e04ac4b7bf885b..3973677998639cd1e76bf89da028857e287ad36b 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -15,7 +15,7 @@
   describe 'GET /projects/:id/labels' do
     it 'should return project labels' do
       get api("/projects/#{project.id}/labels", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.size).to eq(1)
       expect(json_response.first['name']).to eq(label1.name)
@@ -28,7 +28,7 @@
            name: 'Foo',
            color: '#FFAABB',
            description: 'test'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['name']).to eq('Foo')
       expect(json_response['color']).to eq('#FFAABB')
       expect(json_response['description']).to eq('test')
@@ -38,7 +38,7 @@
       post api("/projects/#{project.id}/labels", user),
            name: 'Foo',
            color: '#FFAABB'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['name']).to eq('Foo')
       expect(json_response['color']).to eq('#FFAABB')
       expect(json_response['description']).to be_nil
@@ -46,19 +46,19 @@
 
     it 'should return a 400 bad request if name not given' do
       post api("/projects/#{project.id}/labels", user), color: '#FFAABB'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return a 400 bad request if color not given' do
       post api("/projects/#{project.id}/labels", user), name: 'Foobar'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 for invalid color' do
       post api("/projects/#{project.id}/labels", user),
            name: 'Foo',
            color: '#FFAA'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['color']).to eq(['must be a valid color code'])
     end
 
@@ -66,7 +66,7 @@
       post api("/projects/#{project.id}/labels", user),
            name: 'Foo',
            color: '#FFAAFFFF'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['color']).to eq(['must be a valid color code'])
     end
 
@@ -74,7 +74,7 @@
       post api("/projects/#{project.id}/labels", user),
            name: '?',
            color: '#FFAABB'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['title']).to eq(['is invalid'])
     end
 
@@ -82,7 +82,7 @@
       post api("/projects/#{project.id}/labels", user),
            name: 'label1',
            color: '#FFAABB'
-      expect(response.status).to eq(409)
+      expect(response).to have_http_status(409)
       expect(json_response['message']).to eq('Label already exists')
     end
   end
@@ -90,18 +90,18 @@
   describe 'DELETE /projects/:id/labels' do
     it 'should return 200 for existing label' do
       delete api("/projects/#{project.id}/labels", user), name: 'label1'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it 'should return 404 for non existing label' do
       delete api("/projects/#{project.id}/labels", user), name: 'label2'
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Label Not Found')
     end
 
     it 'should return 400 for wrong parameters' do
       delete api("/projects/#{project.id}/labels", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
@@ -112,7 +112,7 @@
           new_name: 'New Label',
           color: '#FFFFFF',
           description: 'test'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq('New Label')
       expect(json_response['color']).to eq('#FFFFFF')
       expect(json_response['description']).to eq('test')
@@ -122,7 +122,7 @@
       put api("/projects/#{project.id}/labels", user),
           name: 'label1',
           new_name: 'New Label'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq('New Label')
       expect(json_response['color']).to eq(label1.color)
     end
@@ -131,7 +131,7 @@
       put api("/projects/#{project.id}/labels", user),
           name: 'label1',
           color: '#FFFFFF'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq(label1.name)
       expect(json_response['color']).to eq('#FFFFFF')
     end
@@ -140,7 +140,7 @@
       put api("/projects/#{project.id}/labels", user),
           name: 'label1',
           description: 'test'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq(label1.name)
       expect(json_response['description']).to eq('test')
     end
@@ -149,18 +149,18 @@
       put api("/projects/#{project.id}/labels", user),
           name: 'label2',
           new_name: 'label3'
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it 'should return 400 if no label name given' do
       put api("/projects/#{project.id}/labels", user), new_name: 'label2'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "name" not given')
     end
 
     it 'should return 400 if no new parameters given' do
       put api("/projects/#{project.id}/labels", user), name: 'label1'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Required parameters '\
                                          '"new_name" or "color" missing')
     end
@@ -170,7 +170,7 @@
           name: 'label1',
           new_name: '?',
           color: '#FFFFFF'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['title']).to eq(['is invalid'])
     end
 
@@ -178,7 +178,7 @@
       put api("/projects/#{project.id}/labels", user),
           name: 'label1',
           color: '#FF'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['color']).to eq(['must be a valid color code'])
     end
 
@@ -186,7 +186,7 @@
       post api("/projects/#{project.id}/labels", user),
            name: 'Foo',
            color: '#FFAAFFFF'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['color']).to eq(['must be a valid color code'])
     end
   end
@@ -196,7 +196,7 @@
       it "should subscribe to the label" do
         post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response["name"]).to eq(label1.title)
         expect(json_response["subscribed"]).to be_truthy
       end
@@ -206,7 +206,7 @@
       it "should subscribe to the label" do
         post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response["name"]).to eq(label1.title)
         expect(json_response["subscribed"]).to be_truthy
       end
@@ -218,7 +218,7 @@
       it "should return 304" do
         post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
 
-        expect(response.status).to eq(304)
+        expect(response).to have_http_status(304)
       end
     end
 
@@ -226,7 +226,7 @@
       it "should a return 404 error" do
         post api("/projects/#{project.id}/labels/1234/subscription", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -238,7 +238,7 @@
       it "should unsubscribe from the label" do
         delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response["name"]).to eq(label1.title)
         expect(json_response["subscribed"]).to be_falsey
       end
@@ -248,7 +248,7 @@
       it "should unsubscribe from the label" do
         delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response["name"]).to eq(label1.title)
         expect(json_response["subscribed"]).to be_falsey
       end
@@ -260,7 +260,7 @@
       it "should return 304" do
         delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
 
-        expect(response.status).to eq(304)
+        expect(response).to have_http_status(304)
       end
     end
 
@@ -268,7 +268,7 @@
       it "should a return 404 error" do
         delete api("/projects/#{project.id}/labels/1234/subscription", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
diff --git a/spec/requests/api/licenses_spec.rb b/spec/requests/api/license_templates_spec.rb
similarity index 96%
rename from spec/requests/api/licenses_spec.rb
rename to spec/requests/api/license_templates_spec.rb
index 3726b2f56885b63679d2ff62ad1fede756147560..9a1894d63a2d8d73397792bed02f8ef78d97de00 100644
--- a/spec/requests/api/licenses_spec.rb
+++ b/spec/requests/api/license_templates_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe API::Licenses, api: true  do
+describe API::API, api: true  do
   include ApiHelpers
 
   describe 'Entity' do
@@ -23,7 +23,7 @@
     it 'returns a list of available license templates' do
       get api('/licenses')
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.size).to eq(15)
       expect(json_response.map { |l| l['key'] }).to include('agpl-3.0')
@@ -34,7 +34,7 @@
         it 'returns a list of available popular license templates' do
           get api('/licenses?popular=1')
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.size).to eq(3)
           expect(json_response.map { |l| l['key'] }).to include('apache-2.0')
@@ -116,7 +116,7 @@
         let(:license_type) { 'muth-over9000' }
 
         it 'returns a 404' do
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 5896b93603f4afcf572582cda8bcd67d6fde0934..61e897edf8751afa22265df08cbe2eaed03125bb 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -22,14 +22,14 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/projects/#{project.id}/merge_requests")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context "when authenticated" do
       it "should return an array of all merge_requests" do
         get api("/projects/#{project.id}/merge_requests", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(3)
         expect(json_response.last['title']).to eq(merge_request.title)
@@ -37,7 +37,7 @@
 
       it "should return an array of all merge_requests" do
         get api("/projects/#{project.id}/merge_requests?state", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(3)
         expect(json_response.last['title']).to eq(merge_request.title)
@@ -45,7 +45,7 @@
 
       it "should return an array of open merge_requests" do
         get api("/projects/#{project.id}/merge_requests?state=opened", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.last['title']).to eq(merge_request.title)
@@ -53,7 +53,7 @@
 
       it "should return an array of closed merge_requests" do
         get api("/projects/#{project.id}/merge_requests?state=closed", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['title']).to eq(merge_request_closed.title)
@@ -61,7 +61,7 @@
 
       it "should return an array of merged merge_requests" do
         get api("/projects/#{project.id}/merge_requests?state=merged", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.length).to eq(1)
         expect(json_response.first['title']).to eq(merge_request_merged.title)
@@ -75,7 +75,7 @@
 
         it "should return an array of merge_requests in ascending order" do
           get api("/projects/#{project.id}/merge_requests?sort=asc", user)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(3)
           response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
@@ -84,7 +84,7 @@
 
         it "should return an array of merge_requests in descending order" do
           get api("/projects/#{project.id}/merge_requests?sort=desc", user)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(3)
           response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
@@ -93,7 +93,7 @@
 
         it "should return an array of merge_requests ordered by updated_at" do
           get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(3)
           response_dates = json_response.map{ |merge_request| merge_request['updated_at'] }
@@ -102,7 +102,7 @@
 
         it "should return an array of merge_requests ordered by created_at" do
           get api("/projects/#{project.id}/merge_requests?order_by=created_at&sort=asc", user)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(3)
           response_dates = json_response.map{ |merge_request| merge_request['created_at'] }
@@ -116,7 +116,7 @@
     it 'exposes known attributes' do
       get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['id']).to eq(merge_request.id)
       expect(json_response['iid']).to eq(merge_request.iid)
       expect(json_response['project_id']).to eq(merge_request.project.id)
@@ -142,7 +142,7 @@
 
     it "should return merge_request" do
       get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq(merge_request.title)
       expect(json_response['iid']).to eq(merge_request.iid)
       expect(json_response['work_in_progress']).to eq(false)
@@ -159,7 +159,7 @@
 
     it "should return a 404 error if merge_request_id not found" do
       get api("/projects/#{project.id}/merge_requests/999", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     context 'Work in Progress' do
@@ -167,7 +167,7 @@
 
       it "should return merge_request" do
         get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['work_in_progress']).to eq(true)
       end
     end
@@ -186,7 +186,7 @@
 
     it 'returns a 404 when merge_request_id not found' do
       get api("/projects/#{project.id}/merge_requests/999/commits", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -199,7 +199,7 @@
 
     it 'returns a 404 when merge_request_id not found' do
       get api("/projects/#{project.id}/merge_requests/999/changes", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -213,7 +213,7 @@
              author: user,
              labels: 'label, label2',
              milestone_id: milestone.id
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['title']).to eq('Test merge_request')
         expect(json_response['labels']).to eq(['label', 'label2'])
         expect(json_response['milestone']['id']).to eq(milestone.id)
@@ -222,25 +222,25 @@
       it "should return 422 when source_branch equals target_branch" do
         post api("/projects/#{project.id}/merge_requests", user),
         title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
-        expect(response.status).to eq(422)
+        expect(response).to have_http_status(422)
       end
 
       it "should return 400 when source_branch is missing" do
         post api("/projects/#{project.id}/merge_requests", user),
         title: "Test merge_request", target_branch: "master", author: user
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return 400 when target_branch is missing" do
         post api("/projects/#{project.id}/merge_requests", user),
         title: "Test merge_request", source_branch: "markdown", author: user
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return 400 when title is missing" do
         post api("/projects/#{project.id}/merge_requests", user),
         target_branch: 'master', source_branch: 'markdown'
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it 'should return 400 on invalid label names' do
@@ -250,7 +250,7 @@
              target_branch: 'master',
              author: user,
              labels: 'label, ?'
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']['labels']['?']['title']).to eq(
           ['is invalid']
         )
@@ -274,7 +274,7 @@
                  target_branch: 'master',
                  author: user
           end.to change { MergeRequest.count }.by(0)
-          expect(response.status).to eq(409)
+          expect(response).to have_http_status(409)
         end
       end
     end
@@ -292,7 +292,7 @@
         post api("/projects/#{fork_project.id}/merge_requests", user2),
           title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master",
           author: user2, target_project_id: project.id, description: 'Test description for Test merge_request'
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['title']).to eq('Test merge_request')
         expect(json_response['description']).to eq('Test description for Test merge_request')
       end
@@ -303,26 +303,26 @@
         expect(fork_project.forked_from_project).to eq(project)
         post api("/projects/#{fork_project.id}/merge_requests", user2),
         title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['title']).to eq('Test merge_request')
       end
 
       it "should return 400 when source_branch is missing" do
         post api("/projects/#{fork_project.id}/merge_requests", user2),
         title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return 400 when target_branch is missing" do
         post api("/projects/#{fork_project.id}/merge_requests", user2),
         title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return 400 when title is missing" do
         post api("/projects/#{fork_project.id}/merge_requests", user2),
         target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       context 'when target_branch is specified' do
@@ -333,7 +333,7 @@
                source_branch: 'markdown',
                author: user,
                target_project_id: fork_project.id
-          expect(response.status).to eq(422)
+          expect(response).to have_http_status(422)
         end
 
         it 'should return 422 if targeting a different fork' do
@@ -343,14 +343,14 @@
                source_branch: 'markdown',
                author: user2,
                target_project_id: unrelated_project.id
-          expect(response.status).to eq(422)
+          expect(response).to have_http_status(422)
         end
       end
 
       it "should return 201 when target_branch is specified and for the same project" do
         post api("/projects/#{fork_project.id}/merge_requests", user2),
         title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
       end
     end
   end
@@ -365,7 +365,7 @@
 
       it "denies the deletion of the merge request" do
         delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer)
-        expect(response.status).to be(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -373,7 +373,7 @@
       it "destroys the merge request owners can destroy" do
         delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
     end
   end
@@ -381,7 +381,7 @@
   describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
     it "should return merge_request" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['state']).to eq('closed')
     end
   end
@@ -392,7 +392,7 @@
     it "should return merge_request in case of success" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return 406 if branch can't be merged" do
@@ -401,21 +401,21 @@
 
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
 
-      expect(response.status).to eq(406)
+      expect(response).to have_http_status(406)
       expect(json_response['message']).to eq('Branch cannot be merged')
     end
 
     it "should return 405 if merge_request is not open" do
       merge_request.close
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
       expect(json_response['message']).to eq('405 Method Not Allowed')
     end
 
     it "should return 405 if merge_request is a work in progress" do
       merge_request.update_attribute(:title, "WIP: #{merge_request.title}")
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
       expect(json_response['message']).to eq('405 Method Not Allowed')
     end
 
@@ -424,7 +424,7 @@
 
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
 
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
       expect(json_response['message']).to eq('405 Method Not Allowed')
     end
 
@@ -432,21 +432,21 @@
       user2 = create(:user)
       project.team << [user2, :reporter]
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user2)
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
       expect(json_response['message']).to eq('401 Unauthorized')
     end
 
     it "returns 409 if the SHA parameter doesn't match" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha.succ
 
-      expect(response.status).to eq(409)
+      expect(response).to have_http_status(409)
       expect(json_response['message']).to start_with('SHA does not match HEAD of source branch')
     end
 
     it "succeeds if the SHA parameter matches" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "enables merge when build succeeds if the ci is active" do
@@ -455,7 +455,7 @@
 
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq('Test')
       expect(json_response['merge_when_build_succeeds']).to eq(true)
     end
@@ -464,31 +464,31 @@
   describe "PUT /projects/:id/merge_requests/:merge_request_id" do
     it "updates title and returns merge_request" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title"
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq('New title')
     end
 
     it "updates description and returns merge_request" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), description: "New description"
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['description']).to eq('New description')
     end
 
     it "updates milestone_id and returns merge_request" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), milestone_id: milestone.id
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['milestone']['id']).to eq(milestone.id)
     end
 
     it "should return 400 when source_branch is specified" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
       source_branch: "master", target_branch: "master"
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return merge_request with renamed target_branch" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki"
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['target_branch']).to eq('wiki')
     end
 
@@ -497,7 +497,7 @@
               user),
           title: 'new issue',
           labels: 'label, ?'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
     end
   end
@@ -507,7 +507,7 @@
       original_count = merge_request.notes.size
 
       post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), note: "My comment"
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['note']).to eq('My comment')
       expect(json_response['author']['name']).to eq(user.name)
       expect(json_response['author']['username']).to eq(user.username)
@@ -516,20 +516,20 @@
 
     it "should return 400 if note is missing" do
       post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return 404 if note is attached to non existent merge request" do
       post api("/projects/#{project.id}/merge_requests/404/comments", user),
            note: 'My comment'
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
   describe "GET :id/merge_requests/:merge_request_id/comments" do
     it "should return merge_request comments ordered by created_at" do
       get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(2)
       expect(json_response.first['note']).to eq("a comment on a MR")
@@ -539,7 +539,7 @@
 
     it "should return a 404 error if merge_request_id not found" do
       get api("/projects/#{project.id}/merge_requests/999/comments", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -551,7 +551,7 @@
       end
 
       get api("/projects/#{project.id}/merge_requests/#{mr.id}/closes_issues", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['id']).to eq(issue.id)
@@ -559,7 +559,7 @@
 
     it 'returns an empty array when there are no issues to be closed' do
       get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(0)
     end
@@ -572,7 +572,7 @@
 
       get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.id}/closes_issues", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['title']).to eq(issue.title)
@@ -584,20 +584,20 @@
     it 'subscribes to a merge request' do
       post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin)
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['subscribed']).to eq(true)
     end
 
     it 'returns 304 if already subscribed' do
       post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user)
 
-      expect(response.status).to eq(304)
+      expect(response).to have_http_status(304)
     end
 
     it 'returns 404 if the merge request is not found' do
       post api("/projects/#{project.id}/merge_requests/123/subscription", user)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -605,20 +605,20 @@
     it 'unsubscribes from a merge request' do
       delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['subscribed']).to eq(false)
     end
 
     it 'returns 304 if not subscribed' do
       delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin)
 
-      expect(response.status).to eq(304)
+      expect(response).to have_http_status(304)
     end
 
     it 'returns 404 if the merge request is not found' do
       post api("/projects/#{project.id}/merge_requests/123/subscription", user)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 0154d1c62cc02f4bd9661d4907e1c3435bc13234..0f4e38b24750dd32c3034cbba7f5e316b6d711a5 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -12,20 +12,20 @@
   describe 'GET /projects/:id/milestones' do
     it 'should return project milestones' do
       get api("/projects/#{project.id}/milestones", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.first['title']).to eq(milestone.title)
     end
 
     it 'should return a 401 error if user not authenticated' do
       get api("/projects/#{project.id}/milestones")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it 'returns an array of active milestones' do
       get api("/projects/#{project.id}/milestones?state=active", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['id']).to eq(milestone.id)
@@ -34,7 +34,7 @@
     it 'returns an array of closed milestones' do
       get api("/projects/#{project.id}/milestones?state=closed", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(1)
       expect(json_response.first['id']).to eq(closed_milestone.id)
@@ -44,7 +44,7 @@
   describe 'GET /projects/:id/milestones/:milestone_id' do
     it 'should return a project milestone by id' do
       get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq(milestone.title)
       expect(json_response['iid']).to eq(milestone.iid)
     end
@@ -60,19 +60,19 @@
 
     it 'should return 401 error if user not authenticated' do
       get api("/projects/#{project.id}/milestones/#{milestone.id}")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it 'should return a 404 error if milestone id not found' do
       get api("/projects/#{project.id}/milestones/1234", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
   describe 'POST /projects/:id/milestones' do
     it 'should create a new project milestone' do
       post api("/projects/#{project.id}/milestones", user), title: 'new milestone'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['title']).to eq('new milestone')
       expect(json_response['description']).to be_nil
     end
@@ -80,14 +80,14 @@
     it 'should create a new project milestone with description and due date' do
       post api("/projects/#{project.id}/milestones", user),
         title: 'new milestone', description: 'release', due_date: '2013-03-02'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['description']).to eq('release')
       expect(json_response['due_date']).to eq('2013-03-02')
     end
 
     it 'should return a 400 error if title is missing' do
       post api("/projects/#{project.id}/milestones", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
@@ -95,14 +95,14 @@
     it 'should update a project milestone' do
       put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
         title: 'updated title'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq('updated title')
     end
 
     it 'should return a 404 error if milestone id not found' do
       put api("/projects/#{project.id}/milestones/1234", user),
         title: 'updated title'
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -110,7 +110,7 @@
     it 'should update a project milestone' do
       put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
         state_event: 'close'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
 
       expect(json_response['state']).to eq('closed')
     end
@@ -131,14 +131,14 @@
     end
     it 'should return project issues for a particular milestone' do
       get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.first['milestone']['title']).to eq(milestone.title)
     end
 
     it 'should return a 401 error if user not authenticated' do
       get api("/projects/#{project.id}/milestones/#{milestone.id}/issues")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     describe 'confidential issues' do
@@ -155,7 +155,7 @@
       it 'returns confidential issues to team members' do
         get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.size).to eq(2)
         expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
@@ -167,7 +167,7 @@
 
         get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.size).to eq(1)
         expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
@@ -176,7 +176,7 @@
       it 'does not return confidential issues to regular users' do
         get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user))
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.size).to eq(1)
         expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 21787fdd89507a1fae215d2aa1a9ac7f47bc5332..237b4b17eb5c636dab922e9bf792a9b94888349e 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -11,14 +11,14 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/namespaces")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context "when authenticated as admin" do
       it "admin: should return an array of all namespaces" do
         get api("/namespaces", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
 
         expect(json_response.length).to eq(Namespace.count)
@@ -26,7 +26,7 @@
 
       it "admin: should return an array of matched namespaces" do
         get api("/namespaces?search=#{group1.name}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
 
         expect(json_response.length).to eq(1)
@@ -36,7 +36,7 @@
     context "when authenticated as a regular user" do
       it "user: should return an array of namespaces" do
         get api("/namespaces", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
 
         expect(json_response.length).to eq(1)
@@ -44,7 +44,7 @@
 
       it "admin: should return an array of matched namespaces" do
         get api("/namespaces?search=#{user.username}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
 
         expect(json_response.length).to eq(1)
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index beb29a686920987541bb3b3f24c0b0d49934ba71..bacd01f8e748dded436dec1e571abef63af88cc4 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -40,7 +40,7 @@
       it "should return an array of issue notes" do
         get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['body']).to eq(issue_note.note)
       end
@@ -48,14 +48,14 @@
       it "should return a 404 error when issue id not found" do
         get api("/projects/#{project.id}/issues/12345/notes", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       context "and current user cannot view the notes" do
         it "should return an empty array" do
           get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response).to be_empty
         end
@@ -66,7 +66,7 @@
           it "returns 404" do
             get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
 
@@ -74,7 +74,7 @@
           it "should return an empty array" do
             get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user)
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
             expect(json_response).to be_an Array
             expect(json_response.first['body']).to eq(cross_reference_note.note)
           end
@@ -86,7 +86,7 @@
       it "should return an array of snippet notes" do
         get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['body']).to eq(snippet_note.note)
       end
@@ -94,13 +94,13 @@
       it "should return a 404 error when snippet id not found" do
         get api("/projects/#{project.id}/snippets/42/notes", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "returns 404 when not authorized" do
         get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -108,7 +108,7 @@
       it "should return an array of merge_requests notes" do
         get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['body']).to eq(merge_request_note.note)
       end
@@ -116,13 +116,13 @@
       it "should return a 404 error if merge request id not found" do
         get api("/projects/#{project.id}/merge_requests/4444/notes", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "returns 404 when not authorized" do
         get api("/projects/#{project.id}/merge_requests/4444/notes", private_user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -132,21 +132,21 @@
       it "should return an issue note by id" do
         get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['body']).to eq(issue_note.note)
       end
 
       it "should return a 404 error if issue note not found" do
         get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       context "and current user cannot view the note" do
         it "should return a 404 error" do
           get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user)
 
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
 
         context "when issue is confidential" do
@@ -155,7 +155,7 @@
           it "returns 404" do
             get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user)
 
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
 
@@ -164,7 +164,7 @@
           it "should return an issue note by id" do
             get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user)
 
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
             expect(json_response['body']).to eq(cross_reference_note.note)
           end
         end
@@ -175,14 +175,14 @@
       it "should return a snippet note by id" do
         get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['body']).to eq(snippet_note.note)
       end
 
       it "should return a 404 error if snippet note not found" do
         get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -192,7 +192,7 @@
       it "should create a new issue note" do
         post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['body']).to eq('hi!')
         expect(json_response['author']['username']).to eq(user.username)
       end
@@ -200,13 +200,13 @@
       it "should return a 400 bad request error if body not given" do
         post api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return a 401 unauthorized error if user not authenticated" do
         post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
 
       context 'when an admin or owner makes the request' do
@@ -215,7 +215,7 @@
           post api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
             body: 'hi!', created_at: creation_time
 
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           expect(json_response['body']).to eq('hi!')
           expect(json_response['author']['username']).to eq(user.username)
           expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
@@ -228,7 +228,7 @@
       it "should create a new snippet note" do
         post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!'
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['body']).to eq('hi!')
         expect(json_response['author']['username']).to eq(user.username)
       end
@@ -236,13 +236,13 @@
       it "should return a 400 bad request error if body not given" do
         post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it "should return a 401 unauthorized error if user not authenticated" do
         post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!'
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -282,7 +282,7 @@
         put api("/projects/#{project.id}/issues/#{issue.id}/"\
                   "notes/#{issue_note.id}", user), body: 'Hello!'
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['body']).to eq('Hello!')
       end
 
@@ -290,14 +290,14 @@
         put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user),
                 body: 'Hello!'
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should return a 400 bad request error if body not given' do
         put api("/projects/#{project.id}/issues/#{issue.id}/"\
                   "notes/#{issue_note.id}", user)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
     end
 
@@ -306,7 +306,7 @@
         put api("/projects/#{project.id}/snippets/#{snippet.id}/"\
                   "notes/#{snippet_note.id}", user), body: 'Hello!'
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['body']).to eq('Hello!')
       end
 
@@ -314,7 +314,7 @@
         put api("/projects/#{project.id}/snippets/#{snippet.id}/"\
                   "notes/12345", user), body: "Hello!"
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -323,7 +323,7 @@
         put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
                   "notes/#{merge_request_note.id}", user), body: 'Hello!'
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['body']).to eq('Hello!')
       end
 
@@ -331,7 +331,7 @@
         put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
                   "notes/12345", user), body: "Hello!"
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -342,17 +342,17 @@
         delete api("/projects/#{project.id}/issues/#{issue.id}/"\
                    "notes/#{issue_note.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         # Check if note is really deleted
         delete api("/projects/#{project.id}/issues/#{issue.id}/"\
                    "notes/#{issue_note.id}", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'returns a 404 error when note id not found' do
         delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -361,18 +361,18 @@
         delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\
                    "notes/#{snippet_note.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         # Check if note is really deleted
         delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\
                    "notes/#{snippet_note.id}", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'returns a 404 error when note id not found' do
         delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\
                    "notes/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -381,18 +381,18 @@
         delete api("/projects/#{project.id}/merge_requests/"\
                    "#{merge_request.id}/notes/#{merge_request_note.id}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         # Check if note is really deleted
         delete api("/projects/#{project.id}/merge_requests/"\
                    "#{merge_request.id}/notes/#{merge_request_note.id}", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'returns a 404 error when note id not found' do
         delete api("/projects/#{project.id}/merge_requests/"\
                    "#{merge_request.id}/notes/12345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index ffb93bbb120d1d37f9a8ae6ebeb7c989c66b19c5..fd1fffa6223264332085a00cc73e4c0747b1453d 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -22,7 +22,7 @@
     context "authorized user" do
       it "should return project hooks" do
         get api("/projects/#{project.id}/hooks", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
 
         expect(json_response).to be_an Array
         expect(json_response.count).to eq(1)
@@ -40,7 +40,7 @@
     context "unauthorized user" do
       it "should not access project hooks" do
         get api("/projects/#{project.id}/hooks", user3)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
   end
@@ -49,7 +49,7 @@
     context "authorized user" do
       it "should return a project hook" do
         get api("/projects/#{project.id}/hooks/#{hook.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['url']).to eq(hook.url)
         expect(json_response['issues_events']).to eq(hook.issues_events)
         expect(json_response['push_events']).to eq(hook.push_events)
@@ -61,20 +61,20 @@
 
       it "should return a 404 error if hook id is not available" do
         get api("/projects/#{project.id}/hooks/1234", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context "unauthorized user" do
       it "should not access an existing hook" do
         get api("/projects/#{project.id}/hooks/#{hook.id}", user3)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
     it "should return a 404 error if hook id is not available" do
       get api("/projects/#{project.id}/hooks/1234", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -83,7 +83,7 @@
       expect do
         post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true
       end.to change {project.hooks.count}.by(1)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['url']).to eq('http://example.com')
       expect(json_response['issues_events']).to eq(true)
       expect(json_response['push_events']).to eq(true)
@@ -96,12 +96,12 @@
 
     it "should return a 400 error if url not given" do
       post api("/projects/#{project.id}/hooks", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 422 error if url not valid" do
       post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
-      expect(response.status).to eq(422)
+      expect(response).to have_http_status(422)
     end
   end
 
@@ -109,7 +109,7 @@
     it "should update an existing project hook" do
       put api("/projects/#{project.id}/hooks/#{hook.id}", user),
         url: 'http://example.org', push_events: false
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['url']).to eq('http://example.org')
       expect(json_response['issues_events']).to eq(hook.issues_events)
       expect(json_response['push_events']).to eq(false)
@@ -121,17 +121,17 @@
 
     it "should return 404 error if hook id not found" do
       put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return 400 error if url is not given" do
       put api("/projects/#{project.id}/hooks/#{hook.id}", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 422 error if url is not valid" do
       put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
-      expect(response.status).to eq(422)
+      expect(response).to have_http_status(422)
     end
   end
 
@@ -140,22 +140,22 @@
       expect do
         delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
       end.to change {project.hooks.count}.by(-1)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return success when deleting hook" do
       delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return a 404 error when deleting non existent hook" do
       delete api("/projects/#{project.id}/hooks/42", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return a 405 error if hook id not given" do
       delete api("/projects/#{project.id}/hooks", user)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
     end
 
     it "shold return a 404 if a user attempts to delete project hooks he/she does not own" do
@@ -164,7 +164,7 @@
       other_project.team << [test_user, :master]
 
       delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(WebHook.exists?(hook.id)).to be_truthy
     end
   end
diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb
index 44b532b10e142fb588d05f75f80a47ca9edbb24f..9a7c1da44018b4da1d293ce783efa32be27c4f45 100644
--- a/spec/requests/api/project_members_spec.rb
+++ b/spec/requests/api/project_members_spec.rb
@@ -15,7 +15,7 @@
 
     it "should return project team members" do
       get api("/projects/#{project.id}/members", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.count).to eq(2)
       expect(json_response.map { |u| u['username'] }).to include user.username
@@ -23,7 +23,7 @@
 
     it "finds team members with query string" do
       get api("/projects/#{project.id}/members", user), query: user.username
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.count).to eq(1)
       expect(json_response.first['username']).to eq(user.username)
@@ -31,7 +31,7 @@
 
     it "should return a 404 error if id not found" do
       get api("/projects/9999/members", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -40,14 +40,14 @@
 
     it "should return project team member" do
       get api("/projects/#{project.id}/members/#{user.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['username']).to eq(user.username)
       expect(json_response['access_level']).to eq(ProjectMember::MASTER)
     end
 
     it "should return a 404 error if user id not found" do
       get api("/projects/#{project.id}/members/1234", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -57,7 +57,7 @@
         post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER
       end.to change { ProjectMember.count }.by(1)
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['username']).to eq(user2.username)
       expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER)
     end
@@ -70,24 +70,24 @@
         post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER
       end.not_to change { ProjectMember.count }
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['username']).to eq(user2.username)
       expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER)
     end
 
     it "should return a 400 error when user id is not given" do
       post api("/projects/#{project.id}/members", user), access_level: ProjectMember::MASTER
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 400 error when access level is not given" do
       post api("/projects/#{project.id}/members", user), user_id: user2.id
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 422 error when access level is not known" do
       post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234
-      expect(response.status).to eq(422)
+      expect(response).to have_http_status(422)
     end
   end
 
@@ -96,24 +96,24 @@
 
     it "should update project team member" do
       put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: ProjectMember::MASTER
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['username']).to eq(user3.username)
       expect(json_response['access_level']).to eq(ProjectMember::MASTER)
     end
 
     it "should return a 404 error if user_id is not found" do
       put api("/projects/#{project.id}/members/1234", user), access_level: ProjectMember::MASTER
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return a 400 error when access level is not given" do
       put api("/projects/#{project.id}/members/#{user3.id}", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should return a 422 error when access level is not known" do
       put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123
-      expect(response.status).to eq(422)
+      expect(response).to have_http_status(422)
     end
   end
 
@@ -134,20 +134,20 @@
       expect do
         delete api("/projects/#{project.id}/members/#{user3.id}", user)
       end.not_to change { ProjectMember.count }
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return 200 if team member already removed" do
       delete api("/projects/#{project.id}/members/#{user3.id}", user)
       delete api("/projects/#{project.id}/members/#{user3.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return 200 OK when the user was not member" do
       expect do
         delete api("/projects/#{project.id}/members/1000000", user)
       end.to change { ProjectMember.count }.by(0)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['id']).to eq(1000000)
       expect(json_response['message']).to eq('Access revoked')
     end
@@ -158,7 +158,7 @@
           delete api("/projects/#{project.id}/members/#{user3.id}", user3)
         end.to change { ProjectMember.count }.by(-1)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['id']).to eq(project_member2.id)
       end
     end
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 9706d060cfaea22c06f7b9d5cba92a0aa1562421..4ebde20194115c3b0d6590f1d835174dfa0cac50 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -27,7 +27,7 @@
 
       get api("/projects/#{project.id}/snippets/", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response.size).to eq(3)
       expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id)
     end
@@ -38,7 +38,7 @@
       create(:project_snippet, :private, project: project)
 
       get api("/projects/#{project.id}/snippets/", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response.size).to eq(0)
     end
   end
@@ -56,7 +56,7 @@
 
       post api("/projects/#{project.id}/snippets/", admin), params
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       snippet = ProjectSnippet.find(json_response['id'])
       expect(snippet.content).to eq(params[:code])
       expect(snippet.title).to eq(params[:title])
@@ -73,7 +73,7 @@
 
       put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       snippet.reload
       expect(snippet.content).to eq(new_content)
     end
@@ -86,7 +86,7 @@
 
       delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
@@ -97,7 +97,7 @@
 
       get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(response.content_type).to eq 'text/plain'
       expect(response.body).to eq(snippet.content)
     end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 01eb4b44b83ff8911c9922f1297ec094a0026eff..41b5ed9bc3384cbb71106e5231efeb91592c59e7 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -45,14 +45,14 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         get api('/projects')
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context 'when authenticated' do
       it 'should return an array of projects' do
         get api('/projects', user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['name']).to eq(project.name)
         expect(json_response.first['owner']['username']).to eq(user.username)
@@ -84,7 +84,7 @@
       context 'and using search' do
         it 'should return searched project' do
           get api('/projects', user), { search: project.name }
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(1)
         end
@@ -93,21 +93,21 @@
       context 'and using the visibility filter' do
         it 'should filter based on private visibility param' do
           get api('/projects', user), { visibility: 'private' }
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count)
         end
 
         it 'should filter based on internal visibility param' do
           get api('/projects', user), { visibility: 'internal' }
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count)
         end
 
         it 'should filter based on public visibility param' do
           get api('/projects', user), { visibility: 'public' }
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count)
         end
@@ -121,7 +121,7 @@
 
         it 'should return the correct order when sorted by id' do
           get api('/projects', user), { order_by: 'id', sort: 'desc' }
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(json_response.first['id']).to eq(project3.id)
         end
@@ -135,21 +135,21 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         get api('/projects/all')
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context 'when authenticated as regular user' do
       it 'should return authentication error' do
         get api('/projects/all', user)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
     context 'when authenticated as admin' do
       it 'should return an array of all projects' do
         get api('/projects/all', admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
 
         expect(json_response).to satisfy do |response|
@@ -173,7 +173,7 @@
 
     it 'should return the starred projects viewable by the user' do
       get api('/projects/starred', user3)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
     end
@@ -185,25 +185,25 @@
         allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
         expect { post api('/projects', user2), name: 'foo' }.
           to change {Project.count}.by(0)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
     it 'should create new project without path and return 201' do
       expect { post api('/projects', user), name: 'foo' }.
         to change { Project.count }.by(1)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
     end
 
     it 'should create last project before reaching project limit' do
       allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
       post api('/projects', user2), name: 'foo'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
     end
 
     it 'should not create new project without name and return 400' do
       expect { post api('/projects', user) }.not_to change { Project.count }
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should assign attributes to project" do
@@ -273,7 +273,7 @@
       it 'should not allow a non-admin to use a restricted visibility level' do
         post api('/projects', user), @project
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']['visibility_level'].first).to(
           match('restricted by your GitLab administrator')
         )
@@ -295,14 +295,14 @@
 
     it 'should create new project without path and return 201' do
       expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
     end
 
     it 'should respond with 400 on failure and not project' do
       expect { post api("/projects/user/#{user.id}", admin) }.
         not_to change { Project.count }
 
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['name']).to eq([
         'can\'t be blank',
         'is too short (minimum is 0 characters)',
@@ -380,7 +380,7 @@
     it "uploads the file and returns its info" do
       post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
 
-      expect(response.status).to be(201)
+      expect(response).to have_http_status(201)
       expect(json_response['alt']).to eq("dk")
       expect(json_response['url']).to start_with("/uploads/")
       expect(json_response['url']).to end_with("/dk.png")
@@ -394,27 +394,27 @@
 
     it 'should return a project by id' do
       get api("/projects/#{project.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq(project.name)
       expect(json_response['owner']['username']).to eq(user.username)
     end
 
     it 'should return a project by path name' do
       get api("/projects/#{project.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq(project.name)
     end
 
     it 'should return a 404 error if not found' do
       get api('/projects/42', user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Project Not Found')
     end
 
     it 'should return a 404 error if user is not a member' do
       other_user = create(:user)
       get api("/projects/#{project.id}", other_user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it 'should handle users with dots' do
@@ -422,7 +422,7 @@
       project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace)
 
       get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq(project.name)
     end
 
@@ -433,7 +433,7 @@
         it 'contains permission information' do
           get api("/projects", user)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response.first['permissions']['project_access']['access_level']).
               to eq(Gitlab::Access::MASTER)
           expect(json_response.first['permissions']['group_access']).to be_nil
@@ -445,7 +445,7 @@
           project.team << [user, :master]
           get api("/projects/#{project.id}", user)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['permissions']['project_access']['access_level']).
             to eq(Gitlab::Access::MASTER)
           expect(json_response['permissions']['group_access']).to be_nil
@@ -460,7 +460,7 @@
         it 'should set the owner and return 200' do
           get api("/projects/#{project2.id}", user)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['permissions']['project_access']).to be_nil
           expect(json_response['permissions']['group_access']['access_level']).
             to eq(Gitlab::Access::OWNER)
@@ -479,7 +479,7 @@
         get api("/projects/#{project.id}/events", user)
       end
 
-      it { expect(response.status).to eq(200) }
+      it { expect(response).to have_http_status(200) }
 
       context 'joined event' do
         let(:json_event) { json_response[1] }
@@ -500,14 +500,14 @@
 
     it 'should return a 404 error if not found' do
       get api('/projects/42/events', user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Project Not Found')
     end
 
     it 'should return a 404 error if user is not a member' do
       other_user = create(:user)
       get api("/projects/#{project.id}/events", other_user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -516,7 +516,7 @@
 
     it 'should return an array of project snippets' do
       get api("/projects/#{project.id}/snippets", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       expect(json_response.first['title']).to eq(snippet.title)
     end
@@ -525,13 +525,13 @@
   describe 'GET /projects/:id/snippets/:snippet_id' do
     it 'should return a project snippet' do
       get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq(snippet.title)
     end
 
     it 'should return a 404 error if snippet id not found' do
       get api("/projects/#{project.id}/snippets/1234", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -540,7 +540,7 @@
       post api("/projects/#{project.id}/snippets", user),
         title: 'api test', file_name: 'sample.rb', code: 'test',
         visibility_level: '0'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['title']).to eq('api test')
     end
 
@@ -554,7 +554,7 @@
     it 'should update an existing project snippet' do
       put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
         code: 'updated code'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq('example')
       expect(snippet.reload.content).to eq('updated code')
     end
@@ -562,7 +562,7 @@
     it 'should update an existing project snippet with new title' do
       put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
         title: 'other api test'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['title']).to eq('other api test')
     end
   end
@@ -574,24 +574,24 @@
       expect do
         delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
       end.to change { Snippet.count }.by(-1)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it 'should return 404 when deleting unknown snippet id' do
       delete api("/projects/#{project.id}/snippets/1234", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
   describe 'GET /projects/:id/snippets/:snippet_id/raw' do
     it 'should get a raw project snippet' do
       get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it 'should return a 404 error if raw project snippet not found' do
       get api("/projects/#{project.id}/snippets/5555/raw", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -604,7 +604,7 @@
 
       it 'should return array of ssh keys' do
         get api("/projects/#{project.id}/keys", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['title']).to eq(deploy_key.title)
       end
@@ -613,20 +613,20 @@
     describe 'GET /projects/:id/keys/:key_id' do
       it 'should return a single key' do
         get api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['title']).to eq(deploy_key.title)
       end
 
       it 'should return 404 Not Found with invalid ID' do
         get api("/projects/#{project.id}/keys/404", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     describe 'POST /projects/:id/keys' do
       it 'should not create an invalid ssh key' do
         post api("/projects/#{project.id}/keys", user), { title: 'invalid key' }
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']['key']).to eq([
           'can\'t be blank',
           'is too short (minimum is 0 characters)',
@@ -636,7 +636,7 @@
 
       it 'should not create a key without title' do
         post api("/projects/#{project.id}/keys", user), key: 'some key'
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']['title']).to eq([
           'can\'t be blank',
           'is too short (minimum is 0 characters)'
@@ -662,7 +662,7 @@
 
       it 'should return 404 Not Found with invalid ID' do
         delete api("/projects/#{project.id}/keys/404", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -676,13 +676,13 @@
 
       it "shouldn't available for non admin users" do
         post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it 'should allow project to be forked from an existing project' do
         expect(project_fork_target.forked?).not_to be_truthy
         post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         project_fork_target.reload
         expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
         expect(project_fork_target.forked_project_link).not_to be_nil
@@ -691,7 +691,7 @@
 
       it 'should fail if forked_from project which does not exist' do
         post api("/projects/#{project_fork_target.id}/fork/9999", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should fail with 409 if already forked' do
@@ -699,7 +699,7 @@
         project_fork_target.reload
         expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
         post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         project_fork_target.reload
         expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
         expect(project_fork_target.forked?).to be_truthy
@@ -710,7 +710,7 @@
 
       it "shouldn't be visible to users outside group" do
         delete api("/projects/#{project_fork_target.id}/fork", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       context 'when users belong to project group' do
@@ -723,7 +723,7 @@
 
         it 'should be forbidden to non-owner users' do
           delete api("/projects/#{project_fork_target.id}/fork", user2)
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
 
         it 'should make forked project unforked' do
@@ -732,7 +732,7 @@
           expect(project_fork_target.forked_from_project).not_to be_nil
           expect(project_fork_target.forked?).to be_truthy
           delete api("/projects/#{project_fork_target.id}/fork", admin)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           project_fork_target.reload
           expect(project_fork_target.forked_from_project).to be_nil
           expect(project_fork_target.forked?).not_to be_truthy
@@ -741,7 +741,7 @@
         it 'should be idempotent if not forked' do
           expect(project_fork_target.forked_from_project).to be_nil
           delete api("/projects/#{project_fork_target.id}/fork", admin)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(project_fork_target.reload.forked_from_project).to be_nil
         end
       end
@@ -799,14 +799,14 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         get api("/projects/search/#{query}")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context 'when authenticated' do
       it 'should return an array of projects' do
         get api("/projects/search/#{query}",user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.size).to eq(6)
         json_response.each {|project| expect(project['name']).to match(/.*query.*/)}
@@ -816,7 +816,7 @@
     context 'when authenticated as a different user' do
       it 'should return matching public projects' do
         get api("/projects/search/#{query}", user2)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.size).to eq(2)
         json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)}
@@ -838,7 +838,7 @@
       it 'should return authentication error' do
         project_param = { name: 'bar' }
         put api("/projects/#{project.id}"), project_param
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -846,7 +846,7 @@
       it 'should update name' do
         project_param = { name: 'bar' }
         put api("/projects/#{project.id}", user), project_param
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_param.each_pair do |k, v|
           expect(json_response[k.to_s]).to eq(v)
         end
@@ -855,7 +855,7 @@
       it 'should update visibility_level' do
         project_param = { visibility_level: 20 }
         put api("/projects/#{project3.id}", user), project_param
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_param.each_pair do |k, v|
           expect(json_response[k.to_s]).to eq(v)
         end
@@ -866,7 +866,7 @@
 
         project_param = { public: false }
         put api("/projects/#{project3.id}", user), project_param
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_param.each_pair do |k, v|
           expect(json_response[k.to_s]).to eq(v)
         end
@@ -876,14 +876,14 @@
       it 'should not update name to existing name' do
         project_param = { name: project3.name }
         put api("/projects/#{project.id}", user), project_param
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']['name']).to eq(['has already been taken'])
       end
 
       it 'should update path & name to existing path & name in different namespace' do
         project_param = { path: project4.path, name: project4.name }
         put api("/projects/#{project3.id}", user), project_param
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_param.each_pair do |k, v|
           expect(json_response[k.to_s]).to eq(v)
         end
@@ -894,7 +894,7 @@
       it 'should update path' do
         project_param = { path: 'bar' }
         put api("/projects/#{project3.id}", user4), project_param
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_param.each_pair do |k, v|
           expect(json_response[k.to_s]).to eq(v)
         end
@@ -908,7 +908,7 @@
                           description: 'new description' }
 
         put api("/projects/#{project3.id}", user4), project_param
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project_param.each_pair do |k, v|
           expect(json_response[k.to_s]).to eq(v)
         end
@@ -917,20 +917,20 @@
       it 'should not update path to existing path' do
         project_param = { path: project.path }
         put api("/projects/#{project3.id}", user4), project_param
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']['path']).to eq(['has already been taken'])
       end
 
       it 'should not update name' do
         project_param = { name: 'bar' }
         put api("/projects/#{project3.id}", user4), project_param
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it 'should not update visibility_level' do
         project_param = { visibility_level: 20 }
         put api("/projects/#{project3.id}", user4), project_param
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -943,7 +943,7 @@
                           merge_requests_enabled: true,
                           description: 'new description' }
         put api("/projects/#{project.id}", user3), project_param
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
   end
@@ -953,7 +953,7 @@
       it 'archives the project' do
         post api("/projects/#{project.id}/archive", user)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['archived']).to be_truthy
       end
     end
@@ -966,7 +966,7 @@
       it 'remains archived' do
         post api("/projects/#{project.id}/archive", user)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['archived']).to be_truthy
       end
     end
@@ -979,7 +979,7 @@
       it 'rejects the action' do
         post api("/projects/#{project.id}/archive", user3)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
   end
@@ -989,7 +989,7 @@
       it 'remains unarchived' do
         post api("/projects/#{project.id}/unarchive", user)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['archived']).to be_falsey
       end
     end
@@ -1002,7 +1002,7 @@
       it 'unarchives the project' do
         post api("/projects/#{project.id}/unarchive", user)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['archived']).to be_falsey
       end
     end
@@ -1015,7 +1015,7 @@
       it 'rejects the action' do
         post api("/projects/#{project.id}/unarchive", user3)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
   end
@@ -1025,7 +1025,7 @@
       it 'stars the project' do
         expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['star_count']).to eq(1)
       end
     end
@@ -1039,7 +1039,7 @@
       it 'does not modify the star count' do
         expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
 
-        expect(response.status).to eq(304)
+        expect(response).to have_http_status(304)
       end
     end
   end
@@ -1054,7 +1054,7 @@
       it 'unstars the project' do
         expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['star_count']).to eq(0)
       end
     end
@@ -1063,7 +1063,7 @@
       it 'does not modify the star count' do
         expect { delete api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
 
-        expect(response.status).to eq(304)
+        expect(response).to have_http_status(304)
       end
     end
   end
@@ -1072,36 +1072,36 @@
     context 'when authenticated as user' do
       it 'should remove project' do
         delete api("/projects/#{project.id}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should not remove a project if not an owner' do
         user3 = create(:user)
         project.team << [user3, :developer]
         delete api("/projects/#{project.id}", user3)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it 'should not remove a non existing project' do
         delete api('/projects/1328', user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should not remove a project not attached to user' do
         delete api("/projects/#{project.id}", user2)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
     context 'when authenticated as admin' do
       it 'should remove any existing project' do
         delete api("/projects/#{project.id}", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should not remove a non existing project' do
         delete api('/projects/1328', admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 7cf4a01d76b6e903199bf1d7335b7ad3d24913f8..5890e9c9d3d97f3ef431d2f447120399aec790d5 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -18,7 +18,7 @@
 
       it "should return project commits" do
         get api("/projects/#{project.id}/repository/tree", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
 
         expect(json_response).to be_an Array
         expect(json_response.first['name']).to eq('encoding')
@@ -28,7 +28,7 @@
 
       it 'should return a 404 for unknown ref' do
         get api("/projects/#{project.id}/repository/tree?ref_name=foo", user)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
 
         expect(json_response).to be_an Object
         json_response['message'] == '404 Tree Not Found'
@@ -38,7 +38,7 @@
     context "unauthorized user" do
       it "should not return project commits" do
         get api("/projects/#{project.id}/repository/tree")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -46,41 +46,41 @@
   describe "GET /projects/:id/repository/blobs/:sha" do
     it "should get the raw file contents" do
       get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return 404 for invalid branch_name" do
       get api("/projects/#{project.id}/repository/blobs/invalid_branch_name?filepath=README.md", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return 404 for invalid file" do
       get api("/projects/#{project.id}/repository/blobs/master?filepath=README.invalid", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
 
     it "should return a 400 error if filepath is missing" do
       get api("/projects/#{project.id}/repository/blobs/master", user)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
   describe "GET /projects/:id/repository/commits/:sha/blob" do
     it "should get the raw file contents" do
       get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 
   describe "GET /projects/:id/repository/raw_blobs/:sha" do
     it "should get the raw file contents" do
       get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it 'should return a 404 for unknown blob' do
       get api("/projects/#{project.id}/repository/raw_blobs/123456", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
 
       expect(json_response).to be_an Object
       json_response['message'] == '404 Blob Not Found'
@@ -91,7 +91,7 @@
     it "should get the archive" do
       get api("/projects/#{project.id}/repository/archive", user)
       repo_name = project.repository.name.gsub("\.git", "")
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       type, params = workhorse_send_data
       expect(type).to eq('git-archive')
       expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/)
@@ -100,7 +100,7 @@
     it "should get the archive.zip" do
       get api("/projects/#{project.id}/repository/archive.zip", user)
       repo_name = project.repository.name.gsub("\.git", "")
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       type, params = workhorse_send_data
       expect(type).to eq('git-archive')
       expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/)
@@ -109,7 +109,7 @@
     it "should get the archive.tar.bz2" do
       get api("/projects/#{project.id}/repository/archive.tar.bz2", user)
       repo_name = project.repository.name.gsub("\.git", "")
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       type, params = workhorse_send_data
       expect(type).to eq('git-archive')
       expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/)
@@ -117,28 +117,28 @@
 
     it "should return 404 for invalid sha" do
       get api("/projects/#{project.id}/repository/archive/?sha=xxx", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
   describe 'GET /projects/:id/repository/compare' do
     it "should compare branches" do
       get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'feature'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['commits']).to be_present
       expect(json_response['diffs']).to be_present
     end
 
     it "should compare tags" do
       get api("/projects/#{project.id}/repository/compare", user), from: 'v1.0.0', to: 'v1.1.0'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['commits']).to be_present
       expect(json_response['diffs']).to be_present
     end
 
     it "should compare commits" do
       get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.id, to: sample_commit.parent_id
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['commits']).to be_empty
       expect(json_response['diffs']).to be_empty
       expect(json_response['compare_same_ref']).to be_falsey
@@ -146,14 +146,14 @@
 
     it "should compare commits in reverse order" do
       get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.parent_id, to: sample_commit.id
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['commits']).to be_present
       expect(json_response['diffs']).to be_present
     end
 
     it "should compare same refs" do
       get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'master'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['commits']).to be_empty
       expect(json_response['diffs']).to be_empty
       expect(json_response['compare_same_ref']).to be_truthy
@@ -163,7 +163,7 @@
   describe 'GET /projects/:id/repository/contributors' do
     it 'should return valid data' do
       get api("/projects/#{project.id}/repository/contributors", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Array
       contributor = json_response.first
       expect(contributor['email']).to eq('dmitriy.zaporozhets@gmail.com')
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index b4c826522a5d09a45f3f6f945cbd83386b6e5929..00a3c917b6a3e3fd1c6499546e9cde412d98104a 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -39,7 +39,7 @@
         get api('/runners', user)
         shared = json_response.any?{ |r| r['is_shared'] }
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(shared).to be_falsey
       end
@@ -48,14 +48,14 @@
         get api('/runners?scope=active', user)
         shared = json_response.any?{ |r| r['is_shared'] }
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(shared).to be_falsey
       end
 
       it 'should avoid filtering if scope is invalid' do
         get api('/runners?scope=unknown', user)
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
     end
 
@@ -63,7 +63,7 @@
       it 'should not return runners' do
         get api('/runners')
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -75,7 +75,7 @@
           get api('/runners/all', admin)
           shared = json_response.any?{ |r| r['is_shared'] }
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response).to be_an Array
           expect(shared).to be_truthy
         end
@@ -85,7 +85,7 @@
         it 'should not return runners list' do
           get api('/runners/all', user)
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
@@ -93,14 +93,14 @@
         get api('/runners/all?scope=specific', admin)
         shared = json_response.any?{ |r| r['is_shared'] }
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(shared).to be_falsey
       end
 
       it 'should avoid filtering if scope is invalid' do
         get api('/runners?scope=unknown', admin)
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
     end
 
@@ -108,7 +108,7 @@
       it 'should not return runners' do
         get api('/runners')
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -119,7 +119,7 @@
         it "should return runner's details" do
           get api("/runners/#{shared_runner.id}", admin)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['description']).to eq(shared_runner.description)
         end
       end
@@ -128,7 +128,7 @@
         it "should return runner's details" do
           get api("/runners/#{specific_runner.id}", admin)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['description']).to eq(specific_runner.description)
         end
       end
@@ -136,7 +136,7 @@
       it 'should return 404 if runner does not exists' do
         get api('/runners/9999', admin)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -145,7 +145,7 @@
         it "should return runner's details" do
           get api("/runners/#{specific_runner.id}", user)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['description']).to eq(specific_runner.description)
         end
       end
@@ -154,7 +154,7 @@
         it "should return runner's details" do
           get api("/runners/#{shared_runner.id}", user)
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['description']).to eq(shared_runner.description)
         end
       end
@@ -164,7 +164,7 @@
       it "should not return runner's details" do
         get api("/runners/#{specific_runner.id}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -172,7 +172,7 @@
       it "should not return runner's details" do
         get api("/runners/#{specific_runner.id}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -191,7 +191,7 @@
                                                  locked: 'true')
           shared_runner.reload
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(shared_runner.description).to eq("#{description}_updated")
           expect(shared_runner.active).to eq(!active)
           expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
@@ -206,7 +206,7 @@
           update_runner(specific_runner.id, admin, description: 'test')
           specific_runner.reload
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(specific_runner.description).to eq('test')
           expect(specific_runner.description).not_to eq(description)
         end
@@ -215,7 +215,7 @@
       it 'should return 404 if runner does not exists' do
         update_runner(9999, admin, description: 'test')
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       def update_runner(id, user, args)
@@ -228,7 +228,7 @@ def update_runner(id, user, args)
         it 'should not update runner' do
           put api("/runners/#{shared_runner.id}", user)
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
@@ -236,7 +236,7 @@ def update_runner(id, user, args)
         it 'should not update runner without access to it' do
           put api("/runners/#{specific_runner.id}", user2)
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
 
         it 'should update runner with access to it' do
@@ -244,7 +244,7 @@ def update_runner(id, user, args)
           put api("/runners/#{specific_runner.id}", admin), description: 'test'
           specific_runner.reload
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(specific_runner.description).to eq('test')
           expect(specific_runner.description).not_to eq(description)
         end
@@ -255,7 +255,7 @@ def update_runner(id, user, args)
       it 'should not delete runner' do
         put api("/runners/#{specific_runner.id}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -267,7 +267,7 @@ def update_runner(id, user, args)
           expect do
             delete api("/runners/#{shared_runner.id}", admin)
           end.to change{ Ci::Runner.shared.count }.by(-1)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -276,21 +276,21 @@ def update_runner(id, user, args)
           expect do
             delete api("/runners/#{unused_specific_runner.id}", admin)
           end.to change{ Ci::Runner.specific.count }.by(-1)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
 
         it 'should delete used runner' do
           expect do
             delete api("/runners/#{specific_runner.id}", admin)
           end.to change{ Ci::Runner.specific.count }.by(-1)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
       it 'should return 404 if runner does not exists' do
         delete api('/runners/9999', admin)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -298,26 +298,26 @@ def update_runner(id, user, args)
       context 'when runner is shared' do
         it 'should not delete runner' do
           delete api("/runners/#{shared_runner.id}", user)
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
       context 'when runner is not shared' do
         it 'should not delete runner without access to it' do
           delete api("/runners/#{specific_runner.id}", user2)
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
 
         it 'should not delete runner with more than one associated project' do
           delete api("/runners/#{two_projects_runner.id}", user)
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
 
         it 'should delete runner for one owned project' do
           expect do
             delete api("/runners/#{specific_runner.id}", user)
           end.to change{ Ci::Runner.specific.count }.by(-1)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
     end
@@ -326,7 +326,7 @@ def update_runner(id, user, args)
       it 'should not delete runner' do
         delete api("/runners/#{specific_runner.id}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -337,7 +337,7 @@ def update_runner(id, user, args)
         get api("/projects/#{project.id}/runners", user)
         shared = json_response.any?{ |r| r['is_shared'] }
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(shared).to be_truthy
       end
@@ -347,7 +347,7 @@ def update_runner(id, user, args)
       it "should not return project's runners" do
         get api("/projects/#{project.id}/runners", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -355,7 +355,7 @@ def update_runner(id, user, args)
       it "should not return project's runners" do
         get api("/projects/#{project.id}/runners")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -372,14 +372,14 @@ def update_runner(id, user, args)
         expect do
           post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
         end.to change{ project.runners.count }.by(+1)
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
       end
 
       it 'should avoid changes when enabling already enabled runner' do
         expect do
           post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id
         end.to change{ project.runners.count }.by(0)
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
       end
 
       it 'should not enable locked runner' do
@@ -389,13 +389,13 @@ def update_runner(id, user, args)
           post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id
         end.to change{ project.runners.count }.by(0)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       it 'should not enable shared runner' do
         post api("/projects/#{project.id}/runners", user), runner_id: shared_runner.id
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
       context 'user is admin' do
@@ -403,7 +403,7 @@ def update_runner(id, user, args)
           expect do
             post api("/projects/#{project.id}/runners", admin), runner_id: unused_specific_runner.id
           end.to change{ project.runners.count }.by(+1)
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
         end
       end
 
@@ -411,14 +411,14 @@ def update_runner(id, user, args)
         it 'should not enable runner without access to' do
           post api("/projects/#{project.id}/runners", user), runner_id: unused_specific_runner.id
 
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
       it 'should raise an error when no runner_id param is provided' do
         post api("/projects/#{project.id}/runners", admin)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
     end
 
@@ -426,7 +426,7 @@ def update_runner(id, user, args)
       it 'should not enable runner' do
         post api("/projects/#{project.id}/runners", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -434,7 +434,7 @@ def update_runner(id, user, args)
       it 'should not enable runner' do
         post api("/projects/#{project.id}/runners")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -446,7 +446,7 @@ def update_runner(id, user, args)
           expect do
             delete api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user)
           end.to change{ project.runners.count }.by(-1)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
@@ -455,14 +455,14 @@ def update_runner(id, user, args)
           expect do
             delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user)
           end.to change{ project.runners.count }.by(0)
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
       end
 
       it 'should return 404 is runner is not found' do
         delete api("/projects/#{project.id}/runners/9999", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -470,7 +470,7 @@ def update_runner(id, user, args)
       it "should not disable project's runner" do
         delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -478,7 +478,7 @@ def update_runner(id, user, args)
       it "should not disable project's runner" do
         delete api("/projects/#{project.id}/runners/#{specific_runner.id}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index fed9ae1949b419834f3f6d1593765e449f27ff3f..bf7eaaaaaed9b77f1cb90b8f476144803d451dca 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -14,7 +14,7 @@
       it "should update #{service} settings" do
         put api("/projects/#{project.id}/services/#{dashed_service}", user), service_attrs
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it "should return if required fields missing" do
@@ -45,7 +45,7 @@
       it "should delete #{service}" do
         delete api("/projects/#{project.id}/services/#{dashed_service}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         project.send(service_method).reload
         expect(project.send(service_method).activated?).to be_falsey
       end
@@ -64,20 +64,20 @@
 
       it 'should return authentication error when unauthenticated' do
         get api("/projects/#{project.id}/services/#{dashed_service}")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
       
       it "should return all properties of service #{service} when authenticated as admin" do
         get api("/projects/#{project.id}/services/#{dashed_service}", admin)
         
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list.map)
       end
 
       it "should return properties of service #{service} other than passwords when authenticated as project owner" do
         get api("/projects/#{project.id}/services/#{dashed_service}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list_without_passwords)
       end
 
@@ -85,7 +85,7 @@
         project.team << [user2, :developer]
         get api("/projects/#{project.id}/services/#{dashed_service}", user2)
         
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
 
     end
diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb
index fbd57b34a58d32308e969cdddf4b7b681011b645..c15b7ff97928c710cd8cf2d988db22164d1edc4e 100644
--- a/spec/requests/api/session_spec.rb
+++ b/spec/requests/api/session_spec.rb
@@ -9,7 +9,7 @@
     context "when valid password" do
       it "should return private token" do
         post api("/session"), email: user.email, password: '12345678'
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
 
         expect(json_response['email']).to eq(user.email)
         expect(json_response['private_token']).to eq(user.private_token)
@@ -48,7 +48,7 @@
     context "when invalid password" do
       it "should return authentication error" do
         post api("/session"), email: user.email, password: '123'
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
 
         expect(json_response['email']).to be_nil
         expect(json_response['private_token']).to be_nil
@@ -58,7 +58,7 @@
     context "when empty password" do
       it "should return authentication error" do
         post api("/session"), email: user.email
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
 
         expect(json_response['email']).to be_nil
         expect(json_response['private_token']).to be_nil
@@ -68,7 +68,7 @@
     context "when empty name" do
       it "should return authentication error" do
         post api("/session"), password: user.password
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
 
         expect(json_response['email']).to be_nil
         expect(json_response['private_token']).to be_nil
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index c815a8e1d738696832cacb1236f2d5c5bbd2bf26..f756101c51402f00badd86206fabeabf9a9e4316 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -10,7 +10,7 @@
   describe "GET /application/settings" do
     it "should return application settings" do
       get api("/application/settings", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_an Hash
       expect(json_response['default_projects_limit']).to eq(42)
       expect(json_response['signin_enabled']).to be_truthy
@@ -21,7 +21,7 @@
     it "should update application settings" do
       put api("/application/settings", admin),
         default_projects_limit: 3, signin_enabled: false
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['default_projects_limit']).to eq(3)
       expect(json_response['signin_enabled']).to be_falsey
     end
diff --git a/spec/requests/api/sidekiq_metrics_spec.rb b/spec/requests/api/sidekiq_metrics_spec.rb
index 41cbf0c6669bfc32b8ae8b9f12abfe6d0da8e7d7..28067f8ca885dbdb3059a710d4d7860f2782e360 100644
--- a/spec/requests/api/sidekiq_metrics_spec.rb
+++ b/spec/requests/api/sidekiq_metrics_spec.rb
@@ -9,28 +9,28 @@
     it 'defines the `queue_metrics` endpoint' do
       get api('/sidekiq/queue_metrics', admin)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_a Hash
     end
 
     it 'defines the `process_metrics` endpoint' do
       get api('/sidekiq/process_metrics', admin)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['processes']).to be_an Array
     end
 
     it 'defines the `job_stats` endpoint' do
       get api('/sidekiq/job_stats', admin)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_a Hash
     end
 
     it 'defines the `compound_metrics` endpoint' do
       get api('/sidekiq/compound_metrics', admin)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response).to be_a Hash
       expect(json_response['queues']).to be_a Hash
       expect(json_response['processes']).to be_an Array
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 94eebc48ec8e1d6f4f26655beaf25bf22425d85a..cf66f261ade2daa4b832b2b2e5ed09a7a686945c 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -13,21 +13,21 @@
     context "when no user" do
       it "should return authentication error" do
         get api("/hooks")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context "when not an admin" do
       it "should return forbidden error" do
         get api("/hooks", user)
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
     context "when authenticated as admin" do
       it "should return an array of hooks" do
         get api("/hooks", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['url']).to eq(hook.url)
       end
@@ -43,7 +43,7 @@
 
     it "should respond with 400 if url not given" do
       post api("/hooks", admin)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it "should not create new hook without url" do
@@ -56,13 +56,13 @@
   describe "GET /hooks/:id" do
     it "should return hook by id" do
       get api("/hooks/#{hook.id}", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['event_name']).to eq('project_create')
     end
 
     it "should return 404 on failure" do
       get api("/hooks/404", admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -75,7 +75,7 @@
 
     it "should return success if hook id not found" do
       delete api("/hooks/12345", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 12e170b232f23d7b628cad7ad1c24b8ef07600c3..fa700ab73436e3b196d96bb09283f4c8255e62af 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -18,7 +18,7 @@
     context 'without releases' do
       it "should return an array of project tags" do
         get api("/projects/#{project.id}/repository/tags", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['name']).to eq(tag_name)
       end
@@ -33,7 +33,7 @@
       it "should return an array of project tags with release info" do
         get api("/projects/#{project.id}/repository/tags", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['name']).to eq(tag_name)
         expect(json_response.first['message']).to eq('Version 1.1.0')
@@ -48,14 +48,14 @@
     it 'returns a specific tag' do
       get api("/projects/#{project.id}/repository/tags/#{tag_name}", user)
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['name']).to eq(tag_name)
     end
 
     it 'returns 404 for an invalid tag name' do
       get api("/projects/#{project.id}/repository/tags/foobar", user)
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -66,7 +66,7 @@
              tag_name: 'v7.0.1',
              ref: 'master'
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq('v7.0.1')
       end
     end
@@ -78,7 +78,7 @@
              ref: 'master',
              release_description: 'Wow'
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq('v7.0.1')
         expect(json_response['release']['description']).to eq('Wow')
       end
@@ -94,13 +94,13 @@
       context 'delete tag' do
         it 'should delete an existing tag' do
           delete api("/projects/#{project.id}/repository/tags/#{tag_name}", user)
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(json_response['tag_name']).to eq(tag_name)
         end
 
         it 'should raise 404 if the tag does not exist' do
           delete api("/projects/#{project.id}/repository/tags/foobar", user)
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
     end
@@ -117,7 +117,7 @@
              ref: 'master',
              message: 'Release 7.1.0'
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['name']).to eq('v7.1.0')
         expect(json_response['message']).to eq('Release 7.1.0')
       end
@@ -127,14 +127,14 @@
       post api("/projects/#{project.id}/repository/tags", user2),
            tag_name: 'v1.9.0',
            ref: '621491c677087aa243f165eab467bfdfbee00be1'
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     it 'should return 400 if tag name is invalid' do
       post api("/projects/#{project.id}/repository/tags", user),
            tag_name: 'v 1.0.0',
            ref: 'master'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Tag name invalid')
     end
 
@@ -142,11 +142,11 @@
       post api("/projects/#{project.id}/repository/tags", user),
            tag_name: 'v8.0.0',
            ref: 'master'
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       post api("/projects/#{project.id}/repository/tags", user),
            tag_name: 'v8.0.0',
            ref: 'master'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Tag v8.0.0 already exists')
     end
 
@@ -154,7 +154,7 @@
       post api("/projects/#{project.id}/repository/tags", user),
            tag_name: 'mytag',
            ref: 'foo'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('Target foo is invalid')
     end
   end
@@ -167,7 +167,7 @@
       post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
         description: description
 
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       expect(json_response['tag_name']).to eq(tag_name)
       expect(json_response['description']).to eq(description)
     end
@@ -176,7 +176,7 @@
       post api("/projects/#{project.id}/repository/tags/foobar/release", user),
         description: description
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('Tag does not exist')
     end
 
@@ -190,7 +190,7 @@
         post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
           description: description
 
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         expect(json_response['message']).to eq('Release already exists')
       end
     end
@@ -211,7 +211,7 @@
         put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
           description: new_description
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['tag_name']).to eq(tag_name)
         expect(json_response['description']).to eq(new_description)
       end
@@ -221,7 +221,7 @@
       put api("/projects/#{project.id}/repository/tags/foobar/release", user),
         description: new_description
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('Tag does not exist')
     end
 
@@ -229,7 +229,7 @@
       put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
         description: new_description
 
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('Release does not exist')
     end
   end
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index a6d5ade3013abc9e86a7db42c3a93b9a7759f562..68d0f41b489b239ffe4cf1d70e8b1a214180407f 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -22,7 +22,7 @@
       it 'returns a list of available gitignore templates' do
         get api('/gitignores')
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.size).to be > 15
       end
@@ -34,7 +34,7 @@
       it 'returns a list of available gitlab_ci_ymls' do
         get api('/gitlab_ci_ymls')
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['name']).not_to be_nil
       end
@@ -45,7 +45,7 @@
     it 'adds a disclaimer on the top' do
       get api('/gitlab_ci_ymls/Ruby')
 
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['content']).to start_with("# This file is a template,")
     end
   end
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index fdd4ec6d7614ca5c0f728f6db828cc4eceed6fe4..8992996c30aaca5ec33c955b840693c91ecbfda4 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -29,17 +29,17 @@
     context 'Handles errors' do
       it 'should return bad request if token is missing' do
         post api("/projects/#{project.id}/trigger/builds"), ref: 'master'
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it 'should return not found if project is not found' do
         post api('/projects/0/trigger/builds'), options.merge(ref: 'master')
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should return unauthorized if token is for different project' do
         post api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master')
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -48,14 +48,14 @@
 
       it 'should create builds' do
         post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master')
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         pipeline.builds.reload
         expect(pipeline.builds.size).to eq(2)
       end
 
       it 'should return bad request with no builds created if there\'s no commit for that ref' do
         post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch')
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']).to eq('No builds created')
       end
 
@@ -66,19 +66,19 @@
 
         it 'should validate variables to be a hash' do
           post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master')
-          expect(response.status).to eq(400)
+          expect(response).to have_http_status(400)
           expect(json_response['message']).to eq('variables needs to be a hash')
         end
 
         it 'should validate variables needs to be a map of key-valued strings' do
           post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master')
-          expect(response.status).to eq(400)
+          expect(response).to have_http_status(400)
           expect(json_response['message']).to eq('variables needs to be a map of key-valued strings')
         end
 
         it 'create trigger request with variables' do
           post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master')
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           pipeline.builds.reload
           expect(pipeline.builds.first.trigger_request.variables).to eq(variables)
         end
@@ -91,7 +91,7 @@
       it 'should return list of triggers' do
         get api("/projects/#{project.id}/triggers", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_a(Array)
         expect(json_response[0]).to have_key('token')
       end
@@ -101,7 +101,7 @@
       it 'should not return triggers list' do
         get api("/projects/#{project.id}/triggers", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -109,7 +109,7 @@
       it 'should not return triggers list' do
         get api("/projects/#{project.id}/triggers")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -119,14 +119,14 @@
       it 'should return trigger details' do
         get api("/projects/#{project.id}/triggers/#{trigger.token}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_a(Hash)
       end
 
       it 'should respond with 404 Not Found if requesting non-existing trigger' do
         get api("/projects/#{project.id}/triggers/abcdef012345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -134,7 +134,7 @@
       it 'should not return triggers list' do
         get api("/projects/#{project.id}/triggers/#{trigger.token}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -142,7 +142,7 @@
       it 'should not return triggers list' do
         get api("/projects/#{project.id}/triggers/#{trigger.token}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -154,7 +154,7 @@
           post api("/projects/#{project.id}/triggers", user)
         end.to change{project.triggers.count}.by(1)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response).to be_a(Hash)
       end
     end
@@ -163,7 +163,7 @@
       it 'should not create trigger' do
         post api("/projects/#{project.id}/triggers", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -171,7 +171,7 @@
       it 'should not create trigger' do
         post api("/projects/#{project.id}/triggers")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -182,13 +182,13 @@
         expect do
           delete api("/projects/#{project.id}/triggers/#{trigger.token}", user)
         end.to change{project.triggers.count}.by(-1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should respond with 404 Not Found if requesting non-existing trigger' do
         delete api("/projects/#{project.id}/triggers/abcdef012345", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -196,7 +196,7 @@
       it 'should not delete trigger' do
         delete api("/projects/#{project.id}/triggers/#{trigger.token}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -204,7 +204,7 @@
       it 'should not delete trigger' do
         delete api("/projects/#{project.id}/triggers/#{trigger.token}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index a7690f430c4b91c841bbed59d1e87defde9c0516..056256a29f5ccdb599137adb1b081d0b771fa60d 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -15,7 +15,7 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/users")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -29,18 +29,18 @@
 
         it "renders 403" do
           get api("/users")
-          expect(response.status).to eq(403)
+          expect(response).to have_http_status(403)
         end
 
         it "renders 404" do
           get api("/users/#{user.id}")
-          expect(response.status).to eq(404)
+          expect(response).to have_http_status(404)
         end
       end
 
       it "should return an array of users" do
         get api("/users", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         username = user.username
         expect(json_response.detect do |user|
@@ -50,7 +50,7 @@
 
       it "should return one user" do
         get api("/users?username=#{omniauth_user.username}", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['username']).to eq(omniauth_user.username)
       end
@@ -59,7 +59,7 @@
     context "when admin" do
       it "should return an array of users" do
         get api("/users", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first.keys).to include 'email'
         expect(json_response.first.keys).to include 'identities'
@@ -74,24 +74,24 @@
   describe "GET /users/:id" do
     it "should return a user by id" do
       get api("/users/#{user.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['username']).to eq(user.username)
     end
 
     it "should return a 401 if unauthenticated" do
       get api("/users/9998")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it "should return a 404 error if user id not found" do
       get api("/users/9999", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Not found')
     end
 
     it "should return a 404 if invalid ID" do
       get api("/users/1ASDF", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -106,7 +106,7 @@
 
     it "should create user with correct attributes" do
       post api('/users', admin), attributes_for(:user, admin: true, can_create_group: true)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       user_id = json_response['id']
       new_user = User.find(user_id)
       expect(new_user).not_to eq(nil)
@@ -116,7 +116,7 @@
 
     it "should create non-admin user" do
       post api('/users', admin), attributes_for(:user, admin: false, can_create_group: false)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       user_id = json_response['id']
       new_user = User.find(user_id)
       expect(new_user).not_to eq(nil)
@@ -126,7 +126,7 @@
 
     it "should create non-admin users by default" do
       post api('/users', admin), attributes_for(:user)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
       user_id = json_response['id']
       new_user = User.find(user_id)
       expect(new_user).not_to eq(nil)
@@ -135,12 +135,12 @@
 
     it "should return 201 Created on success" do
       post api("/users", admin), attributes_for(:user, projects_limit: 3)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
     end
 
     it 'creates non-external users by default' do
       post api("/users", admin), attributes_for(:user)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
 
       user_id = json_response['id']
       new_user = User.find(user_id)
@@ -150,7 +150,7 @@
 
     it 'should allow an external user to be created' do
       post api("/users", admin), attributes_for(:user, external: true)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
 
       user_id = json_response['id']
       new_user = User.find(user_id)
@@ -163,27 +163,27 @@
         email: 'invalid email',
         password: 'password',
         name: 'test'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 error if name not given' do
       post api('/users', admin), attributes_for(:user).except(:name)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 error if password not given' do
       post api('/users', admin), attributes_for(:user).except(:password)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 error if email not given' do
       post api('/users', admin), attributes_for(:user).except(:email)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 error if username not given' do
       post api('/users', admin), attributes_for(:user).except(:username)
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
 
     it 'should return 400 error if user does not validate' do
@@ -194,7 +194,7 @@
         name: 'test',
         bio: 'g' * 256,
         projects_limit: -1
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['password']).
         to eq(['is too short (minimum is 8 characters)'])
       expect(json_response['message']['bio']).
@@ -207,7 +207,7 @@
 
     it "shouldn't available for non admin users" do
       post api("/users", user), attributes_for(:user)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     context 'with existing user' do
@@ -227,7 +227,7 @@
             password: 'password',
             username: 'foo'
         end.to change { User.count }.by(0)
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         expect(json_response['message']).to eq('Email has already been taken')
       end
 
@@ -239,7 +239,7 @@
             password: 'password',
             username: 'test'
         end.to change { User.count }.by(0)
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         expect(json_response['message']).to eq('Username has already been taken')
       end
     end
@@ -249,7 +249,7 @@
 
     it "should redirect to sign in page" do
       get "/users/sign_up"
-      expect(response.status).to eq(302)
+      expect(response).to have_http_status(302)
       expect(response).to redirect_to(new_user_session_path)
     end
   end
@@ -261,41 +261,41 @@
 
     it "should update user with new bio" do
       put api("/users/#{user.id}", admin), { bio: 'new test bio' }
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['bio']).to eq('new test bio')
       expect(user.reload.bio).to eq('new test bio')
     end
 
     it 'should update user with his own email' do
       put api("/users/#{user.id}", admin), email: user.email
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['email']).to eq(user.email)
       expect(user.reload.email).to eq(user.email)
     end
 
     it 'should update user with his own username' do
       put api("/users/#{user.id}", admin), username: user.username
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['username']).to eq(user.username)
       expect(user.reload.username).to eq(user.username)
     end
 
     it "should update user's existing identity" do
       put api("/users/#{omniauth_user.id}", admin), provider: 'ldapmain', extern_uid: '654321'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321')
     end
 
     it 'should update user with new identity' do
       put api("/users/#{user.id}", admin), provider: 'github', extern_uid: '67890'
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(user.reload.identities.first.extern_uid).to eq('67890')
       expect(user.reload.identities.first.provider).to eq('github')
     end
 
     it "should update admin status" do
       put api("/users/#{user.id}", admin), { admin: true }
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['is_admin']).to eq(true)
       expect(user.reload.admin).to eq(true)
     end
@@ -309,7 +309,7 @@
 
     it "should not update admin status" do
       put api("/users/#{admin_user.id}", admin), { can_create_group: false }
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['is_admin']).to eq(true)
       expect(admin_user.reload.admin).to eq(true)
       expect(admin_user.can_create_group).to eq(false)
@@ -317,18 +317,18 @@
 
     it "should not allow invalid update" do
       put api("/users/#{user.id}", admin), { email: 'invalid email' }
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(user.reload.email).not_to eq('invalid email')
     end
 
     it "shouldn't available for non admin users" do
       put api("/users/#{user.id}", user), attributes_for(:user)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     it "should return 404 for non-existing user" do
       put api("/users/999999", admin), { bio: 'update should fail' }
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Not found')
     end
 
@@ -344,7 +344,7 @@
         name: 'test',
         bio: 'g' * 256,
         projects_limit: -1
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']['password']).
         to eq(['is too short (minimum is 8 characters)'])
       expect(json_response['message']['bio']).
@@ -364,14 +364,14 @@
 
       it 'should return 409 conflict error if email address exists' do
         put api("/users/#{@user.id}", admin), email: 'test@example.com'
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         expect(@user.reload.email).to eq(@user.email)
       end
 
       it 'should return 409 conflict error if username taken' do
         @user_id = User.all.last.id
         put api("/users/#{@user.id}", admin), username: 'test'
-        expect(response.status).to eq(409)
+        expect(response).to have_http_status(409)
         expect(@user.reload.username).to eq(@user.username)
       end
     end
@@ -382,13 +382,13 @@
 
     it "should not create invalid ssh key" do
       post api("/users/#{user.id}/keys", admin), { title: "invalid key" }
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "key" not given')
     end
 
     it 'should not create key without title' do
       post api("/users/#{user.id}/keys", admin), key: 'some key'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "title" not given')
     end
 
@@ -401,7 +401,7 @@
 
     it "should return 405 for invalid ID" do
       post api("/users/ASDF/keys", admin)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
     end
   end
 
@@ -411,14 +411,14 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         get api("/users/#{user.id}/keys")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context 'when authenticated' do
       it 'should return 404 for non-existing user' do
         get api('/users/999999/keys', admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 User Not Found')
       end
 
@@ -426,14 +426,14 @@
         user.keys << key
         user.save
         get api("/users/#{user.id}/keys", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['title']).to eq(key.title)
       end
 
       it "should return 405 for invalid ID" do
         get api("/users/ASDF/keys", admin)
-        expect(response.status).to eq(405)
+        expect(response).to have_http_status(405)
       end
     end
   end
@@ -444,7 +444,7 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         delete api("/users/#{user.id}/keys/42")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -455,20 +455,20 @@
         expect do
           delete api("/users/#{user.id}/keys/#{key.id}", admin)
         end.to change { user.keys.count }.by(-1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should return 404 error if user not found' do
         user.keys << key
         user.save
         delete api("/users/999999/keys/#{key.id}", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 User Not Found')
       end
 
       it 'should return 404 error if key not foud' do
         delete api("/users/#{user.id}/keys/42", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 Key Not Found')
       end
     end
@@ -479,7 +479,7 @@
 
     it "should not create invalid email" do
       post api("/users/#{user.id}/emails", admin), {}
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "email" not given')
     end
 
@@ -492,7 +492,7 @@
 
     it "should raise error for invalid ID" do
       post api("/users/ASDF/emails", admin)
-      expect(response.status).to eq(405)
+      expect(response).to have_http_status(405)
     end
   end
 
@@ -502,14 +502,14 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         get api("/users/#{user.id}/emails")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
     context 'when authenticated' do
       it 'should return 404 for non-existing user' do
         get api('/users/999999/emails', admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 User Not Found')
       end
 
@@ -517,14 +517,14 @@
         user.emails << email
         user.save
         get api("/users/#{user.id}/emails", admin)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['email']).to eq(email.email)
       end
 
       it "should raise error for invalid ID" do
         put api("/users/ASDF/emails", admin)
-        expect(response.status).to eq(405)
+        expect(response).to have_http_status(405)
       end
     end
   end
@@ -535,7 +535,7 @@
     context 'when unauthenticated' do
       it 'should return authentication error' do
         delete api("/users/#{user.id}/emails/42")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -546,20 +546,20 @@
         expect do
           delete api("/users/#{user.id}/emails/#{email.id}", admin)
         end.to change { user.emails.count }.by(-1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should return 404 error if user not found' do
         user.emails << email
         user.save
         delete api("/users/999999/emails/#{email.id}", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 User Not Found')
       end
 
       it 'should return 404 error if email not foud' do
         delete api("/users/#{user.id}/emails/42", admin)
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
         expect(json_response['message']).to eq('404 Email Not Found')
       end
 
@@ -574,24 +574,24 @@
 
     it "should delete user" do
       delete api("/users/#{user.id}", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
       expect(json_response['email']).to eq(user.email)
     end
 
     it "should not delete for unauthenticated user" do
       delete api("/users/#{user.id}")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it "shouldn't available for non admin users" do
       delete api("/users/#{user.id}", user)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
     end
 
     it "should return 404 for non-existing user" do
       delete api("/users/999999", admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 User Not Found')
     end
 
@@ -603,7 +603,7 @@
   describe "GET /user" do
     it "should return current user" do
       get api("/user", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response['email']).to eq(user.email)
       expect(json_response['is_admin']).to eq(user.is_admin?)
       expect(json_response['can_create_project']).to eq(user.can_create_project?)
@@ -613,7 +613,7 @@
 
     it "should return 401 error if user is unauthenticated" do
       get api("/user")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
   end
 
@@ -621,7 +621,7 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/user/keys")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -630,7 +630,7 @@
         user.keys << key
         user.save
         get api("/user/keys", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first["title"]).to eq(key.title)
       end
@@ -642,13 +642,13 @@
       user.keys << key
       user.save
       get api("/user/keys/#{key.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response["title"]).to eq(key.title)
     end
 
     it "should return 404 Not Found within invalid ID" do
       get api("/user/keys/42", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Not found')
     end
 
@@ -657,13 +657,13 @@
       user.save
       admin
       get api("/user/keys/#{key.id}", admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Not found')
     end
 
     it "should return 404 for invalid ID" do
       get api("/users/keys/ASDF", admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -673,29 +673,29 @@
       expect do
         post api("/user/keys", user), key_attrs
       end.to change{ user.keys.count }.by(1)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
     end
 
     it "should return a 401 error if unauthorized" do
       post api("/user/keys"), title: 'some title', key: 'some key'
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it "should not create ssh key without key" do
       post api("/user/keys", user), title: 'title'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "key" not given')
     end
 
     it 'should not create ssh key without title' do
       post api('/user/keys', user), key: 'some key'
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "title" not given')
     end
 
     it "should not create ssh key without title" do
       post api("/user/keys", user), key: "somekey"
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
     end
   end
 
@@ -706,19 +706,19 @@
       expect do
         delete api("/user/keys/#{key.id}", user)
       end.to change{user.keys.count}.by(-1)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return success if key ID not found" do
       delete api("/user/keys/42", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return 401 error if unauthorized" do
       user.keys << key
       user.save
       delete api("/user/keys/#{key.id}")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it "should raise error for invalid ID" do
@@ -730,7 +730,7 @@
     context "when unauthenticated" do
       it "should return authentication error" do
         get api("/user/emails")
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -739,7 +739,7 @@
         user.emails << email
         user.save
         get api("/user/emails", user)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first["email"]).to eq(email.email)
       end
@@ -751,13 +751,13 @@
       user.emails << email
       user.save
       get api("/user/emails/#{email.id}", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(json_response["email"]).to eq(email.email)
     end
 
     it "should return 404 Not Found within invalid ID" do
       get api("/user/emails/42", user)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Not found')
     end
 
@@ -766,13 +766,13 @@
       user.save
       admin
       get api("/user/emails/#{email.id}", admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 Not found')
     end
 
     it "should return 404 for invalid ID" do
       get api("/users/emails/ASDF", admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
     end
   end
 
@@ -782,17 +782,17 @@
       expect do
         post api("/user/emails", user), email_attrs
       end.to change{ user.emails.count }.by(1)
-      expect(response.status).to eq(201)
+      expect(response).to have_http_status(201)
     end
 
     it "should return a 401 error if unauthorized" do
       post api("/user/emails"), email: 'some email'
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it "should not create email with invalid email" do
       post api("/user/emails", user), {}
-      expect(response.status).to eq(400)
+      expect(response).to have_http_status(400)
       expect(json_response['message']).to eq('400 (Bad request) "email" not given')
     end
   end
@@ -804,19 +804,19 @@
       expect do
         delete api("/user/emails/#{email.id}", user)
       end.to change{user.emails.count}.by(-1)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return success if email ID not found" do
       delete api("/user/emails/42", user)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
     end
 
     it "should return 401 error if unauthorized" do
       user.emails << email
       user.save
       delete api("/user/emails/#{email.id}")
-      expect(response.status).to eq(401)
+      expect(response).to have_http_status(401)
     end
 
     it "should raise error for invalid ID" do
@@ -828,25 +828,25 @@
     before { admin }
     it 'should block existing user' do
       put api("/users/#{user.id}/block", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(user.reload.state).to eq('blocked')
     end
 
     it 'should not re-block ldap blocked users' do
       put api("/users/#{ldap_blocked_user.id}/block", admin)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
       expect(ldap_blocked_user.reload.state).to eq('ldap_blocked')
     end
 
     it 'should not be available for non admin users' do
       put api("/users/#{user.id}/block", user)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
       expect(user.reload.state).to eq('active')
     end
 
     it 'should return a 404 error if user id not found' do
       put api('/users/9999/block', admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 User Not Found')
     end
   end
@@ -857,31 +857,31 @@
 
     it 'should unblock existing user' do
       put api("/users/#{user.id}/unblock", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(user.reload.state).to eq('active')
     end
 
     it 'should unblock a blocked user' do
       put api("/users/#{blocked_user.id}/unblock", admin)
-      expect(response.status).to eq(200)
+      expect(response).to have_http_status(200)
       expect(blocked_user.reload.state).to eq('active')
     end
 
     it 'should not unblock ldap blocked users' do
       put api("/users/#{ldap_blocked_user.id}/unblock", admin)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
       expect(ldap_blocked_user.reload.state).to eq('ldap_blocked')
     end
 
     it 'should not be available for non admin users' do
       put api("/users/#{user.id}/unblock", user)
-      expect(response.status).to eq(403)
+      expect(response).to have_http_status(403)
       expect(user.reload.state).to eq('active')
     end
 
     it 'should return a 404 error if user id not found' do
       put api('/users/9999/block', admin)
-      expect(response.status).to eq(404)
+      expect(response).to have_http_status(404)
       expect(json_response['message']).to eq('404 User Not Found')
     end
 
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index b1e1053d0372d3220df338b502d51ea96e5a1f7e..ddba18245f862b0c31d168c8746d1d0b6152c5a3 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -15,7 +15,7 @@
       it 'should return project variables' do
         get api("/projects/#{project.id}/variables", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response).to be_a(Array)
       end
     end
@@ -24,7 +24,7 @@
       it 'should not return project variables' do
         get api("/projects/#{project.id}/variables", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -32,7 +32,7 @@
       it 'should not return project variables' do
         get api("/projects/#{project.id}/variables")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -42,14 +42,14 @@
       it 'should return project variable details' do
         get api("/projects/#{project.id}/variables/#{variable.key}", user)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_response['value']).to eq(variable.value)
       end
 
       it 'should respond with 404 Not Found if requesting non-existing variable' do
         get api("/projects/#{project.id}/variables/non_existing_variable", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -57,7 +57,7 @@
       it 'should not return project variable details' do
         get api("/projects/#{project.id}/variables/#{variable.key}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -65,7 +65,7 @@
       it 'should not return project variable details' do
         get api("/projects/#{project.id}/variables/#{variable.key}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -77,7 +77,7 @@
           post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2'
         end.to change{project.variables.count}.by(1)
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['key']).to eq('TEST_VARIABLE_2')
         expect(json_response['value']).to eq('VALUE_2')
       end
@@ -87,7 +87,7 @@
           post api("/projects/#{project.id}/variables", user), key: variable.key, value: 'VALUE_2'
         end.to change{project.variables.count}.by(0)
 
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
     end
 
@@ -95,7 +95,7 @@
       it 'should not create variable' do
         post api("/projects/#{project.id}/variables", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -103,7 +103,7 @@
       it 'should not create variable' do
         post api("/projects/#{project.id}/variables")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -118,7 +118,7 @@
 
         updated_variable = project.variables.first
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(value_before).to eq(variable.value)
         expect(updated_variable.value).to eq('VALUE_1_UP')
       end
@@ -126,7 +126,7 @@
       it 'should responde with 404 Not Found if requesting non-existing variable' do
         put api("/projects/#{project.id}/variables/non_existing_variable", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -134,7 +134,7 @@
       it 'should not update variable' do
         put api("/projects/#{project.id}/variables/#{variable.key}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -142,7 +142,7 @@
       it 'should not update variable' do
         put api("/projects/#{project.id}/variables/#{variable.key}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
@@ -153,13 +153,13 @@
         expect do
           delete api("/projects/#{project.id}/variables/#{variable.key}", user)
         end.to change{project.variables.count}.by(-1)
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should responde with 404 Not Found if requesting non-existing variable' do
         delete api("/projects/#{project.id}/variables/non_existing_variable", user)
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -167,7 +167,7 @@
       it 'should not delete variable' do
         delete api("/projects/#{project.id}/variables/#{variable.key}", user2)
 
-        expect(response.status).to eq(403)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -175,7 +175,7 @@
       it 'should not delete variable' do
         delete api("/projects/#{project.id}/variables/#{variable.key}")
 
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 7e50bea90d1e12fcdf55e4a932743881fd940544..1bc51783c3adfa6f5e4af4040f9e33be5223677e 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -26,7 +26,7 @@
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response['sha']).to eq(build.sha)
         expect(runner.reload.platform).to eq("darwin")
       end
@@ -34,7 +34,7 @@
       it "should return 404 error if no pending build found" do
         post ci_api("/builds/register"), token: runner.token
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should return 404 error if no builds for specific runner" do
@@ -43,7 +43,7 @@
 
         post ci_api("/builds/register"), token: runner.token
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "should return 404 error if no builds for shared runner" do
@@ -52,7 +52,7 @@
 
         post ci_api("/builds/register"), token: shared_runner.token
 
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it "returns options" do
@@ -61,7 +61,7 @@
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] })
       end
 
@@ -72,7 +72,7 @@
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response["variables"]).to eq([
           { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
           { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
@@ -91,7 +91,7 @@
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response["variables"]).to eq([
           { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
           { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
@@ -109,7 +109,7 @@
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         expect(json_response["depends_on_builds"].count).to eq(2)
         expect(json_response["depends_on_builds"][0]["name"]).to eq("rspec")
       end
@@ -122,7 +122,7 @@
 
           it do
             post ci_api("/builds/register"), token: runner.token, info: { param => value }
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
             runner.reload
             is_expected.to eq(value)
           end
@@ -172,7 +172,7 @@ def register_builds
       end
 
       it "should update a running build" do
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
 
       it 'should not override trace information when no trace is given' do
@@ -252,13 +252,13 @@ def register_builds
         context "should authorize posting artifact to running build" do
           it "using token as parameter" do
             post authorize_url, { token: build.token }, headers
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
             expect(json_response["TempPath"]).not_to be_nil
           end
 
           it "using token as header" do
             post authorize_url, {}, headers_with_token
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
             expect(json_response["TempPath"]).not_to be_nil
           end
         end
@@ -267,13 +267,13 @@ def register_builds
           it "using token as parameter" do
             stub_application_setting(max_artifacts_size: 0)
             post authorize_url, { token: build.token, filesize: 100 }, headers
-            expect(response.status).to eq(413)
+            expect(response).to have_http_status(413)
           end
 
           it "using token as header" do
             stub_application_setting(max_artifacts_size: 0)
             post authorize_url, { filesize: 100 }, headers_with_token
-            expect(response.status).to eq(413)
+            expect(response).to have_http_status(413)
           end
         end
 
@@ -281,7 +281,7 @@ def register_builds
           before { post authorize_url, { token: 'invalid', filesize: 100 } }
 
           it 'should respond with forbidden' do
-            expect(response.status).to eq(403)
+            expect(response).to have_http_status(403)
           end
         end
       end
@@ -305,20 +305,20 @@ def register_builds
           context "should post artifact to running build" do
             it "uses regual file post" do
               upload_artifacts(file_upload, headers_with_token, false)
-              expect(response.status).to eq(201)
+              expect(response).to have_http_status(201)
               expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
             end
 
             it "uses accelerated file post" do
               upload_artifacts(file_upload, headers_with_token, true)
-              expect(response.status).to eq(201)
+              expect(response).to have_http_status(201)
               expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
             end
 
             it "updates artifact" do
               upload_artifacts(file_upload, headers_with_token)
               upload_artifacts(file_upload2, headers_with_token)
-              expect(response.status).to eq(201)
+              expect(response).to have_http_status(201)
               expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename)
             end
           end
@@ -343,7 +343,7 @@ def register_builds
               end
 
               it 'stores artifacts and artifacts metadata' do
-                expect(response.status).to eq(201)
+                expect(response).to have_http_status(201)
                 expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename)
                 expect(stored_metadata_file.original_filename).to eq(metadata.original_filename)
               end
@@ -355,7 +355,7 @@ def register_builds
               end
 
               it 'is expected to respond with bad request' do
-                expect(response.status).to eq(400)
+                expect(response).to have_http_status(400)
               end
 
               it 'does not store metadata' do
@@ -382,7 +382,7 @@ def register_builds
 
               it 'updates when specified' do
                 build.reload
-                expect(response.status).to eq(201)
+                expect(response).to have_http_status(201)
                 expect(json_response['artifacts_expire_at']).not_to be_empty
                 expect(build.artifacts_expire_at).to be_within(5.minutes).of(Time.now + 7.days)
               end
@@ -393,7 +393,7 @@ def register_builds
 
               it 'ignores if not specified' do
                 build.reload
-                expect(response.status).to eq(201)
+                expect(response).to have_http_status(201)
                 expect(json_response['artifacts_expire_at']).to be_nil
                 expect(build.artifacts_expire_at).to be_nil
               end
@@ -404,21 +404,21 @@ def register_builds
             it "should fail to post too large artifact" do
               stub_application_setting(max_artifacts_size: 0)
               upload_artifacts(file_upload, headers_with_token)
-              expect(response.status).to eq(413)
+              expect(response).to have_http_status(413)
             end
           end
 
           context "artifacts post request does not contain file" do
             it "should fail to post artifacts without file" do
               post post_url, {}, headers_with_token
-              expect(response.status).to eq(400)
+              expect(response).to have_http_status(400)
             end
           end
 
           context 'GitLab Workhorse is not configured' do
             it "should fail to post artifacts without GitLab-Workhorse" do
               post post_url, { token: build.token }, {}
-              expect(response.status).to eq(403)
+              expect(response).to have_http_status(403)
             end
           end
         end
@@ -437,7 +437,7 @@ def register_builds
 
           it "should fail to post artifacts for outside of tmp path" do
             upload_artifacts(file_upload, headers_with_token)
-            expect(response.status).to eq(400)
+            expect(response).to have_http_status(400)
           end
         end
 
@@ -458,7 +458,7 @@ def upload_artifacts(file, headers = {}, accelerated = true)
         before { delete delete_url, token: build.token }
 
         it 'should remove build artifacts' do
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
           expect(build.artifacts_file.exists?).to be_falsy
           expect(build.artifacts_metadata.exists?).to be_falsy
         end
@@ -475,14 +475,14 @@ def upload_artifacts(file, headers = {}, accelerated = true)
           end
 
           it 'should download artifact' do
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
             expect(response.headers).to include download_headers
           end
         end
 
         context 'build does not has artifacts' do
           it 'should respond with not found' do
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
       end
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index 72f6a3c981daeae00d75d77d5944d4bff9955d57..f12678e5a8ecd0fe36306c7962b13780f9ef6be3 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -21,17 +21,17 @@
     context 'Handles errors' do
       it 'should return bad request if token is missing' do
         post ci_api("/projects/#{project.ci_id}/refs/master/trigger")
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
       end
 
       it 'should return not found if project is not found' do
         post ci_api('/projects/0/refs/master/trigger'), options
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
 
       it 'should return unauthorized if token is for different project' do
         post ci_api("/projects/#{project2.ci_id}/refs/master/trigger"), options
-        expect(response.status).to eq(401)
+        expect(response).to have_http_status(401)
       end
     end
 
@@ -40,14 +40,14 @@
 
       it 'should create builds' do
         post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options
-        expect(response.status).to eq(201)
+        expect(response).to have_http_status(201)
         pipeline.builds.reload
         expect(pipeline.builds.size).to eq(2)
       end
 
       it 'should return bad request with no builds created if there\'s no commit for that ref' do
         post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options
-        expect(response.status).to eq(400)
+        expect(response).to have_http_status(400)
         expect(json_response['message']).to eq('No builds created')
       end
 
@@ -58,19 +58,19 @@
 
         it 'should validate variables to be a hash' do
           post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value')
-          expect(response.status).to eq(400)
+          expect(response).to have_http_status(400)
           expect(json_response['message']).to eq('variables needs to be a hash')
         end
 
         it 'should validate variables needs to be a map of key-valued strings' do
           post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) })
-          expect(response.status).to eq(400)
+          expect(response).to have_http_status(400)
           expect(json_response['message']).to eq('variables needs to be a map of key-valued strings')
         end
 
         it 'create trigger request with variables' do
           post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: variables)
-          expect(response.status).to eq(201)
+          expect(response).to have_http_status(201)
           pipeline.builds.reload
           expect(pipeline.builds.first.trigger_request.variables).to eq(variables)
         end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index fd26ca978187200693c509accf2d73dfc0aac30b..bae56334be48a2432f980060a757f80759ab00b3 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -14,7 +14,7 @@
     context "when no authentication is provided" do
       it "responds with status 401 (no project existence information leak)" do
         download('doesnt/exist.git') do |response|
-          expect(response.status).to eq(401)
+          expect(response).to have_http_status(401)
         end
       end
     end
@@ -23,7 +23,7 @@
       context "when authentication fails" do
         it "responds with status 401" do
           download('doesnt/exist.git', user: user.username, password: "nope") do |response|
-            expect(response.status).to eq(401)
+            expect(response).to have_http_status(401)
           end
         end
       end
@@ -31,7 +31,7 @@
       context "when authentication succeeds" do
         it "responds with status 404" do
           download('/doesnt/exist.git', user: user.username, password: user.password) do |response|
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
       end
@@ -46,7 +46,7 @@
       download("/#{wiki.repository.path_with_namespace}.git") do |response|
         json_body = ActiveSupport::JSON.decode(response.body)
 
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
         expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace)
       end
     end
@@ -62,13 +62,13 @@
 
       it "downloads get status 200" do
         download(path, {}) do |response|
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
       end
 
       it "uploads get status 401" do
         upload(path, {}) do |response|
-          expect(response.status).to eq(401)
+          expect(response).to have_http_status(401)
         end
       end
 
@@ -77,7 +77,7 @@
 
         it "uploads get status 200 (because Git hooks do the real check)" do
           upload(path, env) do |response|
-            expect(response.status).to eq(200)
+            expect(response).to have_http_status(200)
           end
         end
 
@@ -86,7 +86,7 @@
             allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false)
 
             upload(path, env) do |response|
-              expect(response.status).to eq(404)
+              expect(response).to have_http_status(404)
             end
           end
         end
@@ -97,7 +97,7 @@
           allow(Gitlab.config.gitlab_shell).to receive(:upload_pack).and_return(false)
 
           download(path, {}) do |response|
-            expect(response.status).to eq(404)
+            expect(response).to have_http_status(404)
           end
         end
       end
@@ -111,13 +111,13 @@
       context "when no authentication is provided" do
         it "responds with status 401 to downloads" do
           download(path, {}) do |response|
-            expect(response.status).to eq(401)
+            expect(response).to have_http_status(401)
           end
         end
 
         it "responds with status 401 to uploads" do
           upload(path, {}) do |response|
-            expect(response.status).to eq(401)
+            expect(response).to have_http_status(401)
           end
         end
       end
@@ -128,7 +128,7 @@
         context "when authentication fails" do
           it "responds with status 401" do
             download(path, env) do |response|
-              expect(response.status).to eq(401)
+              expect(response).to have_http_status(401)
             end
           end
 
@@ -139,7 +139,7 @@
 
               clone_get(path, env)
 
-              expect(response.status).to eq(401)
+              expect(response).to have_http_status(401)
             end
           end
         end
@@ -158,7 +158,7 @@
                 project.team << [user, :master]
 
                 download(path, env) do |response|
-                  expect(response.status).to eq(404)
+                  expect(response).to have_http_status(404)
                 end
               end
             end
@@ -169,12 +169,12 @@
 
                 clone_get(path, env)
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
 
               it "uploads get status 200" do
                 upload(path, env) do |response|
-                  expect(response.status).to eq(200)
+                  expect(response).to have_http_status(200)
                 end
               end
             end
@@ -188,13 +188,13 @@
               it "downloads get status 200" do
                 clone_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token
 
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
 
               it "uploads get status 401 (no project existence information leak)" do
                 push_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token
 
-                expect(response.status).to eq(401)
+                expect(response).to have_http_status(401)
               end
             end
 
@@ -232,13 +232,13 @@ def attempt_login(include_password)
           context "when the user doesn't have access to the project" do
             it "downloads get status 404" do
               download(path, user: user.username, password: user.password) do |response|
-                expect(response.status).to eq(404)
+                expect(response).to have_http_status(404)
               end
             end
 
             it "uploads get status 200 (because Git hooks do the real check)" do
               upload(path, user: user.username, password: user.password) do |response|
-                expect(response.status).to eq(200)
+                expect(response).to have_http_status(200)
               end
             end
           end
@@ -256,13 +256,13 @@ def attempt_login(include_password)
         it "downloads get status 200" do
           clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
 
-          expect(response.status).to eq(200)
+          expect(response).to have_http_status(200)
         end
 
         it "uploads get status 401 (no project existence information leak)" do
           push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
 
-          expect(response.status).to eq(401)
+          expect(response).to have_http_status(401)
         end
       end
     end
@@ -336,7 +336,7 @@ def attempt_login(include_password)
       end
 
       it "returns the file" do
-        expect(response.status).to eq(200)
+        expect(response).to have_http_status(200)
       end
     end
 
@@ -344,7 +344,7 @@ def attempt_login(include_password)
       before { get "/#{project.path_with_namespace}/blob/master/info/refs" }
 
       it "returns not found" do
-        expect(response.status).to eq(404)
+        expect(response).to have_http_status(404)
       end
     end
   end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index d2d4a9eca18faded89a085ce5c033570b760de7e..c6172b9cc7d857fde28d4f9d0ff0f1a4337e875c 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -11,12 +11,12 @@
   context 'existing service' do
     subject! { get '/jwt/auth', parameters }
 
-    it { expect(response.status).to eq(200) }
+    it { expect(response).to have_http_status(200) }
 
     context 'returning custom http code' do
       let(:service) { double(execute: { http_status: 505 }) }
 
-      it { expect(response.status).to eq(505) }
+      it { expect(response).to have_http_status(505) }
     end
   end
 
@@ -36,7 +36,7 @@
       context 'project with disabled CI' do
         let(:builds_enabled) { false }
 
-        it { expect(response.status).to eq(403) }
+        it { expect(response).to have_http_status(403) }
       end
     end
 
@@ -56,14 +56,14 @@
 
       subject! { get '/jwt/auth', parameters, headers }
 
-      it { expect(response.status).to eq(403) }
+      it { expect(response).to have_http_status(403) }
     end
   end
 
   context 'unknown service' do
     subject! { get '/jwt/auth', service: 'unknown' }
 
-    it { expect(response.status).to eq(404) }
+    it { expect(response).to have_http_status(404) }
   end
 
   def credentials(login, password)
diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb
index deab242f45a577b63eaf69508876cb9f1b54b23e..309213bd44ccc17fa014ed26273b1b1d06d0f1e6 100644
--- a/spec/services/create_commit_builds_service_spec.rb
+++ b/spec/services/create_commit_builds_service_spec.rb
@@ -83,6 +83,9 @@
 
     context 'when commit contains a [ci skip] directive' do
       let(:message) { "some message[ci skip]" }
+      let(:messageFlip) { "some message[skip ci]" }
+      let(:capMessage) { "some message[CI SKIP]" }
+      let(:capMessageFlip) { "some message[SKIP CI]" }
 
       before do
         allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message }
@@ -96,12 +99,55 @@
                                    after: '31das312',
                                    commits: commits
                                   )
+
+        expect(pipeline).to be_persisted
+        expect(pipeline.builds.any?).to be false
+        expect(pipeline.status).to eq("skipped")
+      end
+
+      it "skips builds creation if there is [skip ci] tag in commit message" do
+        commits = [{ message: messageFlip }]
+        pipeline = service.execute(project, user,
+                                   ref: 'refs/tags/0_1',
+                                   before: '00000000',
+                                   after: '31das312',
+                                   commits: commits
+                                  )
+
+        expect(pipeline).to be_persisted
+        expect(pipeline.builds.any?).to be false
+        expect(pipeline.status).to eq("skipped")
+      end
+
+      it "skips builds creation if there is [CI SKIP] tag in commit message" do
+        commits = [{ message: capMessage }]
+        pipeline = service.execute(project, user,
+                                   ref: 'refs/tags/0_1',
+                                   before: '00000000',
+                                   after: '31das312',
+                                   commits: commits
+                                  )
+
+        expect(pipeline).to be_persisted
+        expect(pipeline.builds.any?).to be false
+        expect(pipeline.status).to eq("skipped")
+      end
+
+      it "skips builds creation if there is [SKIP CI] tag in commit message" do
+        commits = [{ message: capMessageFlip }]
+        pipeline = service.execute(project, user,
+                                   ref: 'refs/tags/0_1',
+                                   before: '00000000',
+                                   after: '31das312',
+                                   commits: commits
+                                  )
+
         expect(pipeline).to be_persisted
         expect(pipeline.builds.any?).to be false
         expect(pipeline.status).to eq("skipped")
       end
 
-      it "does not skips builds creation if there is no [ci skip] tag in commit message" do
+      it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do
         allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" }
 
         commits = [{ message: "some message" }]
diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..14f3301d9f48a199fedf032d2cf47e8f1cca5701
--- /dev/null
+++ b/spec/services/search/snippet_service_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe Search::SnippetService, services: true do
+  let(:author) { create(:author) }
+  let(:project) { create(:empty_project) }
+
+  let!(:public_snippet)   { create(:snippet, :public, content: 'password: XXX') }
+  let!(:internal_snippet) { create(:snippet, :internal, content: 'password: XXX') }
+  let!(:private_snippet)  { create(:snippet, :private, content: 'password: XXX', author: author) }
+
+  let!(:project_public_snippet)   { create(:snippet, :public, project: project, content: 'password: XXX') }
+  let!(:project_internal_snippet) { create(:snippet, :internal, project: project, content: 'password: XXX') }
+  let!(:project_private_snippet)  { create(:snippet, :private, project: project, content: 'password: XXX') }
+
+  describe '#execute' do
+    context 'unauthenticated' do
+      it 'returns public snippets only' do
+        search = described_class.new(nil, search: 'password')
+        results = search.execute
+
+        expect(results.objects('snippet_blobs')).to match_array [public_snippet, project_public_snippet]
+      end
+    end
+
+    context 'authenticated' do
+      it 'returns only public & internal snippets for regular users' do
+        user = create(:user)
+        search = described_class.new(user, search: 'password')
+        results = search.execute
+
+        expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet]
+      end
+
+      it 'returns public, internal snippets and project private snippets for project members' do
+        member = create(:user)
+        project.team << [member, :developer]
+        search = described_class.new(member, search: 'password')
+        results = search.execute
+
+        expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
+      end
+
+      it 'returns public, internal and private snippets where user is the author' do
+        search = described_class.new(author, search: 'password')
+        results = search.execute
+
+        expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet]
+      end
+
+      it 'returns all snippets when user is admin' do
+        admin = create(:admin)
+        search = described_class.new(admin, search: 'password')
+        results = search.execute
+
+        expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
+      end
+    end
+  end
+end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 498bd4bf8000e9203c643f93aa129d6e4f225f70..426bf53f19852563f5e3a64c46b2e4148bdde72c 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -17,6 +17,7 @@ module TestEnv
     "'test'"           => 'e56497b',
     'orphaned-branch'  => '45127a9',
     'binary-encoding'  => '7b1cf43',
+    'gitattributes'    => '5a62481',
   }
 
   # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily